/* ---- This file is part of SECONDO. Copyright (C) 2004, University in Hagen, Department of Computer Science, Database Systems for New Applications. SECONDO is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. SECONDO is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with SECONDO; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ---- //paragraph [1] Title: [{\Large \bf \begin{center}] [\end{center}}] //paragraph [10] Footnote: [{\footnote{] [}}] //[TOC] [\tableofcontents] [1] Image Similarity Algebra March 2017 Michael Loris The Image Similarity Algebra for SECONDO provides operations to store pictures in JPEG format into m-trees. It consists a ~Signature~ object representing a the signature of a single JPEG image. The object is a subtype of the Attribute class. The Image Similarity Algebra consists of multiple files, including external files for k-means clustering. [TOC] 1.2 Defines and includes */ #include #include #include #include #include #include #include #include #include #include "../JPEGImage.h" #include #include #include #include #include #include #include #include #include #include #include #include /* 1.0 Euclidean distance */ double euclidDist(FeatureSignatureTuple ist1, FeatureSignatureTuple ist2) { double tmpRes = 0.0; tmpRes += pow(ist1.weight - ist2.weight, 2); tmpRes += pow(ist1.centroid.x - ist2.centroid.x, 2); tmpRes += pow(ist1.centroid.y - ist2.centroid.y, 2); tmpRes += pow(ist1.centroid.colorValue1 - ist2.centroid.colorValue1, 2); tmpRes += pow(ist1.centroid.colorValue2 - ist2.centroid.colorValue2, 2); tmpRes += pow(ist1.centroid.colorValue3 - ist2.centroid.colorValue3, 2); tmpRes += pow(ist1.centroid.coarseness - ist2.centroid.coarseness, 2); tmpRes += pow(ist1.centroid.contrast - ist2.centroid.contrast, 2); return sqrt(tmpRes); } /* 2.0 Type for a cell of the tableau */ struct Cell { int x; int y; long val; }; /* 3.0 Type for a vertex */ class Vertex { public: Vertex() {}; Vertex(int x, int y, bool visited) : _x(x), _y(y), _visited(visited){} int _x; int _y; bool _visited; }; bool operator== (const Vertex v1, const Vertex v2) { return ((v1._x == v2._x) && (v1._y == v2._y)); } bool operator!= (const Vertex v1, const Vertex v2) { return ((v1._x != v2._x) || (v1._y != v2._y)); } bool operator< (const Vertex v1, const Vertex v2) { return ((v1._x < v2._x) || (v1._y < v2._y)); } /* 4.0 Type for an edge */ class Edge { public: Edge(Vertex* from, Vertex* to): _from(from), _to(to){} Vertex* _from; Vertex* _to; }; /* 5.0 Minigraph class, needed in optimization step */ class MiniGraph { public: ~MiniGraph(); void addVertex(int x, int y, bool visited); void addEdge(Vertex* vp1, Vertex* vp2); void getElementaryPath(Vertex* startingVertex); bool dfs(int v, int p); bool* visited; std::vector> g; std::vector edges; std::vector vertices; //std::vectorpath; std::vector path; }; MiniGraph::~MiniGraph() { } void MiniGraph::addVertex(int x, int y, bool visited) { Vertex v = Vertex(x, y, visited); //Vertex* vp = &v; this->vertices.push_back(v); } void MiniGraph::addEdge(Vertex* vp1, Vertex* vp2) { Edge e = Edge(vp1, vp2); //Edge* ep = &e; this->edges.push_back(e); } bool MiniGraph::dfs(int v, int p) { this->visited[v] = true; //std::cout << "pp:" << p << std::endl; this->path.push_back(p); for (int i = 0; i < (int)this->g[v].size(); i++) { if (!this->visited[this->g[v][i]]) { //std::cout << "p:" << p << std::endl; //this->path.push_back(p); dfs(this->g[v][i], v); return true; } if (this->g[v][i] != p) { return true; } } this->path.pop_back(); return false; } /* 6.0 Index improvement entry, same as a cell in a tableau */ struct IIEntry { double value; int x; int y; }; struct MyCoord { int x; int y; bool visited; }; /* 7.0 definition of the transport problem */ class TransportProblem { public: TransportProblem(); ~TransportProblem(); double distance; bool visitedSteppingStones; bool visitedShadowCosts; bool visitedUpdateSolution; // void initialNWCM(std::vector fst1, // std::vector fst2); void initialVAM(std::vector fst1, std::vector fst2); int getRowLowestCost(int row); int getColLowestCost(int col); void calcRowPenalties(); void calcColPenalties(); std::vector sup; std::vector dem; int maxRowPenIdx(); int maxColPenIdx(); int rowPenIdx; int rowLcIdx; int colPenIdx; int colLcIdx; bool* rowSat; bool* colSat; //bool* satRow; //bool* satCol; int* rowPen; int* colPen; // void assignGoods(); long** distanceMatrix; // costs / distances long** amountMatrix; // how many goods/dirt got transported bool** basicsMatrix; // which fields are occupied or NULL double transport(std::vector fst1, std::vector fst2); std::vectorbasics; // basic variables (cells used in solution) // std::vectornonBasics; std::vectorbasics2; // basic variables (cells used in solution) // std::vector>pathes; // elementary pathes //std::vector uCosts; // shadow costs //std::vector vCosts; // shadow costs (costs = u + v) bool basicsError; int* ucost; int* vcost; int supSize; // nodes in supply (size of signature 1) int demSize; // nodes in demand (size of signature 2) void calcShadowCosts(); Cell enteringCell; std::vectorimprovementIndexes; static bool compareIIEntries(const IIEntry& i1, const IIEntry& i2); //todo: IIEntry should be an inner class MiniGraph* mg; // graph required for finding stepping stones // bool** adjMatrix; void findSteppingStones(); // takes coordidates of entering cell void bfs(MyCoord start, MyCoord destination); std::vectorpath; int* parents; int* colors; int** adjList; bool* visited; int* path2; void getPathes(int s, int d); void getPathesUtil(int u, int d, int pathIdx, std::vector> g); void dfs(int v); //void dfsRun(int v, bool visited[], //std::vector> g); void dfsRun(int v, bool visited[], std::vector> g, std::stack* vis); bool visitVertex(int v, int parent, std::vector> g); void printCycle(int v, int u); //void triggerChainReaction(Vertex start, MiniGraph* g); //void runPath(Cell c, Cell dest, bool horizontal, int x, int y, //std::vector* path); //void scanRow(int x, int y, Cell dest); //void scanCol(int x, int y, Cell dest); std::stack st; int* vis; void depth_first_search(int u, std::vector> g); void dfs_visit(int u, int dest, int time, std::vector> g); std::vectorloop; void findSimpleLoop(int x, int y); void updateSolution(); double currentDistance; double newDistance; }; TransportProblem::TransportProblem() : visitedSteppingStones(false), visitedShadowCosts(false), visitedUpdateSolution(false) { this->mg = new MiniGraph(); } // this method will only detect rectangular loops // closed loops can be way more complex and switching // multiple times between x and y axis. void TransportProblem::findSimpleLoop(int x, int y) { std::vector xBasics; for (int j = 0; j < (int) this->supSize; j++) { for (int i = 0; i < (int) this->demSize; i++) { if (this->basicsMatrix[j][i]) { Cell yb = {i, j, this->distanceMatrix[j][x]}; basics.push_back(yb); } } } std::vectorxEntries; for (int i = 0; i < (int)this->demSize; i++) { if (this->basicsMatrix[y][i]) { Cell xb = {i, y, this->distanceMatrix[i][x]}; xEntries.push_back(xb); } } std::vectoryEntries; for (int j = 0; j < (int)this->supSize; j++) { if (this->basicsMatrix[j][x]) { Cell xb = {x, j, this->distanceMatrix[j][x]}; yEntries.push_back(xb); } } Cell candidate; Cell yEntry; Cell xEntry; for (auto c : xEntries) { for (auto b : basics) { if ((c.x == b.x) && (c.y != b.y)) { // for each match check if there's a yEntry for (auto ye : yEntries) { if (b.y == ye.y) { candidate = {b.x, b.y, this->distanceMatrix[b.y][b.x]}; yEntry = {ye.x, ye.y, this->distanceMatrix[ye.y][ye.x]}; xEntry = {c.x, c.y, this->distanceMatrix[c.y][c.x]}; break; } } } } } this->loop.push_back({x, y, this->distanceMatrix[y][x]}); this->loop.push_back({xEntry.x, xEntry.y, xEntry.val}); this->loop.push_back({candidate.x, candidate.y, candidate.val}); this->loop.push_back({yEntry.x, yEntry.y, yEntry.val}); // find all basics in same row //std::vector xBasics; for (int i = 0; i < (int) this->demSize; i++) { if (this->basicsMatrix[y][i]) { Cell xb = {i, y, this->distanceMatrix[y][i]}; xBasics.push_back(xb); } } // find all basics in same column std::vector yBasics; for (int j = 0; j < (int) this->supSize; j++) { if (this->basicsMatrix[j][x]) { Cell yb = {x, j, this->distanceMatrix[j][x]}; yBasics.push_back(yb); } } std::vector candidates; for (auto c : xBasics) { //std::cout << "checking xBasics: x:" << c.x << ", " //<< c.y << std::endl; for (int j = 0; j < (int)this->supSize;j++) { if (this->basicsMatrix[j][c.x] && (c.y != j) ) { Cell cc = {c.x, j, 0}; candidates.push_back(cc); } } // std::cout << std::endl; } std::vector solution; for (auto c : candidates) { for (int i = 0; i < (int)this->demSize; i++) { if (this->basicsMatrix[c.y][i] && (c.x != i) ) { Cell s = {i,c.y, 0}; solution.push_back(s); } } } } /* 7.0 depth first search in a */ void TransportProblem::depth_first_search(int u, std::vector> g) { this->vis = new int[this->mg->vertices.size()]{0}; // white // this->parents = new int[this->mg->vertices.size()]{-1}; std::cout << "smoke 1" << std::endl; this->dfs_visit(u, u, 1, g); } void TransportProblem::dfs_visit(int u, int dest, int time, std::vector> g) { } void TransportProblem::dfsRun(int v, bool visited[], std::vector> g, std::stack* vis) { vis->push(v); //std::cout << "visiting:" << v << std::endl; if (visited[v]) { std::cout << "found a cycle" << std::endl; for (int i = 0; i < (int)this->mg->vertices.size(); i++) { std::cout << vis->top() << ","; vis->pop(); } return; } else { visited[v] = true; for (int i = 0; i < (int)g[v].size(); i++) { // std::cout << "i:" << i << std::endl; if (!visited[g[v][i]]) { dfsRun(g[v][i], visited, g, vis); visited[v] = false; vis->pop(); } } } } void TransportProblem::dfs(int s) { //this->visited = new bool[this->mg->vertices.size()]; std::vector> g(this->mg->vertices.size()); std::vector p; for (int i = 0; i < (int)this->mg->vertices.size(); i++) { for (int j = 0; j < (int)this->mg->vertices.size(); j++) { if ((this->mg->vertices[i]._x == this->mg->vertices[j]._x) ||(this->mg->vertices[i]._y == this->mg->vertices[j]._y)) { if (j != i) { g[i].push_back(j); } } } } std::cout << "displaying list before:" << std::endl; for (int i = 0; i < (int)this->mg->vertices.size(); i++) { std::cout << i << ":"; for (int j = 0; j < (int)g.at(i).size(); j++) { std::cout << g[i][j] << ","; } std::cout << std::endl; } std::cout << "displaying vertices" << std::endl; for (int i = 0; i < (int)this->mg->vertices.size(); i++) { std::cout << "vert " << i << " :" << this->mg->vertices[i]._y << "," << this->mg->vertices[i]._x << std::endl; } this->visited = new bool[this->mg->vertices.size()]{false}; //int* parent = new int[this->mg->vertices.size()]{false}; // depth_first_search(s,g); std::cout << "end of dfs" << std::endl; for (int i = 0; i < (int)this->mg->vertices.size(); i++) { std::cout << this->parents[i] << ", "; } } /* 8.0 Destructor of the Transportation problem */ TransportProblem::~TransportProblem() { for (int i = 0; i < this->supSize; i++) { delete[] this->amountMatrix[i]; } delete[] this->amountMatrix; delete [] rowSat; delete [] colSat; delete [] rowPen; delete [] colPen; for (int i = 0; i < this->supSize; i++) { delete[] this->distanceMatrix[i]; } delete [] this->distanceMatrix; delete this->mg; if (this->visitedShadowCosts) { delete [] ucost; delete [] vcost; } for (int i = 0; i < this->supSize; i++) { delete[] basicsMatrix[i]; } delete [] basicsMatrix; } bool TransportProblem::compareIIEntries( const IIEntry& i1, const IIEntry& i2) { return i1.value < i2.value; } void TransportProblem::getPathes(int s, int d) { std::vector> g(this->mg->vertices.size()); this->parents = new int[this->mg->vertices.size()]{}; this->colors = new int[this->mg->vertices.size()]{0}; // white // build adjacency list for (int i = 0; i < (int)this->mg->vertices.size(); i++) { for (int j = 0; j < (int)this->mg->vertices.size(); j++) { if ( (this->mg->vertices[i]._x == this->mg->vertices[j]._x) || (this->mg->vertices[i]._y == this->mg->vertices[j]._y)) { if (j != i) { g[i].push_back(j); } } } } // now I cut all vertical edges from the entering cell, so that // no cycle exists // the destination points will be the basic cells which are // on the same column Vertex sVert = this->mg->vertices[s]; // Vertex dVert = this->mg->vertices[d]; for (int i = 0; i < (int)g[s].size(); i++) { for (int j = 0; j < (int)g[s].size(); j++) { if (this->mg->vertices[j]._y == sVert._y) { g[s].erase(g[s].begin() + j); } } } std::cout << std::endl; // std::cout << "dfs displaying list:" << std::endl; for (int i = 0; i < (int)this->mg->vertices.size(); i++) { std::cout << i << ":"; for (int j = 0; j < (int)g.at(i).size(); j++) { std::cout << g[i][j] << ","; } std::cout << std::endl; } std::cout << std::endl; this->visited = new bool[this->mg->vertices.size()]{false}; this->path2 = new int[this->mg->vertices.size()]{}; int pathIdx = 0; this->getPathesUtil(s, d, pathIdx, g); } void TransportProblem::getPathesUtil(int u, int d, int pathIdx, std::vector> g) { this->visited[u] = true; this->path2[pathIdx] = u; pathIdx++; if (u == d) { for (int i = 0; i < pathIdx; i++) { std::cout << this->path2[i] << " "; } std::cout << std::endl; } else { for (int i = 0; i < (int)g[u].size(); i++) { if (!this->visited[i]) { getPathesUtil(i, d, pathIdx, g); } } } pathIdx--; this->visited[u] = false; } void TransportProblem::printCycle(int v, int u) { std::cout << "print cycle: u:" << u << " v:" << v << std::endl; do { std::cout << u << ","; u = this->parents[u]; } while (u != v); } void TransportProblem::bfs(MyCoord start, MyCoord destination) { std::vector> g(this->mg->vertices.size()); int n; // number of vertices int s; // start point int to; n = (int)this->mg->vertices.size(); //std::cout << "displaying s" << std::endl; // find index of s s = 0; for (int i = 0; i < (int)this->mg->vertices.size(); i++) { if ((this->mg->vertices[i]._x == start.x) && (this->mg->vertices[i]._y == start.y)) { s = i; break; } } //std::cout << "displaying to" << std::endl; // find index of to to = 0; for (int i = 0; i < (int)this->mg->vertices.size(); i++) { if ((this->mg->vertices[i]._x == destination.x) && (this->mg->vertices[i]._y == destination.y)) { to = i; break; } } //std::cout << "building list" << std::endl; // create adjacency list // the connection between must be broken for (int i = 0; i < (int)this->mg->vertices.size(); i++) { for (int j = 0; j < (int)this->mg->vertices.size(); j++) { if ( (this->mg->vertices[i]._x == this->mg->vertices[j]._x) || (this->mg->vertices[i]._y == this->mg->vertices[j]._y)) { if (j != i) { if (!((i == s) && (j == to))) { g[i].push_back(j); } } } } } std::cout << "displaying list" << std::endl; for (int i = 0; i < (int)this->mg->vertices.size(); i++) { std::cout << i << ":"; for (int j = 0; j < (int)g.at(i).size(); j++) { std::cout << g[i][j] << ","; } std::cout << std::endl; } std::cout << "destination y:" << destination.y << " x:" << destination.x << std::endl; std::cout << "displaying vertices" << std::endl; for (int i = 0; i < (int)this->mg->vertices.size(); i++) { std::cout << "vert " << i << " :" << this->mg->vertices[i]._y << "," << this->mg->vertices[i]._x << std::endl; } std::queue q; q.push(s); std::vector used(n); std::vector d(n); std::vector p(n); used[s] = true; p[s] = -1; // parent //std::cout << "starting queue" << std::endl; while (!q.empty()) { int v = q.front(); q.pop(); for (int i = 0; i < (int)g[v].size(); ++i) { int to = g[v][i]; if (!used[to]) { used[to] = true; q.push(to); d[to] = d[v] + 1; p[to] = v; } } } //std::cout << "displaying path" << std::endl; if (!used[to]) { // no path //std::cout << "no path" << std::endl; ; } else { std::vector path; for (int v = to; v != -1; v = p[v]) { path.push_back(v); int xv = this->mg->vertices[v]._x; int yv = this->mg->vertices[v]._y; MyCoord c = {xv, yv}; this->path.push_back(c); } reverse(path.begin(), path.end()); reverse(this->path.begin(), this->path.end()); /* std::cout << "path:" << std::endl; for (int i = 0; i < (int)path.size(); i++) { std::cout << path[i] << ", "; } */ } } /* 9.0 Method to find stepping stones in the tableau */ void TransportProblem::findSteppingStones() { this->mg->vertices.erase(this->mg->vertices.begin(), this->mg->vertices.end()); int cntVert = 0; // std::cout << "adding vertices" << std::endl; for (int y = 0; y < this->supSize; y++) { for (int x = 0; x < this->demSize; x++) { if (this->basicsMatrix[y][x]) { this->mg->addVertex(x, y, false); cntVert++; } } } this->mg->addVertex(this->enteringCell.x, this->enteringCell.y,this->enteringCell.val); for (int y = 0; y < this->supSize; y++) { for (int x = 0; x < this->demSize; x++) { if (this->basicsMatrix[y][x]) { this->mg->addVertex(x, y, false); cntVert++; } } } this->mg->addVertex(this->enteringCell.x, this->enteringCell.y,this->enteringCell.val); mg->g.resize(mg->vertices.size()); // std::cout << "adding edges" << std::endl; int half = (int)this->mg->vertices.size() / 2; //std::cout << "half:" << half << std::endl; for (int i = 0; i < half ; i++) { for (int j = 0; j < half; j++) { if (j != i) { if (this->mg->vertices[i]._x == this->mg->vertices[j]._x) { this->mg->g[i].push_back(j+half); } } } } //std::cout << "adding second batch of edges" << std::endl; for (int i = 0; i < half ; i++) { for (int j = 0; j < half; j++) { if (j != i) { if (this->mg->vertices[i]._y == this->mg->vertices[j]._y) { this->mg->g[i+half].push_back(j); } } } } mg->visited = new bool[mg->vertices.size()]{false}; //mg->g.resize(mg->vertices.size()); int pos = this->mg->vertices.size() -1; if (!this->mg->dfs(pos, -1)) { this->basicsError = true; } return; } /* 10.0 Method to update a non-optimal solution */ void TransportProblem::updateSolution() { std::vectortmpPath; for (int i = 1; i < this->mg->path.size() / 2; i++) { tmpPath.push_back(this->mg->path[i]); } // for (auto l : tmpPath) // { // std::cout << " tmp loop:" << this->mg->vertices[l]._x << "," //<< this->mg->vertices[l]._y << std::endl; //} //std::cout << "tmp loop size:" << tmpPath.size() << std::endl; int minBasicVal = std::numeric_limits::max(); int idxMinVal = 0; for (int i = 0; i < this->mg->vertices.size(); i += 2) { Vertex min = this->mg->vertices[i]; int minVal = this->amountMatrix[min._y][min._x]; if (minVal < minBasicVal) { //std::cout << "minVal:" << minVal << std::endl; minBasicVal = minVal; idxMinVal = i; } } Vertex minBasicCell = this->mg->vertices[idxMinVal]; double theta = this->amountMatrix[minBasicCell._y][minBasicCell._x]; //std::cout << "theta:" << theta << std::endl; int x; int y; int ii = 0; for (auto p : tmpPath) { //std::cout << "reducing theta" << std::endl; x = this->mg->vertices[p]._x; y = this->mg->vertices[p]._y; if ((ii % 2) == 0) { //std::cout << "adding theta" << std::endl; this->amountMatrix[y][x] += theta; } else { // std::cout << "removing theta" << std::endl; this->amountMatrix[y][x] -= theta; } ii++; } int dist = 0; for ( int y = 0; y < (int)this->supSize; y++) // rows { for ( int x = 0; x < (int)this->demSize; x++) // columns { //std::cout << std::setw(8) << this->amountMatrix[x][y] //<< "*" << //this->distanceMatrix[x][y] << " = " << //(this->amountMatrix[x][y] * this->distanceMatrix[x][y]) //<< "|"; dist += (this->amountMatrix[y][x] * this->distanceMatrix[y][x]); } //std::cout << "| " << std::setw(8) << dem.at(y).weight << "|"; //std::cout << std::endl; } //std::cout << "new dist:" << this->newDistance << std::endl; this->newDistance = dist; } /* 11.0 Method to calculate the opportunity costs, first step of the u-v method */ void TransportProblem::calcShadowCosts() { this->ucost = new int[this->supSize]{0}; this->vcost = new int[this->demSize]{0}; this->visitedShadowCosts = true; ucost[0] = 0; // for each cell calculate: c_ij = ucost[x] + vcost[y] // the first row equals the distance/costs // for every first cell in a row -> calculate ucost[y] // for all subsequent cells in a row -> calculate vcost[x] for ( int x = 0; x < (int)this->demSize; x++) { if (this->basicsMatrix[0][x]) { vcost[x] = this->distanceMatrix[0][x]; } } // the first cell in a row determines the u value of this row // all following cells determine v values for (int y = 1; y < (int)this->supSize; y++) { bool firstCell = true; for (int x = 0; x < (int)this->demSize; x++) { if (this->basicsMatrix[y][x]) { if (firstCell) { ucost[y] = this->distanceMatrix[y][x] - vcost[x]; firstCell = false; } else { vcost[x] = this->distanceMatrix[y][x] - ucost[y]; } } } } double** oppCostMatrix = new double*[this->supSize]; for ( int y = 0; y < (int)this->supSize; y++) oppCostMatrix[y] = new double[this->demSize]; // the entering cell is the non-basic cell with the lowest value Cell enteringCell; int minOppCost = std::numeric_limits::max(); //std::cout << std::endl; for (int y = 0; y < (int)this->supSize; y++) { for (int x = 0; x < (int)this->demSize; x++) { oppCostMatrix[y][x] = this->distanceMatrix[y][x] - (ucost[y] + vcost[x]); if (!this->basicsMatrix[y][x]) { int cost = this->distanceMatrix[y][x] - (ucost[y] + vcost[x]); //std::cout << cost << "|"; if (cost < minOppCost) { minOppCost = cost; enteringCell = {x, y, cost}; } } } //std::cout << std::endl; } //std::cout << std::endl; // // std::cout << "enteringCell.x:" << enteringCell.x //<< ", " << enteringCell.y << " " //<< enteringCell.val << std::endl; this->enteringCell = enteringCell; for ( int y = 0; y < (int)this->supSize; y++) { delete [] oppCostMatrix[y]; } delete [] oppCostMatrix; } /* 12.0 Method to calculate colum penalties, part of Vogel's approximation method */ void TransportProblem::calcColPenalties() { //std::cout << "calc col penalties:" << std::endl; ; for ( int x = 0; x < (int)this->demSize; x++) { int min = std::numeric_limits::max(); int min2 = std::numeric_limits::max(); for ( int y = 0; y < (int)this->supSize; y++) // vertical { bool satisfied = rowSat[y]; if (!satisfied) { if (this->distanceMatrix[y][x] < min2) { if (this->distanceMatrix[y][x] < min) { min2 = min; min = this->distanceMatrix[y][x]; } else { min2 = this->distanceMatrix[y][x]; } } } } if (min2 != std::numeric_limits::max()) { int tmpPen = min2 - min; this->colPen[x] = tmpPen; } else { this->colPen[x] = min; } // delete [] tmpv; } // std::cout << std::endl; int max = std::numeric_limits::min(); // int idx = 0; // std::cout << "col penalties:"; for ( int i = 0; i < (int)this->demSize; i++) { // std::cout << this->colPen[i] << ","; if (max < this->colPen[i]) { max = this->colPen[i]; // idx = i; } } //std::cout << std::endl; // std:: cout << "found max col:" << max << " at:" //<< idx << std::endl; // return idx; } /* 13.0 Method to calculate row penalties, part of Vogel's approximation method */ void TransportProblem::calcRowPenalties() { // std::cout << "calc row penalties:" << std::endl;; for ( int y = 0; y < (int)this->supSize; y++) // horizontal { int min = std::numeric_limits::max(); int min2 = std::numeric_limits::max(); for ( int x = 0; x < (int)this->demSize; x++) { bool satisfied = colSat[x]; if (!satisfied) { if (this->distanceMatrix[y][x] < min2) { if (this->distanceMatrix[y][x] < min) { min2 = min; min = this->distanceMatrix[y][x]; } else { min2 = this->distanceMatrix[y][x]; } } } } if (min2 != std::numeric_limits::max()) { int tmpPen = min2 - min; this->rowPen[y] = tmpPen; } else { this->rowPen[y] = min; } } int max = std::numeric_limits::min(); // int idx = 0; // std::cout << "row penalties:"; for ( int i = 0; i < (int)this->supSize; i++) { //std::cout << this->rowPen[i] << ","; if (max < this->rowPen[i]) { max = this->rowPen[i]; // idx = i; } } //std::cout << std::endl; // std:: cout << "found max row:" << max << " at:" << idx //<< std::endl; // return idx; } /* 14.0 Method to calculate the lowest cost per row */ int TransportProblem::getColLowestCost(int row) { int min = std::numeric_limits::max(); int idx; //std::cout << "getColLowestCost in row:" << row << std::endl; for (int x = 0; x < (int)this->demSize; x++) { //std::cout << "x:" << x << std::endl; if (this->colSat[x]) { continue; } // std::cout << "x2:" << x << std::endl; // std::cout << this->distanceMatrix[row][x] << ","; // std::cout << " min:" << min << " "; if (this->distanceMatrix[row][x] < min) { min = distanceMatrix[row][x]; idx = x; } } // std::cout << "min cost col idx:" << idx << " val:" //<< distanceMatrix[row][idx] << std::endl; return idx; } /* 14.0 Method to calculate the lowest cost per column */ int TransportProblem::getRowLowestCost(int col) { //std::cout << std::endl; int min = std::numeric_limits::max(); int idx; //std::cout << "getRowLowestCost in col:" << col << std::endl; for ( int y = 0; y < (int)this->supSize; y++) { if (this->rowSat[y]) { continue; } //std::cout << this->distanceMatrix[y][col] << ","; if (this->distanceMatrix[y][col] < min) { min = distanceMatrix[y][col]; idx = y; } } // std::cout << "min cost row idx:" << idx << " val:" // << distanceMatrix[idx][col] << std::endl; return idx; } int TransportProblem::maxRowPenIdx() { int idx = 0; int maxVal = 0; for ( int i = 0; i < (int)this->supSize; i++) { if (!this->rowSat[i]) { if (maxVal < this->rowPen[i]) { maxVal = this->rowPen[i]; idx = i; } } } return idx; } int TransportProblem::maxColPenIdx() { int idx = 0; int maxVal = 0; for ( int i = 0; i < (int)this->demSize; i++) { // std::cout << "colPenalty:" << this->colPen[i] << ","; //std::cout << std::endl; if (!this->colSat[i]) { if (maxVal < this->colPen[i]) { maxVal = this->colPen[i]; idx = i; } } } //std::cout << std::endl; //std::cout << "maxColPenIdx:" << idx << std::endl; return idx; } /* 16.0 Method to create an inital feasible solution using Vogel's approximation method */ void TransportProblem::initialVAM( std::vector fst1, std::vector fst2) { sup = fst1; dem = fst2; this->sup = sup; this->dem = dem; long sumSup = 0.0; long sumDem = 0.0; for (auto fst : sup) sumSup += fst.weight; for (auto fst : dem) sumDem += fst.weight; //std::cout << "supWeights:" ; std::vectorsupWeights; for (int i = 0; i < (int)sup.size(); i++) { // std::cout << (long)round(sup[i].weight * 10000.0) //<< std::endl; supWeights.push_back((long)round(sup[i].weight * 10000.0) ); } std::vectordemWeights; for (int i = 0; i < (int)dem.size(); i++) { demWeights.push_back((long)round(dem[i].weight * 10000.0) ); } for (auto fst : supWeights) sumSup += fst; for (auto fst : demWeights) sumDem += fst; //std::cout << "sumSup: before:" << sumSup << std::endl; //std::cout << "sumDem before :" << sumDem << std::endl; if (sumSup > sumDem) { // add an artifical node to demand holding the difference // the cost will be zero FeatureSignatureTuple fst; // std::cout << "sumSup:" << sumSup << std::endl; // std::cout << "sumDem:" << sumDem << std::endl; long dummyWeight = sumSup - sumDem; // std::cout << "dummy weight, sup:" << dummyWeight << std::endl; fst = {0.0, 0,0,0.0,0.0,0.0,0.0,0.0}; dem.push_back(fst); demWeights.push_back(dummyWeight); } else if (sumSup < sumDem) { // add an artifical node to demand holding the difference // the cost will be zero FeatureSignatureTuple fst; long dummyWeight = sumDem - sumSup; // std::cout << "dummy weight, dem:" << dummyWeight << std::endl; fst = {0.0, 0,0,0.0,0.0,0.0,0.0,0.0}; sup.push_back(fst); supWeights.push_back(dummyWeight); } this->supSize = sup.size(); this->demSize = dem.size(); // std::cout << "demSize:" << this->demSize << std::endl; // std::cout << "supSize 2:" << this->supSize << std::endl; sumSup = 0.0; sumDem = 0.0; for (auto fst : supWeights) sumSup += fst; for (auto fst : demWeights) sumDem += fst; // std::cout << "sumSup:" << sumSup << std::endl; // std::cout << "sumDem:" << sumDem << std::endl; // 2 a. init distance matrix this->distanceMatrix = new long*[this->supSize]; for ( int y = 0; y < (int)this->supSize; y++) this->distanceMatrix[y] = new long[this->demSize]; // std::cout << "fog 2" << std::endl; // 2. fill distance matrix, //the artificial node has the distance of zero for ( int y = 0; y < (int)this->supSize; y++) // rows { for ( int x = 0; x < (int)this->demSize; x++) // columns { // std::cout << "i:" << i << std::endl; this->distanceMatrix[y][x] = (long)round(euclidDist( sup.at(y), dem.at(x)) * 1000.0) / 1000.0; } } // 2a. init basics matrix // this table will keep track of occupied cells (basic variables) this->basicsMatrix = new bool*[this->supSize]; for ( int y = 0; y < (int)this->supSize; y++) this->basicsMatrix[y] = new bool[this->demSize]{false}; //std::cout << "fog 6" << std::endl; // 3. init transport matrix, stores how much is transported this->amountMatrix = new long*[this->supSize]; for ( int y = 0; y < (int)this->supSize; y++) this->amountMatrix[y] = new long[this->demSize]{}; // 4. setup arrays to store penalties this->rowPen = new int[this->supSize]{0}; this->colPen = new int[this->demSize]{0}; // 5. setup array for column / row satisfied this->rowSat = new bool[this->supSize]{false}; this->colSat = new bool[this->demSize]{false}; // std::cout << "fog 7" << std::endl; calcRowPenalties(); // vertical penalties int rowPenIdx; rowPenIdx = maxRowPenIdx(); // idx of the biggest penalty int rowMinIdx = getColLowestCost(rowPenIdx); calcColPenalties(); int colPenIdx; colPenIdx = maxColPenIdx(); int colMinIdx = getRowLowestCost(colPenIdx); const int MAX_ITERATIONS = 200; for (int iter = 0; iter < MAX_ITERATIONS; iter++) { //std::cout << "--------------------- start ------------------- " //<< iter << std::endl; bool allCols = true; for (int i = 0; i < (int)this->demSize; i++) { if (!this->colSat[i]) { allCols = false; } } bool allRows = true; for (int i = 0; i < (int)this->supSize; i++) { if (!this->rowSat[i]) { allRows = false; } } if (allCols && allRows) { //std::cout << "breaking good" << std::endl; break; } colPenIdx = maxColPenIdx(); rowPenIdx = maxRowPenIdx(); colMinIdx = getRowLowestCost(colPenIdx); rowMinIdx = getColLowestCost(rowPenIdx); if (this->rowPen[rowPenIdx] >= this->colPen[colPenIdx]) { //std::cout << "max row is larger, "; // set either col or row to satisfied, whichever is 0 //std::cout << "sup[rowPenIdx]:" << supWeights[rowPenIdx] //<< " dem[rowMinIdx]:" << demWeights[rowMinIdx] // << std::endl; //std::cout << "rowPenIdx:" << rowPenIdx << std::endl; //std::cout << "rowMinIdx:" << rowMinIdx << std::endl; if (supWeights[rowPenIdx] > demWeights[rowMinIdx]) { this->colSat[rowMinIdx] = true; //std::cout << "f1:" << std::endl; this->amountMatrix[rowPenIdx][rowMinIdx] = demWeights[rowMinIdx]; //std::cout << "f2:" << std::endl; supWeights[rowPenIdx] -= demWeights[rowMinIdx]; //std::cout << "f3:" << std::endl; demWeights[rowMinIdx] = 0; // calcColPenalties(); // change calcRowPenalties(); //rowPenIdx = maxRowPenIdx(); } else if (supWeights[rowPenIdx] < demWeights[rowMinIdx]) { // -- done this->rowSat[rowPenIdx] = true; this->amountMatrix[rowPenIdx][rowMinIdx] = supWeights[rowPenIdx]; demWeights[rowMinIdx] -= supWeights[rowPenIdx]; supWeights[rowPenIdx] = 0; calcColPenalties(); // rowPenIdx = maxRowPenIdx(); } else { this->rowSat[rowPenIdx] = true; this->colSat[rowMinIdx] = true; this->amountMatrix[rowPenIdx][rowMinIdx] = supWeights[rowPenIdx]; demWeights[rowMinIdx] = 0; supWeights[rowPenIdx] = 0; calcColPenalties(); calcRowPenalties(); } } else { // std::cout << "max col is larger "; // std::cout << "col sup[rowMinIdx]:" //<< supWeights[colMinIdx] // << " dem[colPenIdx]:" << demWeights[colPenIdx] // << std::endl; if (supWeights[colMinIdx] > demWeights[colPenIdx]) { this->colSat[colPenIdx] = true; this->amountMatrix[colMinIdx][colPenIdx] = demWeights[colPenIdx]; supWeights[colMinIdx] -= demWeights[colPenIdx]; demWeights[colPenIdx] = 0; calcRowPenalties(); // colPenIdx = maxColPenIdx(); } else if (supWeights[colMinIdx] < demWeights[colPenIdx]) { this->rowSat[colMinIdx] = true; this->amountMatrix[colMinIdx][colPenIdx] = supWeights[colMinIdx]; demWeights[colPenIdx] -= supWeights[colMinIdx]; supWeights[colMinIdx] = 0; calcColPenalties(); // changed // colPenIdx = maxColPenIdx(); } else { this->rowSat[colMinIdx] = true; this->colSat[colPenIdx] = true; this->amountMatrix[colMinIdx][colPenIdx] = supWeights[colMinIdx]; demWeights[colPenIdx] = 0; supWeights[colMinIdx] = 0; calcRowPenalties(); calcColPenalties(); } } } long dist = 0; for ( int y = 0; y < (int)this->supSize; y++) // rows { for ( int x = 0; x < (int)this->demSize; x++) // columns { dist += (this->amountMatrix[y][x] * this->distanceMatrix[y][x]); } } this->newDistance = (double)(dist / 1); this->currentDistance = (double)(dist / 1); } int main(int argc, char** argv) { if (argc < 2) { std::cout << "please provide two signature files" << std::endl; return 1; } std::vector ist1; std::vector ist2; double w; int x, y; double r,g,b; double coa, con; std::ifstream infile(argv[1]); std::string std; //int counter = 0; while (infile >> w >> x >> y >> r >> g >> b >> coa >> con) { ist1.push_back({w, x, y, r, g, b, coa, con}); } std::ifstream infile2(argv[2]); while (infile2 >> w >> x >> y >> r >> g >> b >> coa >> con) { ist2.push_back({w, x, y, r, g, b, coa, con}); } TransportProblem tp; std::cout << argv[1] << "," << argv[2] << ","; tp.initialVAM(ist1, ist2); const int maxIterations = 50; // how often does the algorithm cycle int actualIterations = 0; tp.basicsError = false; for (int i = 0; i < maxIterations; i++) { actualIterations++; if (tp.currentDistance == 0) { break; } try { tp.calcShadowCosts(); tp.visitedShadowCosts = true; if (tp.enteringCell.val > -1) { break; // solution is already optimal } tp.visitedSteppingStones = true; tp.findSteppingStones(); if (!tp.basicsError) { tp.visitedUpdateSolution = true; tp.updateSolution(); } else { break; } } catch (std::exception& e) { std::cout << e.what() << '\n'; std::cout << argv[1] << " " << argv[2] << std::endl; } if (tp.currentDistance > tp.newDistance) { tp.currentDistance = tp.newDistance; } else { break; } } std::cout << "current distance:" << tp.currentDistance << std::endl; return 0; }