annotate DEPENDENCIES/generic/include/boost/graph/tiernan_all_cycles.hpp @ 118:770eb830ec19 emscripten

Typo fix
author Chris Cannam
date Wed, 18 May 2016 16:14:08 +0100
parents 2665513ce2d3
children
rev   line source
Chris@16 1 // (C) Copyright 2007-2009 Andrew Sutton
Chris@16 2 //
Chris@16 3 // Use, modification and distribution are subject to the
Chris@16 4 // Boost Software License, Version 1.0 (See accompanying file
Chris@16 5 // LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
Chris@16 6
Chris@16 7 #ifndef BOOST_GRAPH_CYCLE_HPP
Chris@16 8 #define BOOST_GRAPH_CYCLE_HPP
Chris@16 9
Chris@16 10 #include <vector>
Chris@16 11
Chris@16 12 #include <boost/config.hpp>
Chris@16 13 #include <boost/graph/graph_concepts.hpp>
Chris@16 14 #include <boost/graph/graph_traits.hpp>
Chris@16 15 #include <boost/graph/properties.hpp>
Chris@16 16 #include <boost/concept/assert.hpp>
Chris@16 17
Chris@16 18 #include <boost/concept/detail/concept_def.hpp>
Chris@16 19 namespace boost {
Chris@16 20 namespace concepts {
Chris@16 21 BOOST_concept(CycleVisitor,(Visitor)(Path)(Graph))
Chris@16 22 {
Chris@16 23 BOOST_CONCEPT_USAGE(CycleVisitor)
Chris@16 24 {
Chris@16 25 vis.cycle(p, g);
Chris@16 26 }
Chris@16 27 private:
Chris@16 28 Visitor vis;
Chris@16 29 Graph g;
Chris@16 30 Path p;
Chris@16 31 };
Chris@16 32 } /* namespace concepts */
Chris@16 33 using concepts::CycleVisitorConcept;
Chris@16 34 } /* namespace boost */
Chris@16 35 #include <boost/concept/detail/concept_undef.hpp>
Chris@16 36
Chris@16 37
Chris@16 38 namespace boost
Chris@16 39 {
Chris@16 40
Chris@16 41 // The implementation of this algorithm is a reproduction of the Teirnan
Chris@16 42 // approach for directed graphs: bibtex follows
Chris@16 43 //
Chris@16 44 // @article{362819,
Chris@16 45 // author = {James C. Tiernan},
Chris@16 46 // title = {An efficient search algorithm to find the elementary circuits of a graph},
Chris@16 47 // journal = {Commun. ACM},
Chris@16 48 // volume = {13},
Chris@16 49 // number = {12},
Chris@16 50 // year = {1970},
Chris@16 51 // issn = {0001-0782},
Chris@16 52 // pages = {722--726},
Chris@16 53 // doi = {http://doi.acm.org/10.1145/362814.362819},
Chris@16 54 // publisher = {ACM Press},
Chris@16 55 // address = {New York, NY, USA},
Chris@16 56 // }
Chris@16 57 //
Chris@16 58 // It should be pointed out that the author does not provide a complete analysis for
Chris@16 59 // either time or space. This is in part, due to the fact that it's a fairly input
Chris@16 60 // sensitive problem related to the density and construction of the graph, not just
Chris@16 61 // its size.
Chris@16 62 //
Chris@16 63 // I've also taken some liberties with the interpretation of the algorithm - I've
Chris@16 64 // basically modernized it to use real data structures (no more arrays and matrices).
Chris@16 65 // Oh... and there's explicit control structures - not just gotos.
Chris@16 66 //
Chris@16 67 // The problem is definitely NP-complete, an unbounded implementation of this
Chris@16 68 // will probably run for quite a while on a large graph. The conclusions
Chris@16 69 // of this paper also reference a Paton algorithm for undirected graphs as being
Chris@16 70 // much more efficient (apparently based on spanning trees). Although not implemented,
Chris@16 71 // it can be found here:
Chris@16 72 //
Chris@16 73 // @article{363232,
Chris@16 74 // author = {Keith Paton},
Chris@16 75 // title = {An algorithm for finding a fundamental set of cycles of a graph},
Chris@16 76 // journal = {Commun. ACM},
Chris@16 77 // volume = {12},
Chris@16 78 // number = {9},
Chris@16 79 // year = {1969},
Chris@16 80 // issn = {0001-0782},
Chris@16 81 // pages = {514--518},
Chris@16 82 // doi = {http://doi.acm.org/10.1145/363219.363232},
Chris@16 83 // publisher = {ACM Press},
Chris@16 84 // address = {New York, NY, USA},
Chris@16 85 // }
Chris@16 86
Chris@16 87 /**
Chris@16 88 * The default cycle visitor provides an empty visit function for cycle
Chris@16 89 * visitors.
Chris@16 90 */
Chris@16 91 struct cycle_visitor
Chris@16 92 {
Chris@16 93 template <typename Path, typename Graph>
Chris@16 94 inline void cycle(const Path& p, const Graph& g)
Chris@16 95 { }
Chris@16 96 };
Chris@16 97
Chris@16 98 /**
Chris@16 99 * The min_max_cycle_visitor simultaneously records the minimum and maximum
Chris@16 100 * cycles in a graph.
Chris@16 101 */
Chris@16 102 struct min_max_cycle_visitor
Chris@16 103 {
Chris@16 104 min_max_cycle_visitor(std::size_t& min_, std::size_t& max_)
Chris@16 105 : minimum(min_), maximum(max_)
Chris@16 106 { }
Chris@16 107
Chris@16 108 template <typename Path, typename Graph>
Chris@16 109 inline void cycle(const Path& p, const Graph& g)
Chris@16 110 {
Chris@16 111 BOOST_USING_STD_MIN();
Chris@16 112 BOOST_USING_STD_MAX();
Chris@16 113 std::size_t len = p.size();
Chris@16 114 minimum = min BOOST_PREVENT_MACRO_SUBSTITUTION (minimum, len);
Chris@16 115 maximum = max BOOST_PREVENT_MACRO_SUBSTITUTION (maximum, len);
Chris@16 116 }
Chris@16 117 std::size_t& minimum;
Chris@16 118 std::size_t& maximum;
Chris@16 119 };
Chris@16 120
Chris@16 121 inline min_max_cycle_visitor
Chris@16 122 find_min_max_cycle(std::size_t& min_, std::size_t& max_)
Chris@16 123 { return min_max_cycle_visitor(min_, max_); }
Chris@16 124
Chris@16 125 namespace detail
Chris@16 126 {
Chris@16 127 template <typename Graph, typename Path>
Chris@16 128 inline bool
Chris@16 129 is_vertex_in_path(const Graph&,
Chris@16 130 typename graph_traits<Graph>::vertex_descriptor v,
Chris@16 131 const Path& p)
Chris@16 132 {
Chris@16 133 return (std::find(p.begin(), p.end(), v) != p.end());
Chris@16 134 }
Chris@16 135
Chris@16 136 template <typename Graph, typename ClosedMatrix>
Chris@16 137 inline bool
Chris@16 138 is_path_closed(const Graph& g,
Chris@16 139 typename graph_traits<Graph>::vertex_descriptor u,
Chris@16 140 typename graph_traits<Graph>::vertex_descriptor v,
Chris@16 141 const ClosedMatrix& closed)
Chris@16 142 {
Chris@16 143 // the path from u to v is closed if v can be found in the list
Chris@16 144 // of closed vertices associated with u.
Chris@16 145 typedef typename ClosedMatrix::const_reference Row;
Chris@16 146 Row r = closed[get(vertex_index, g, u)];
Chris@16 147 if(find(r.begin(), r.end(), v) != r.end()) {
Chris@16 148 return true;
Chris@16 149 }
Chris@16 150 return false;
Chris@16 151 }
Chris@16 152
Chris@16 153 template <typename Graph, typename Path, typename ClosedMatrix>
Chris@16 154 inline bool
Chris@16 155 can_extend_path(const Graph& g,
Chris@16 156 typename graph_traits<Graph>::edge_descriptor e,
Chris@16 157 const Path& p,
Chris@16 158 const ClosedMatrix& m)
Chris@16 159 {
Chris@16 160 BOOST_CONCEPT_ASSERT(( IncidenceGraphConcept<Graph> ));
Chris@16 161 BOOST_CONCEPT_ASSERT(( VertexIndexGraphConcept<Graph> ));
Chris@16 162 typedef typename graph_traits<Graph>::vertex_descriptor Vertex;
Chris@16 163
Chris@16 164 // get the vertices in question
Chris@16 165 Vertex
Chris@16 166 u = source(e, g),
Chris@16 167 v = target(e, g);
Chris@16 168
Chris@16 169 // conditions for allowing a traversal along this edge are:
Chris@16 170 // 1. the index of v must be greater than that at which the
Chris@16 171 // path is rooted (p.front()).
Chris@16 172 // 2. the vertex v cannot already be in the path
Chris@16 173 // 3. the vertex v cannot be closed to the vertex u
Chris@16 174
Chris@16 175 bool indices = get(vertex_index, g, p.front()) < get(vertex_index, g, v);
Chris@16 176 bool path = !is_vertex_in_path(g, v, p);
Chris@16 177 bool closed = !is_path_closed(g, u, v, m);
Chris@16 178 return indices && path && closed;
Chris@16 179 }
Chris@16 180
Chris@16 181 template <typename Graph, typename Path>
Chris@16 182 inline bool
Chris@16 183 can_wrap_path(const Graph& g, const Path& p)
Chris@16 184 {
Chris@16 185 BOOST_CONCEPT_ASSERT(( IncidenceGraphConcept<Graph> ));
Chris@16 186 typedef typename graph_traits<Graph>::vertex_descriptor Vertex;
Chris@16 187 typedef typename graph_traits<Graph>::out_edge_iterator OutIterator;
Chris@16 188
Chris@16 189 // iterate over the out-edges of the back, looking for the
Chris@16 190 // front of the path. also, we can't travel along the same
Chris@16 191 // edge that we did on the way here, but we don't quite have the
Chris@16 192 // stringent requirements that we do in can_extend_path().
Chris@16 193 Vertex
Chris@16 194 u = p.back(),
Chris@16 195 v = p.front();
Chris@16 196 OutIterator i, end;
Chris@16 197 for(boost::tie(i, end) = out_edges(u, g); i != end; ++i) {
Chris@16 198 if((target(*i, g) == v)) {
Chris@16 199 return true;
Chris@16 200 }
Chris@16 201 }
Chris@16 202 return false;
Chris@16 203 }
Chris@16 204
Chris@16 205 template <typename Graph,
Chris@16 206 typename Path,
Chris@16 207 typename ClosedMatrix>
Chris@16 208 inline typename graph_traits<Graph>::vertex_descriptor
Chris@16 209 extend_path(const Graph& g,
Chris@16 210 Path& p,
Chris@16 211 ClosedMatrix& closed)
Chris@16 212 {
Chris@16 213 BOOST_CONCEPT_ASSERT(( IncidenceGraphConcept<Graph> ));
Chris@16 214 typedef typename graph_traits<Graph>::vertex_descriptor Vertex;
Chris@16 215 typedef typename graph_traits<Graph>::out_edge_iterator OutIterator;
Chris@16 216
Chris@16 217 // get the current vertex
Chris@16 218 Vertex u = p.back();
Chris@16 219 Vertex ret = graph_traits<Graph>::null_vertex();
Chris@16 220
Chris@16 221 // AdjacencyIterator i, end;
Chris@16 222 OutIterator i, end;
Chris@16 223 for(boost::tie(i, end) = out_edges(u, g); i != end; ++i) {
Chris@16 224 Vertex v = target(*i, g);
Chris@16 225
Chris@16 226 // if we can actually extend along this edge,
Chris@16 227 // then that's what we want to do
Chris@16 228 if(can_extend_path(g, *i, p, closed)) {
Chris@16 229 p.push_back(v); // add the vertex to the path
Chris@16 230 ret = v;
Chris@16 231 break;
Chris@16 232 }
Chris@16 233 }
Chris@16 234 return ret;
Chris@16 235 }
Chris@16 236
Chris@16 237 template <typename Graph, typename Path, typename ClosedMatrix>
Chris@16 238 inline bool
Chris@16 239 exhaust_paths(const Graph& g, Path& p, ClosedMatrix& closed)
Chris@16 240 {
Chris@16 241 BOOST_CONCEPT_ASSERT(( GraphConcept<Graph> ));
Chris@16 242 typedef typename graph_traits<Graph>::vertex_descriptor Vertex;
Chris@16 243
Chris@16 244 // if there's more than one vertex in the path, this closes
Chris@16 245 // of some possible routes and returns true. otherwise, if there's
Chris@16 246 // only one vertex left, the vertex has been used up
Chris@16 247 if(p.size() > 1) {
Chris@16 248 // get the last and second to last vertices, popping the last
Chris@16 249 // vertex off the path
Chris@16 250 Vertex last, prev;
Chris@16 251 last = p.back();
Chris@16 252 p.pop_back();
Chris@16 253 prev = p.back();
Chris@16 254
Chris@16 255 // reset the closure for the last vertex of the path and
Chris@16 256 // indicate that the last vertex in p is now closed to
Chris@16 257 // the next-to-last vertex in p
Chris@16 258 closed[get(vertex_index, g, last)].clear();
Chris@16 259 closed[get(vertex_index, g, prev)].push_back(last);
Chris@16 260 return true;
Chris@16 261 }
Chris@16 262 else {
Chris@16 263 return false;
Chris@16 264 }
Chris@16 265 }
Chris@16 266
Chris@16 267 template <typename Graph, typename Visitor>
Chris@16 268 inline void
Chris@16 269 all_cycles_from_vertex(const Graph& g,
Chris@16 270 typename graph_traits<Graph>::vertex_descriptor v,
Chris@16 271 Visitor vis,
Chris@16 272 std::size_t minlen,
Chris@16 273 std::size_t maxlen)
Chris@16 274 {
Chris@16 275 BOOST_CONCEPT_ASSERT(( VertexListGraphConcept<Graph> ));
Chris@16 276 typedef typename graph_traits<Graph>::vertex_descriptor Vertex;
Chris@16 277 typedef std::vector<Vertex> Path;
Chris@16 278 BOOST_CONCEPT_ASSERT(( CycleVisitorConcept<Visitor,Path,Graph> ));
Chris@16 279 typedef std::vector<Vertex> VertexList;
Chris@16 280 typedef std::vector<VertexList> ClosedMatrix;
Chris@16 281
Chris@16 282 Path p;
Chris@16 283 ClosedMatrix closed(num_vertices(g), VertexList());
Chris@16 284 Vertex null = graph_traits<Graph>::null_vertex();
Chris@16 285
Chris@16 286 // each path investigation starts at the ith vertex
Chris@16 287 p.push_back(v);
Chris@16 288
Chris@16 289 while(1) {
Chris@16 290 // extend the path until we've reached the end or the
Chris@16 291 // maxlen-sized cycle
Chris@16 292 Vertex j = null;
Chris@16 293 while(((j = detail::extend_path(g, p, closed)) != null)
Chris@16 294 && (p.size() < maxlen))
Chris@16 295 ; // empty loop
Chris@16 296
Chris@16 297 // if we're done extending the path and there's an edge
Chris@16 298 // connecting the back to the front, then we should have
Chris@16 299 // a cycle.
Chris@16 300 if(detail::can_wrap_path(g, p) && p.size() >= minlen) {
Chris@16 301 vis.cycle(p, g);
Chris@16 302 }
Chris@16 303
Chris@16 304 if(!detail::exhaust_paths(g, p, closed)) {
Chris@16 305 break;
Chris@16 306 }
Chris@16 307 }
Chris@16 308 }
Chris@16 309
Chris@16 310 // Select the minimum allowable length of a cycle based on the directedness
Chris@16 311 // of the graph - 2 for directed, 3 for undirected.
Chris@16 312 template <typename D> struct min_cycles { enum { value = 2 }; };
Chris@16 313 template <> struct min_cycles<undirected_tag> { enum { value = 3 }; };
Chris@16 314 } /* namespace detail */
Chris@16 315
Chris@16 316 template <typename Graph, typename Visitor>
Chris@16 317 inline void
Chris@16 318 tiernan_all_cycles(const Graph& g,
Chris@16 319 Visitor vis,
Chris@16 320 std::size_t minlen,
Chris@16 321 std::size_t maxlen)
Chris@16 322 {
Chris@16 323 BOOST_CONCEPT_ASSERT(( VertexListGraphConcept<Graph> ));
Chris@16 324 typedef typename graph_traits<Graph>::vertex_iterator VertexIterator;
Chris@16 325
Chris@16 326 VertexIterator i, end;
Chris@16 327 for(boost::tie(i, end) = vertices(g); i != end; ++i) {
Chris@16 328 detail::all_cycles_from_vertex(g, *i, vis, minlen, maxlen);
Chris@16 329 }
Chris@16 330 }
Chris@16 331
Chris@16 332 template <typename Graph, typename Visitor>
Chris@16 333 inline void
Chris@16 334 tiernan_all_cycles(const Graph& g, Visitor vis, std::size_t maxlen)
Chris@16 335 {
Chris@16 336 typedef typename graph_traits<Graph>::directed_category Dir;
Chris@16 337 tiernan_all_cycles(g, vis, detail::min_cycles<Dir>::value, maxlen);
Chris@16 338 }
Chris@16 339
Chris@16 340 template <typename Graph, typename Visitor>
Chris@16 341 inline void
Chris@16 342 tiernan_all_cycles(const Graph& g, Visitor vis)
Chris@16 343 {
Chris@16 344 typedef typename graph_traits<Graph>::directed_category Dir;
Chris@16 345 tiernan_all_cycles(g, vis, detail::min_cycles<Dir>::value,
Chris@16 346 (std::numeric_limits<std::size_t>::max)());
Chris@16 347 }
Chris@16 348
Chris@16 349 template <typename Graph>
Chris@16 350 inline std::pair<std::size_t, std::size_t>
Chris@16 351 tiernan_girth_and_circumference(const Graph& g)
Chris@16 352 {
Chris@16 353 std::size_t
Chris@16 354 min_ = (std::numeric_limits<std::size_t>::max)(),
Chris@16 355 max_ = 0;
Chris@16 356 tiernan_all_cycles(g, find_min_max_cycle(min_, max_));
Chris@16 357
Chris@16 358 // if this is the case, the graph is acyclic...
Chris@16 359 if(max_ == 0) max_ = min_;
Chris@16 360
Chris@16 361 return std::make_pair(min_, max_);
Chris@16 362 }
Chris@16 363
Chris@16 364 template <typename Graph>
Chris@16 365 inline std::size_t
Chris@16 366 tiernan_girth(const Graph& g)
Chris@16 367 { return tiernan_girth_and_circumference(g).first; }
Chris@16 368
Chris@16 369 template <typename Graph>
Chris@16 370 inline std::size_t
Chris@16 371 tiernan_circumference(const Graph& g)
Chris@16 372 { return tiernan_girth_and_circumference(g).second; }
Chris@16 373
Chris@16 374 } /* namespace boost */
Chris@16 375
Chris@16 376 #endif