1 // (C) Copyright 2007-2009 Andrew Sutton
3 // Use, modification and distribution are subject to the
4 // Boost Software License, Version 1.0 (See accompanying file
5 // LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
7 #ifndef BOOST_GRAPH_CYCLE_HPP
8 #define BOOST_GRAPH_CYCLE_HPP
12 #include <boost/config.hpp>
13 #include <boost/graph/graph_concepts.hpp>
14 #include <boost/graph/graph_traits.hpp>
15 #include <boost/graph/properties.hpp>
17 #include <boost/concept/detail/concept_def.hpp>
20 BOOST_concept(CycleVisitor
,(Visitor
)(Path
)(Graph
))
22 BOOST_CONCEPT_USAGE(CycleVisitor
)
31 } /* namespace concepts */
32 using concepts::CycleVisitorConcept
;
33 } /* namespace boost */
34 #include <boost/concept/detail/concept_undef.hpp>
40 // The implementation of this algorithm is a reproduction of the Teirnan
41 // approach for directed graphs: bibtex follows
44 // author = {James C. Tiernan},
45 // title = {An efficient search algorithm to find the elementary circuits of a graph},
46 // journal = {Commun. ACM},
50 // issn = {0001-0782},
51 // pages = {722--726},
52 // doi = {http://doi.acm.org/10.1145/362814.362819},
53 // publisher = {ACM Press},
54 // address = {New York, NY, USA},
57 // It should be pointed out that the author does not provide a complete analysis for
58 // either time or space. This is in part, due to the fact that it's a fairly input
59 // sensitive problem related to the density and construction of the graph, not just
62 // I've also taken some liberties with the interpretation of the algorithm - I've
63 // basically modernized it to use real data structures (no more arrays and matrices).
64 // Oh... and there's explicit control structures - not just gotos.
66 // The problem is definitely NP-complete, an an unbounded implementation of this
67 // will probably run for quite a while on a large graph. The conclusions
68 // of this paper also reference a Paton algorithm for undirected graphs as being
69 // much more efficient (apparently based on spanning trees). Although not implemented,
70 // it can be found here:
73 // author = {Keith Paton},
74 // title = {An algorithm for finding a fundamental set of cycles of a graph},
75 // journal = {Commun. ACM},
79 // issn = {0001-0782},
80 // pages = {514--518},
81 // doi = {http://doi.acm.org/10.1145/363219.363232},
82 // publisher = {ACM Press},
83 // address = {New York, NY, USA},
87 * The default cycle visitor providse an empty visit function for cycle
92 template <typename Path
, typename Graph
>
93 inline void cycle(const Path
& p
, const Graph
& g
)
98 * The min_max_cycle_visitor simultaneously records the minimum and maximum
101 struct min_max_cycle_visitor
103 min_max_cycle_visitor(std::size_t& min_
, std::size_t& max_
)
104 : minimum(min_
), maximum(max_
)
107 template <typename Path
, typename Graph
>
108 inline void cycle(const Path
& p
, const Graph
& g
)
110 BOOST_USING_STD_MIN();
111 BOOST_USING_STD_MAX();
112 std::size_t len
= p
.size();
113 minimum
= min
BOOST_PREVENT_MACRO_SUBSTITUTION (minimum
, len
);
114 maximum
= max
BOOST_PREVENT_MACRO_SUBSTITUTION (maximum
, len
);
116 std::size_t& minimum
;
117 std::size_t& maximum
;
120 inline min_max_cycle_visitor
121 find_min_max_cycle(std::size_t& min_
, std::size_t& max_
)
122 { return min_max_cycle_visitor(min_
, max_
); }
126 template <typename Graph
, typename Path
>
128 is_vertex_in_path(const Graph
&,
129 typename graph_traits
<Graph
>::vertex_descriptor v
,
132 return (std::find(p
.begin(), p
.end(), v
) != p
.end());
135 template <typename Graph
, typename ClosedMatrix
>
137 is_path_closed(const Graph
& g
,
138 typename graph_traits
<Graph
>::vertex_descriptor u
,
139 typename graph_traits
<Graph
>::vertex_descriptor v
,
140 const ClosedMatrix
& closed
)
142 // the path from u to v is closed if v can be found in the list
143 // of closed vertices associated with u.
144 typedef typename
ClosedMatrix::const_reference Row
;
145 Row r
= closed
[get(vertex_index
, g
, u
)];
146 if(find(r
.begin(), r
.end(), v
) != r
.end()) {
152 template <typename Graph
, typename Path
, typename ClosedMatrix
>
154 can_extend_path(const Graph
& g
,
155 typename graph_traits
<Graph
>::edge_descriptor e
,
157 const ClosedMatrix
& m
)
159 function_requires
< IncidenceGraphConcept
<Graph
> >();
160 function_requires
< VertexIndexGraphConcept
<Graph
> >();
161 typedef typename graph_traits
<Graph
>::vertex_descriptor Vertex
;
163 // get the vertices in question
168 // conditions for allowing a traversal along this edge are:
169 // 1. the index of v must be greater than that at which the
170 // the path is rooted (p.front()).
171 // 2. the vertex v cannot already be in the path
172 // 3. the vertex v cannot be closed to the vertex u
174 bool indices
= get(vertex_index
, g
, p
.front()) < get(vertex_index
, g
, v
);
175 bool path
= !is_vertex_in_path(g
, v
, p
);
176 bool closed
= !is_path_closed(g
, u
, v
, m
);
177 return indices
&& path
&& closed
;
180 template <typename Graph
, typename Path
>
182 can_wrap_path(const Graph
& g
, const Path
& p
)
184 function_requires
< IncidenceGraphConcept
<Graph
> >();
185 typedef typename graph_traits
<Graph
>::vertex_descriptor Vertex
;
186 typedef typename graph_traits
<Graph
>::out_edge_iterator OutIterator
;
188 // iterate over the out-edges of the back, looking for the
189 // front of the path. also, we can't travel along the same
190 // edge that we did on the way here, but we don't quite have the
191 // stringent requirements that we do in can_extend_path().
196 for(tie(i
, end
) = out_edges(u
, g
); i
!= end
; ++i
) {
197 if((target(*i
, g
) == v
)) {
204 template <typename Graph
,
206 typename ClosedMatrix
>
207 inline typename graph_traits
<Graph
>::vertex_descriptor
208 extend_path(const Graph
& g
,
210 ClosedMatrix
& closed
)
212 function_requires
< IncidenceGraphConcept
<Graph
> >();
213 typedef typename graph_traits
<Graph
>::vertex_descriptor Vertex
;
214 typedef typename graph_traits
<Graph
>::edge_descriptor Edge
;
215 typedef typename graph_traits
<Graph
>::out_edge_iterator OutIterator
;
217 // get the current vertex
219 Vertex ret
= graph_traits
<Graph
>::null_vertex();
221 // AdjacencyIterator i, end;
223 for(tie(i
, end
) = out_edges(u
, g
); i
!= end
; ++i
) {
224 Vertex v
= target(*i
, g
);
226 // if we can actually extend along this edge,
227 // then that's what we want to do
228 if(can_extend_path(g
, *i
, p
, closed
)) {
229 p
.push_back(v
); // add the vertex to the path
237 template <typename Graph
, typename Path
, typename ClosedMatrix
>
239 exhaust_paths(const Graph
& g
, Path
& p
, ClosedMatrix
& closed
)
241 function_requires
< GraphConcept
<Graph
> >();
242 typedef typename graph_traits
<Graph
>::vertex_descriptor Vertex
;
244 // if there's more than one vertex in the path, this closes
245 // of some possible routes and returns true. otherwise, if there's
246 // only one vertex left, the vertex has been used up
248 // get the last and second to last vertices, popping the last
249 // vertex off the path
255 // reset the closure for the last vertex of the path and
256 // indicate that the last vertex in p is now closed to
257 // the next-to-last vertex in p
258 closed
[get(vertex_index
, g
, last
)].clear();
259 closed
[get(vertex_index
, g
, prev
)].push_back(last
);
267 template <typename Graph
, typename Visitor
>
269 all_cycles_from_vertex(const Graph
& g
,
270 typename graph_traits
<Graph
>::vertex_descriptor v
,
275 function_requires
< VertexListGraphConcept
<Graph
> >();
276 typedef typename graph_traits
<Graph
>::vertex_descriptor Vertex
;
277 typedef std::vector
<Vertex
> Path
;
278 function_requires
< CycleVisitorConcept
<Visitor
,Path
,Graph
> >();
279 typedef std::vector
<Vertex
> VertexList
;
280 typedef std::vector
<VertexList
> ClosedMatrix
;
283 ClosedMatrix
closed(num_vertices(g
), VertexList());
284 Vertex null
= graph_traits
<Graph
>::null_vertex();
286 // each path investigation starts at the ith vertex
290 // extend the path until we've reached the end or the
291 // maxlen-sized cycle
293 while(((j
= detail::extend_path(g
, p
, closed
)) != null
)
294 && (p
.size() < maxlen
))
297 // if we're done extending the path and there's an edge
298 // connecting the back to the front, then we should have
300 if(detail::can_wrap_path(g
, p
) && p
.size() >= minlen
) {
304 if(!detail::exhaust_paths(g
, p
, closed
)) {
310 // Select the minimum allowable length of a cycle based on the directedness
311 // of the graph - 2 for directed, 3 for undirected.
312 template <typename D
> struct min_cycles
{ enum { value
= 2 }; };
313 template <> struct min_cycles
<undirected_tag
> { enum { value
= 3 }; };
314 } /* namespace detail */
316 template <typename Graph
, typename Visitor
>
318 tiernan_all_cycles(const Graph
& g
,
323 function_requires
< VertexListGraphConcept
<Graph
> >();
324 typedef typename graph_traits
<Graph
>::vertex_iterator VertexIterator
;
326 VertexIterator i
, end
;
327 for(tie(i
, end
) = vertices(g
); i
!= end
; ++i
) {
328 detail::all_cycles_from_vertex(g
, *i
, vis
, minlen
, maxlen
);
332 template <typename Graph
, typename Visitor
>
334 tiernan_all_cycles(const Graph
& g
, Visitor vis
, std::size_t maxlen
)
336 typedef typename graph_traits
<Graph
>::directed_category Dir
;
337 tiernan_all_cycles(g
, vis
, detail::min_cycles
<Dir
>::value
, maxlen
);
340 template <typename Graph
, typename Visitor
>
342 tiernan_all_cycles(const Graph
& g
, Visitor vis
)
344 typedef typename graph_traits
<Graph
>::directed_category Dir
;
345 tiernan_all_cycles(g
, vis
, detail::min_cycles
<Dir
>::value
,
346 (std::numeric_limits
<std::size_t>::max
)());
349 template <typename Graph
>
350 inline std::pair
<std::size_t, std::size_t>
351 tiernan_girth_and_circumference(const Graph
& g
)
354 min_
= (std::numeric_limits
<std::size_t>::max
)(),
356 tiernan_all_cycles(g
, find_min_max_cycle(min_
, max_
));
358 // if this is the case, the graph is acyclic...
359 if(max_
== 0) max_
= min_
;
361 return std::make_pair(min_
, max_
);
364 template <typename Graph
>
366 tiernan_girth(const Graph
& g
)
367 { return tiernan_girth_and_circumference(g
).first
; }
369 template <typename Graph
>
371 tiernan_circumference(const Graph
& g
)
372 { return tiernan_girth_and_circumference(g
).second
; }
374 } /* namespace boost */