/* * Graph.hpp, part of Grid * * Copyright (C) 2015 Antonin Portelli * * Grid 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 3 of the License, or * (at your option) any later version. * * Grid 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 Grid. If not, see . */ #ifndef Hadrons_Graph_hpp_ #define Hadrons_Graph_hpp_ #include BEGIN_HADRONS_NAMESPACE /****************************************************************************** * Oriented graph class * ******************************************************************************/ // I/O for edges template std::ostream & operator<<(std::ostream &out, const std::pair &e) { out << "\"" << e.first << "\" -> \"" << e.second << "\""; return out; } // main class template class Graph { public: typedef std::pair Edge; public: // constructor Graph(void) = default; // destructor virtual ~Graph(void) = default; // access void addVertex(const T &value); void addEdge(const Edge &e); void addEdge(const T &start, const T &end); void removeVertex(const T &value); void removeEdge(const Edge &e); void removeEdge(const T &start, const T &end); unsigned int size(void) const; // tests bool gotValue(const T &value) const; // graph topological manipulations std::vector getAdjacentVertices(const T &value) const; std::vector getChildren(const T &value) const; std::vector getParents(const T &value) const; std::vector getRoots(void) const; std::vector> getConnectedComponents(void) const; std::vector topoSort(void); std::vector> allTopoSort(void); // I/O friend std::ostream & operator<<(std::ostream &out, const Graph &g) { out << "{"; for (auto &e: g.edgeSet_) { out << e << ", "; } if (g.edgeSet_.size() != 0) { out << "\b\b"; } out << "}"; return out; } private: // vertex marking void mark(const T &value, const bool doMark = true); void markAll(const bool doMark = true); void unmark(const T &value); void unmarkAll(void); bool isMarked(const T &value) const; const T * getFirstMarked(const bool isMarked = true) const; const T * getFirstUnmarked(void) const; // prune marked/unmarked vertices void removeMarked(const bool isMarked = true); void removeUnmarked(void); // depth-first search marking void depthFirstSearch(void); void depthFirstSearch(const T &root); private: std::map isMarked_; std::set edgeSet_; }; // build depedency matrix from topological sorts template std::map> makeDependencyMatrix(const std::vector> &topSort); /****************************************************************************** * template implementation * ******************************************************************************/ // access ////////////////////////////////////////////////////////////////////// template void Graph::addVertex(const T &value) { isMarked_[value] = false; } template void Graph::addEdge(const Edge &e) { addVertex(e.first); addVertex(e.second); edgeSet_.insert(e); } template void Graph::addEdge(const T &start, const T &end) { addEdge(Edge(start, end)); } template void Graph::removeVertex(const T &value) { // remove vertex from the mark table auto vIt = isMarked_.find(value); if (vIt != isMarked_.end()) { isMarked_.erase(vIt); } else { HADRON_ERROR("vertex " << value << " does not exists"); } // remove all edges containing the vertex auto pred = [&value](const Edge &e) { return ((e.first == value) or (e.second == value)); }; auto eIt = find_if(edgeSet_.begin(), edgeSet_.end(), pred); while (eIt != edgeSet_.end()) { edgeSet_.erase(eIt); eIt = find_if(edgeSet_.begin(), edgeSet_.end(), pred); } } template void Graph::removeEdge(const Edge &e) { auto eIt = edgeSet_.find(e); if (eIt != edgeSet_.end()) { edgeSet_.erase(eIt); } else { HADRON_ERROR("edge " << e << " does not exists"); } } template void Graph::removeEdge(const T &start, const T &end) { removeEdge(Edge(start, end)); } template unsigned int Graph::size(void) const { return isMarked_.size(); } // tests /////////////////////////////////////////////////////////////////////// template bool Graph::gotValue(const T &value) const { try { isMarked_.at(value); } catch (std::out_of_range &) { return false; } return true; } // vertex marking ////////////////////////////////////////////////////////////// template void Graph::mark(const T &value, const bool doMark) { try { isMarked_.at(value) = doMark; } catch (std::out_of_range &) { HADRON_ERROR("vertex " << value << " does not exists"); } } template void Graph::markAll(const bool doMark) { for (auto &v: isMarked_) { mark(v.first, doMark); } } template void Graph::unmark(const T &value) { mark(value, false); } template void Graph::unmarkAll(void) { markAll(false); } template bool Graph::isMarked(const T &value) const { try { return isMarked_.at(value); } catch (std::out_of_range &) { HADRON_ERROR("vertex " << value << " does not exists"); return false; } } template const T * Graph::getFirstMarked(const bool isMarked) const { auto pred = [&isMarked](const std::pair &v) { return (v.second == isMarked); }; auto vIt = std::find_if(isMarked_.begin(), isMarked_.end(), pred); if (vIt != isMarked_.end()) { return &(vIt->first); } else { return nullptr; } } template const T * Graph::getFirstUnmarked(void) const { return getFirstMarked(false); } // prune marked/unmarked vertices ////////////////////////////////////////////// template void Graph::removeMarked(const bool isMarked) { auto isMarkedCopy = isMarked_; for (auto &v: isMarkedCopy) { if (v.second == isMarked) { removeVertex(v.first); } } } template void Graph::removeUnmarked(void) { removeMarked(false); } // depth-first search marking ////////////////////////////////////////////////// template void Graph::depthFirstSearch(void) { depthFirstSearch(isMarked_.begin()->first); } template void Graph::depthFirstSearch(const T &root) { std::vector adjacentVertex; mark(root); adjacentVertex = getAdjacentVertices(root); for (auto &v: adjacentVertex) { if (!isMarked(v)) { depthFirstSearch(v); } } } // graph topological manipulations ///////////////////////////////////////////// template std::vector Graph::getAdjacentVertices(const T &value) const { std::vector adjacentVertex; auto pred = [&value](const Edge &e) { return ((e.first == value) or (e.second == value)); }; auto eIt = find_if(edgeSet_.begin(), edgeSet_.end(), pred); while (eIt != edgeSet_.end()) { if (eIt->first == value) { adjacentVertex.push_back((*eIt).second); } else if (eIt->second == value) { adjacentVertex.push_back((*eIt).first); } eIt = find_if(++eIt, edgeSet_.end(), pred); } return adjacentVertex; } template std::vector Graph::getChildren(const T &value) const { std::vector child; auto pred = [&value](const Edge &e) { return (e.first == value); }; auto eIt = find_if(edgeSet_.begin(), edgeSet_.end(), pred); while (eIt != edgeSet_.end()) { child.push_back((*eIt).second); eIt = find_if(++eIt, edgeSet_.end(), pred); } return child; } template std::vector Graph::getParents(const T &value) const { std::vector parent; auto pred = [&value](const Edge &e) { return (e.second == value); }; auto eIt = find_if(edgeSet_.begin(), edgeSet_.end(), pred); while (eIt != edgeSet_.end()) { parent.push_back((*eIt).first); eIt = find_if(++eIt, edgeSet_.end(), pred); } return parent; } template std::vector Graph::getRoots(void) const { std::vector root; for (auto &v: isMarked_) { auto parent = getParents(v.first); if (parent.size() == 0) { root.push_back(v.first); } } return root; } template std::vector> Graph::getConnectedComponents(void) const { std::vector> res; Graph copy(*this); while (copy.size() > 0) { copy.depthFirstSearch(); res.push_back(copy); res.back().removeUnmarked(); res.back().unmarkAll(); copy.removeMarked(); copy.unmarkAll(); } return res; } // topological sort using Tarjan's algorithm template std::vector Graph::topoSort(void) { std::stack buf; std::vector res; const T *vPt; std::map tmpMarked(isMarked_); // visit function std::function visit = [&](const T &v) { if (tmpMarked.at(v)) { HADRON_ERROR("cannot topologically sort a cyclic graph"); } if (!this->isMarked(v)) { std::vector child = this->getChildren(v); tmpMarked[v] = true; for (auto &c: child) { visit(c); } this->mark(v); tmpMarked[v] = false; buf.push(v); } }; // reset temporary marks for (auto &v: tmpMarked) { tmpMarked.at(v.first) = false; } // loop on unmarked vertices vPt = getFirstUnmarked(); while (vPt) { visit(*vPt); vPt = getFirstUnmarked(); } unmarkAll(); // create result vector while (!buf.empty()) { res.push_back(buf.top()); buf.pop(); } return res; } // generate all possible topological sorts // Y. L. Varol & D. Rotem, Comput. J. 24(1), pp. 83–84, 1981 // http://comjnl.oupjournals.org/cgi/doi/10.1093/comjnl/24.1.83 template std::vector> Graph::allTopoSort(void) { std::vector> res; std::map> iMat; // create incidence matrix for (auto &v1: isMarked_) for (auto &v2: isMarked_) { iMat[v1.first][v2.first] = false; } for (auto &v: isMarked_) { auto cVec = getChildren(v.first); for (auto &c: cVec) { iMat[v.first][c] = true; } } // generate initial topological sort res.push_back(topoSort()); // generate all other topological sorts by permutation std::vector p = res[0]; const unsigned int n = size(); std::vector loc(n); unsigned int i, k, k1; T obj_k, obj_k1; bool isFinal; for (unsigned int j = 0; j < n; ++j) { loc[j] = j; } i = 0; while (i < n-1) { k = loc[i]; k1 = k + 1; obj_k = p[k]; if (k1 >= n) { isFinal = true; obj_k1 = obj_k; } else { isFinal = false; obj_k1 = p[k1]; } if (iMat[res[0][i]][obj_k1] or isFinal) { for (unsigned int l = k; l >= i + 1; --l) { p[l] = p[l-1]; } p[i] = obj_k; loc[i] = i; i++; } else { p[k] = obj_k1; p[k1] = obj_k; loc[i] = k1; i = 0; res.push_back(p); } } return res; } // build depedency matrix from topological sorts /////////////////////////////// template std::map> makeDependencyMatrix(const std::vector> &topSort) { std::map> m; const std::vector &vList = topSort[0]; for (auto &v1: vList) for (auto &v2: vList) { bool dep = true; for (auto &t: topSort) { auto i1 = std::find(t.begin(), t.end(), v1); auto i2 = std::find(t.begin(), t.end(), v2); dep = dep and (i1 - i2 > 0); if (!dep) break; } m[v1][v2] = dep; } return m; } END_HADRONS_NAMESPACE #endif // Hadrons_Graph_hpp_