Chris@16: // Copyright Louis Dionne 2013 Chris@16: Chris@16: // Use, modification and distribution is subject to the Boost Software Chris@16: // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy Chris@16: // at http://www.boost.org/LICENSE_1_0.txt) Chris@16: Chris@16: #ifndef BOOST_GRAPH_HAWICK_CIRCUITS_HPP Chris@16: #define BOOST_GRAPH_HAWICK_CIRCUITS_HPP Chris@16: Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include // for boost::tie Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include // for std::pair Chris@16: #include Chris@16: Chris@16: Chris@16: namespace boost { Chris@16: namespace hawick_circuits_detail { Chris@16: //! @internal Functor returning all the vertices adjacent to a vertex. Chris@16: struct get_all_adjacent_vertices { Chris@16: template Chris@16: struct result; Chris@16: Chris@16: template Chris@16: struct result { Chris@16: private: Chris@16: typedef typename remove_reference::type RawGraph; Chris@16: typedef graph_traits Traits; Chris@16: typedef typename Traits::adjacency_iterator AdjacencyIterator; Chris@16: Chris@16: public: Chris@16: typedef std::pair type; Chris@16: }; Chris@16: Chris@16: template Chris@16: typename result< Chris@16: get_all_adjacent_vertices(BOOST_FWD_REF(Vertex), BOOST_FWD_REF(Graph)) Chris@16: >::type Chris@16: operator()(BOOST_FWD_REF(Vertex) v, BOOST_FWD_REF(Graph) g) const { Chris@16: return adjacent_vertices(boost::forward(v), Chris@16: boost::forward(g)); Chris@16: } Chris@16: }; Chris@16: Chris@16: //! @internal Functor returning a set of the vertices adjacent to a vertex. Chris@16: struct get_unique_adjacent_vertices { Chris@16: template Chris@16: struct result; Chris@16: Chris@16: template Chris@16: struct result { Chris@16: typedef std::set::type> type; Chris@16: }; Chris@16: Chris@16: template Chris@16: typename result::type Chris@16: operator()(Vertex v, Graph const& g) const { Chris@16: typedef typename result< Chris@16: get_unique_adjacent_vertices(Vertex, Graph const&) Chris@16: >::type Set; Chris@16: return Set(adjacent_vertices(v, g).first, Chris@16: adjacent_vertices(v, g).second); Chris@16: } Chris@16: }; Chris@16: Chris@16: //! @internal Chris@16: //! Return whether a container contains a given value. Chris@16: //! This is not meant as a general purpose membership testing function; it Chris@16: //! would have to be more clever about possible optimizations. Chris@16: template Chris@16: bool contains(Container const& c, Value const& v) { Chris@16: return std::find(boost::begin(c), boost::end(c), v) != boost::end(c); Chris@16: } Chris@16: Chris@16: /*! Chris@16: * @internal Chris@16: * Algorithm finding all the cycles starting from a given vertex. Chris@16: * Chris@16: * The search is only done in the subgraph induced by the starting vertex Chris@16: * and the vertices with an index higher than the starting vertex. Chris@16: */ Chris@16: template < Chris@16: typename Graph, Chris@16: typename Visitor, Chris@16: typename VertexIndexMap, Chris@16: typename Stack, Chris@16: typename ClosedMatrix, Chris@16: typename GetAdjacentVertices Chris@16: > Chris@16: struct hawick_circuits_from { Chris@16: private: Chris@16: typedef graph_traits Traits; Chris@16: typedef typename Traits::vertex_descriptor Vertex; Chris@16: typedef typename Traits::edge_descriptor Edge; Chris@16: typedef typename Traits::vertices_size_type VerticesSize; Chris@16: typedef typename property_traits::value_type VertexIndex; Chris@16: Chris@16: typedef typename result_of< Chris@16: GetAdjacentVertices(Vertex, Graph const&) Chris@16: >::type AdjacentVertices; Chris@16: typedef typename range_iterator::type AdjacencyIterator; Chris@16: Chris@16: // The one_bit_color_map starts all white, i.e. not blocked. Chris@16: // Since we make that assumption (I looked at the implementation, but Chris@16: // I can't find anything that documents this behavior), we're gonna Chris@16: // assert it in the constructor. Chris@16: typedef one_bit_color_map BlockedMap; Chris@16: typedef typename property_traits::value_type BlockedColor; Chris@16: Chris@16: static BlockedColor blocked_false_color() Chris@16: { return color_traits::white(); } Chris@16: Chris@16: static BlockedColor blocked_true_color() Chris@16: { return color_traits::black(); } Chris@16: Chris@16: // This is used by the constructor to secure the assumption Chris@16: // documented above. Chris@16: bool blocked_map_starts_all_unblocked() const { Chris@16: BOOST_FOREACH(Vertex v, vertices(graph_)) Chris@16: if (is_blocked(v)) Chris@16: return false; Chris@16: return true; Chris@16: } Chris@16: Chris@16: // This is only used in the constructor to make sure the optimization of Chris@16: // sharing data structures between iterations does not break the code. Chris@16: bool all_closed_rows_are_empty() const { Chris@16: BOOST_FOREACH(typename ClosedMatrix::reference row, closed_) Chris@16: if (!row.empty()) Chris@16: return false; Chris@16: return true; Chris@16: } Chris@16: Chris@16: public: Chris@16: hawick_circuits_from(Graph const& graph, Visitor& visitor, Chris@16: VertexIndexMap const& vim, Chris@16: Stack& stack, ClosedMatrix& closed, Chris@16: VerticesSize n_vertices) Chris@16: : graph_(graph), visitor_(visitor), vim_(vim), stack_(stack), Chris@16: closed_(closed), blocked_(n_vertices, vim_) Chris@16: { Chris@16: BOOST_ASSERT(blocked_map_starts_all_unblocked()); Chris@16: Chris@16: // Since sharing the data structures between iterations is Chris@16: // just an optimization, it must always be equivalent to Chris@16: // constructing new ones in this constructor. Chris@16: BOOST_ASSERT(stack_.empty()); Chris@16: BOOST_ASSERT(closed_.size() == n_vertices); Chris@16: BOOST_ASSERT(all_closed_rows_are_empty()); Chris@16: } Chris@16: Chris@16: private: Chris@16: //! @internal Return the index of a given vertex. Chris@16: VertexIndex index_of(Vertex v) const { Chris@16: return get(vim_, v); Chris@16: } Chris@16: Chris@16: Chris@16: //! @internal Return whether a vertex `v` is closed to a vertex `u`. Chris@16: bool is_closed_to(Vertex u, Vertex v) const { Chris@16: typedef typename ClosedMatrix::const_reference VertexList; Chris@16: VertexList closed_to_u = closed_[index_of(u)]; Chris@16: return contains(closed_to_u, v); Chris@16: } Chris@16: Chris@16: //! @internal Close a vertex `v` to a vertex `u`. Chris@16: void close_to(Vertex u, Vertex v) { Chris@16: BOOST_ASSERT(!is_closed_to(u, v)); Chris@16: closed_[index_of(u)].push_back(v); Chris@16: } Chris@16: Chris@16: Chris@16: //! @internal Return whether a given vertex is blocked. Chris@16: bool is_blocked(Vertex v) const { Chris@16: return get(blocked_, v) == blocked_true_color(); Chris@16: } Chris@16: Chris@16: //! @internal Block a given vertex. Chris@16: void block(Vertex v) { Chris@16: put(blocked_, v, blocked_true_color()); Chris@16: } Chris@16: Chris@16: //! @internal Unblock a given vertex. Chris@16: void unblock(Vertex u) { Chris@16: typedef typename ClosedMatrix::reference VertexList; Chris@16: Chris@16: put(blocked_, u, blocked_false_color()); Chris@16: VertexList closed_to_u = closed_[index_of(u)]; Chris@16: Chris@16: while (!closed_to_u.empty()) { Chris@16: Vertex const w = closed_to_u.back(); Chris@16: closed_to_u.pop_back(); Chris@16: if (is_blocked(w)) Chris@16: unblock(w); Chris@16: } Chris@16: BOOST_ASSERT(closed_to_u.empty()); Chris@16: } Chris@16: Chris@16: //! @internal Main procedure as described in the paper. Chris@16: bool circuit(Vertex start, Vertex v) { Chris@16: bool found_circuit = false; Chris@16: stack_.push_back(v); Chris@16: block(v); Chris@16: Chris@16: // Cache some values that are used more than once in the function. Chris@16: VertexIndex const index_of_start = index_of(start); Chris@16: AdjacentVertices const adj_vertices = GetAdjacentVertices()(v, graph_); Chris@16: AdjacencyIterator const w_end = boost::end(adj_vertices); Chris@16: Chris@16: for (AdjacencyIterator w_it = boost::begin(adj_vertices); Chris@16: w_it != w_end; Chris@16: ++w_it) Chris@16: { Chris@16: Vertex const w = *w_it; Chris@16: // Since we're only looking in the subgraph induced by `start` Chris@16: // and the vertices with an index higher than `start`, we skip Chris@16: // any vertex that does not satisfy that. Chris@16: if (index_of(w) < index_of_start) Chris@16: continue; Chris@16: Chris@16: // If the last vertex is equal to `start`, we have a circuit. Chris@16: else if (w == start) { Chris@16: // const_cast to ensure the visitor does not modify the stack Chris@16: visitor_.cycle(const_cast(stack_), graph_); Chris@16: found_circuit = true; Chris@16: } Chris@16: Chris@16: // If `w` is not blocked, we continue searching further down the Chris@16: // same path for a cycle with `w` in it. Chris@16: else if (!is_blocked(w) && circuit(start, w)) Chris@16: found_circuit = true; Chris@16: } Chris@16: Chris@16: if (found_circuit) Chris@16: unblock(v); Chris@16: else Chris@16: for (AdjacencyIterator w_it = boost::begin(adj_vertices); Chris@16: w_it != w_end; Chris@16: ++w_it) Chris@16: { Chris@16: Vertex const w = *w_it; Chris@16: // Like above, we skip vertices that are not in the subgraph Chris@16: // we're considering. Chris@16: if (index_of(w) < index_of_start) Chris@16: continue; Chris@16: Chris@16: // If `v` is not closed to `w`, we make it so. Chris@16: if (!is_closed_to(w, v)) Chris@16: close_to(w, v); Chris@16: } Chris@16: Chris@16: BOOST_ASSERT(v == stack_.back()); Chris@16: stack_.pop_back(); Chris@16: return found_circuit; Chris@16: } Chris@16: Chris@16: public: Chris@16: void operator()(Vertex start) { Chris@16: circuit(start, start); Chris@16: } Chris@16: Chris@16: private: Chris@16: Graph const& graph_; Chris@16: Visitor& visitor_; Chris@16: VertexIndexMap const& vim_; Chris@16: Stack& stack_; Chris@16: ClosedMatrix& closed_; Chris@16: BlockedMap blocked_; Chris@16: }; Chris@16: Chris@16: template < Chris@16: typename GetAdjacentVertices, Chris@16: typename Graph, typename Visitor, typename VertexIndexMap Chris@16: > Chris@16: void call_hawick_circuits(Graph const& graph, Chris@16: Visitor /* by value */ visitor, Chris@16: VertexIndexMap const& vertex_index_map) { Chris@16: typedef graph_traits Traits; Chris@16: typedef typename Traits::vertex_descriptor Vertex; Chris@16: typedef typename Traits::vertices_size_type VerticesSize; Chris@16: typedef typename Traits::vertex_iterator VertexIterator; Chris@16: Chris@16: typedef std::vector Stack; Chris@16: typedef std::vector > ClosedMatrix; Chris@16: Chris@16: typedef hawick_circuits_from< Chris@16: Graph, Visitor, VertexIndexMap, Stack, ClosedMatrix, Chris@16: GetAdjacentVertices Chris@16: > SubAlgorithm; Chris@16: Chris@16: VerticesSize const n_vertices = num_vertices(graph); Chris@16: Stack stack; stack.reserve(n_vertices); Chris@16: ClosedMatrix closed(n_vertices); Chris@16: Chris@16: VertexIterator start, last; Chris@16: for (boost::tie(start, last) = vertices(graph); start != last; ++start) { Chris@16: // Note1: The sub algorithm may NOT be reused once it has been called. Chris@16: Chris@16: // Note2: We reuse the Stack and the ClosedMatrix (after clearing them) Chris@16: // in each iteration to avoid redundant destruction and construction. Chris@16: // It would be strictly equivalent to have these as member variables Chris@16: // of the sub algorithm. Chris@16: SubAlgorithm sub_algo(graph, visitor, vertex_index_map, Chris@16: stack, closed, n_vertices); Chris@16: sub_algo(*start); Chris@16: stack.clear(); Chris@16: typename ClosedMatrix::iterator row, last_row = closed.end(); Chris@16: for (row = closed.begin(); row != last_row; ++row) Chris@16: row->clear(); Chris@16: } Chris@16: } Chris@16: Chris@16: template Chris@16: void call_hawick_circuits(Graph const& graph, BOOST_FWD_REF(Visitor) visitor) { Chris@16: call_hawick_circuits( Chris@16: graph, boost::forward(visitor), get(vertex_index, graph) Chris@16: ); Chris@16: } Chris@16: } // end namespace hawick_circuits_detail Chris@16: Chris@16: //! Enumerate all the elementary circuits in a directed multigraph. Chris@16: template Chris@16: void hawick_circuits(BOOST_FWD_REF(Graph) graph, Chris@16: BOOST_FWD_REF(Visitor) visitor, Chris@16: BOOST_FWD_REF(VertexIndexMap) vertex_index_map) { Chris@16: hawick_circuits_detail::call_hawick_circuits< Chris@16: hawick_circuits_detail::get_all_adjacent_vertices Chris@16: >( Chris@16: boost::forward(graph), Chris@16: boost::forward(visitor), Chris@16: boost::forward(vertex_index_map) Chris@16: ); Chris@16: } Chris@16: Chris@16: template Chris@16: void hawick_circuits(BOOST_FWD_REF(Graph) graph, Chris@16: BOOST_FWD_REF(Visitor) visitor) { Chris@16: hawick_circuits_detail::call_hawick_circuits< Chris@16: hawick_circuits_detail::get_all_adjacent_vertices Chris@16: >(boost::forward(graph), boost::forward(visitor)); Chris@16: } Chris@16: Chris@16: /*! Chris@16: * Same as `boost::hawick_circuits`, but duplicate circuits caused by parallel Chris@16: * edges will not be considered. Each circuit will be considered only once. Chris@16: */ Chris@16: template Chris@16: void hawick_unique_circuits(BOOST_FWD_REF(Graph) graph, Chris@16: BOOST_FWD_REF(Visitor) visitor, Chris@16: BOOST_FWD_REF(VertexIndexMap) vertex_index_map) { Chris@16: hawick_circuits_detail::call_hawick_circuits< Chris@16: hawick_circuits_detail::get_unique_adjacent_vertices Chris@16: >( Chris@16: boost::forward(graph), Chris@16: boost::forward(visitor), Chris@16: boost::forward(vertex_index_map) Chris@16: ); Chris@16: } Chris@16: Chris@16: template Chris@16: void hawick_unique_circuits(BOOST_FWD_REF(Graph) graph, Chris@16: BOOST_FWD_REF(Visitor) visitor) { Chris@16: hawick_circuits_detail::call_hawick_circuits< Chris@16: hawick_circuits_detail::get_unique_adjacent_vertices Chris@16: >(boost::forward(graph), boost::forward(visitor)); Chris@16: } Chris@16: } // end namespace boost Chris@16: Chris@16: #endif // !BOOST_GRAPH_HAWICK_CIRCUITS_HPP