/* * 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::stack topoSort(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_; }; /****************************************************************************** * 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 Kahn's algorithm template std::stack Graph::topoSort(void) { std::stack 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; res.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(); return res; } END_HADRONS_NAMESPACE #endif // Hadrons_Graph_hpp_