Boost logo

Boost-Commit :

Subject: [Boost-commit] svn:boost r85533 - in trunk: boost/graph libs/graph/doc libs/graph/example libs/graph/test
From: jewillco_at_[hidden]
Date: 2013-08-31 15:29:22


Author: jewillco
Date: 2013-08-31 15:29:22 EDT (Sat, 31 Aug 2013)
New Revision: 85533
URL: http://svn.boost.org/trac/boost/changeset/85533

Log:
Added Hawick circuits code from Louis Dionne; fixes #8433

Added:
   trunk/boost/graph/hawick_circuits.hpp (contents, props changed)
   trunk/libs/graph/doc/hawick_circuits.html (contents, props changed)
   trunk/libs/graph/doc/hawick_circuits.md (contents, props changed)
   trunk/libs/graph/example/hawick_circuits.cpp (contents, props changed)
   trunk/libs/graph/test/cycle_test.hpp (contents, props changed)
   trunk/libs/graph/test/hawick_circuits.cpp (contents, props changed)
Text files modified:
   trunk/boost/graph/hawick_circuits.hpp | 381 ++++++++++++++++++++++++++++++++++++++++
   trunk/libs/graph/doc/hawick_circuits.html | 58 ++++++
   trunk/libs/graph/doc/hawick_circuits.md | 53 +++++
   trunk/libs/graph/doc/table_of_contents.html | 9
   trunk/libs/graph/example/Jamfile.v2 | 1
   trunk/libs/graph/example/hawick_circuits.cpp | 96 ++++++++++
   trunk/libs/graph/test/Jamfile.v2 | 1
   trunk/libs/graph/test/cycle_test.hpp | 80 ++++++++
   trunk/libs/graph/test/hawick_circuits.cpp | 32 +++
   9 files changed, 707 insertions(+), 4 deletions(-)

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

Added: trunk/libs/graph/doc/hawick_circuits.html
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ trunk/libs/graph/doc/hawick_circuits.html 2013-08-31 15:29:22 EDT (Sat, 31 Aug 2013) (r85533)
@@ -0,0 +1,58 @@
+<p><body bgcolor="#ffffff" link="#0000ee" text="#000000" vlink="#551a8b" alink="#ff0000"></p>
+
+<p><img src="../../../boost.png" alt="C++ Boost" /></p>
+
+<h1 id="hawick_circuits"><code>hawick_circuits</code></h1>
+
+<pre><code>template &lt;typename Graph, typename Visitor, typename VertexIndexMap&gt;
+void hawick_circuits(Graph const&amp; graph, Visitor visitor, VertexIndexMap const&amp; vim = get(vertex_index, graph));
+
+template &lt;typename Graph, typename Visitor, typename VertexIndexMap&gt;
+void hawick_unique_circuits(Graph const&amp; graph, Visitor visitor, VertexIndexMap const&amp; vim = get(vertex_index, graph));
+</code></pre>
+
+<p>Enumerate all the elementary circuits in a directed multigraph. Specifically,
+self-loops and redundant circuits caused by parallel edges are enumerated too.
+<code>hawick_unique_circuits</code> may be used if redundant circuits caused by parallel
+edges are not desired.</p>
+
+<p>The algorithm is described in detail in
+http://www.massey.ac.nz/~kahawick/cstn/013/cstn-013.pdf.</p>
+
+<h3 id="where-defined">Where defined</h3>
+
+<p>#include <boost/graph/hawick_circuits.hpp></p>
+
+<h3 id="parameters">Parameters</h3>
+
+<p><strong>IN:</strong> <code>Graph const&amp; graph</code></p>
+
+<blockquote>
+ <p>The graph on which the algorithm is to be performed. It must be a model of
+ the <code>VertexListGraph</code> and <code>AdjacencyGraph</code> concepts.</p>
+</blockquote>
+
+<p><strong>IN:</strong> <code>Visitor visitor</code></p>
+
+<blockquote>
+ <p>The visitor that will be notified on each circuit found by the algorithm.
+ The <code>visitor.cycle(circuit, graph)</code> expression must be valid, with <code>circuit</code>
+ being a <code>const</code>-reference to a random access sequence of <code>vertex_descriptor</code>s.</p>
+
+ <p>For example, if a circuit <code>u -&gt; v -&gt; w -&gt; u</code> exists in the graph, the
+ visitor will be called with a sequence consisting of <code>(u, v, w)</code>.</p>
+</blockquote>
+
+<p><strong>IN:</strong> <code>VertexIndexMap const&amp; vim = get(vertex_index, graph)</code></p>
+
+<blockquote>
+ <p>A model of the <code>ReadablePropertyMap</code> concept mapping each <code>vertex_descriptor</code>
+ to an integer in the range <code>[0, num_vertices(graph))</code>. It defaults to using
+ the vertex index map provided by the <code>graph</code>.</p>
+</blockquote>
+
+<hr />
+
+<div class="footer">
+ &copy; 2013 Louis Dionne
+</div>

Added: trunk/libs/graph/doc/hawick_circuits.md
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ trunk/libs/graph/doc/hawick_circuits.md 2013-08-31 15:29:22 EDT (Sat, 31 Aug 2013) (r85533)
@@ -0,0 +1,53 @@
+<body bgcolor="#ffffff" link="#0000ee" text="#000000" vlink="#551a8b" alink="#ff0000">
+
+![C++ Boost](../../../boost.png)
+
+# `hawick_circuits`
+
+ template <typename Graph, typename Visitor, typename VertexIndexMap>
+ void hawick_circuits(Graph const& graph, Visitor visitor, VertexIndexMap const& vim = get(vertex_index, graph));
+
+ template <typename Graph, typename Visitor, typename VertexIndexMap>
+ void hawick_unique_circuits(Graph const& graph, Visitor visitor, VertexIndexMap const& vim = get(vertex_index, graph));
+
+Enumerate all the elementary circuits in a directed multigraph. Specifically,
+self-loops and redundant circuits caused by parallel edges are enumerated too.
+`hawick_unique_circuits` may be used if redundant circuits caused by parallel
+edges are not desired.
+
+The algorithm is described in detail in
+<http://www.massey.ac.nz/~kahawick/cstn/013/cstn-013.pdf>.
+
+
+### Where defined
+
+[`#include <boost/graph/hawick_circuits.hpp>`](../../../boost/graph/hawick_circuits.hpp)
+
+
+### Parameters
+
+__IN:__ `Graph const& graph`
+
+> The graph on which the algorithm is to be performed. It must be a model of
+> the `VertexListGraph` and `AdjacencyGraph` concepts.
+
+__IN:__ `Visitor visitor`
+
+> The visitor that will be notified on each circuit found by the algorithm.
+> The `visitor.cycle(circuit, graph)` expression must be valid, with `circuit`
+> being a `const`-reference to a random access sequence of `vertex_descriptor`s.
+>
+> For example, if a circuit `u -> v -> w -> u` exists in the graph, the
+> visitor will be called with a sequence consisting of `(u, v, w)`.
+
+__IN:__ `VertexIndexMap const& vim = get(vertex_index, graph)`
+
+> A model of the `ReadablePropertyMap` concept mapping each `vertex_descriptor`
+> to an integer in the range `[0, num_vertices(graph))`. It defaults to using
+> the vertex index map provided by the `graph`.
+
+
+------------------------------------------------------------------------------
+<div class="footer">
+ &copy; 2013 Louis Dionne
+</div>

Modified: trunk/libs/graph/doc/table_of_contents.html
==============================================================================
--- trunk/libs/graph/doc/table_of_contents.html Sat Aug 31 07:44:25 2013 (r85532)
+++ trunk/libs/graph/doc/table_of_contents.html 2013-08-31 15:29:22 EDT (Sat, 31 Aug 2013) (r85533)
@@ -284,10 +284,11 @@
               <li>Miscellaneous Algorithms
                   <ol>
                       <li>metric_tsp_approx</li>
- <LI>sequential_vertex_coloring
- <LI>is_bipartite (including two-coloring of bipartite graphs)
- <LI>find_odd_cycle
- <LI>maximum_adjacency_search
+ <LI>sequential_vertex_coloring</li>
+ <LI>is_bipartite (including two-coloring of bipartite graphs)</li>
+ <LI>find_odd_cycle</li>
+ <LI>maximum_adjacency_search</li>
+ <LI>hawick_circuits (find all circuits of a directed graph)</li>
                   </ol>
               </li>
 

Modified: trunk/libs/graph/example/Jamfile.v2
==============================================================================
--- trunk/libs/graph/example/Jamfile.v2 Sat Aug 31 07:44:25 2013 (r85532)
+++ trunk/libs/graph/example/Jamfile.v2 2013-08-31 15:29:22 EDT (Sat, 31 Aug 2013) (r85533)
@@ -50,4 +50,5 @@
 exe vf2_sub_graph_iso_example : vf2_sub_graph_iso_example.cpp ;
 exe vf2_sub_graph_iso_multi_example : vf2_sub_graph_iso_multi_example.cpp ;
 exe sloan_ordering : sloan_ordering.cpp ;
+exe hawick_circuits : hawick_circuits.cpp ;
 

Added: trunk/libs/graph/example/hawick_circuits.cpp
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ trunk/libs/graph/example/hawick_circuits.cpp 2013-08-31 15:29:22 EDT (Sat, 31 Aug 2013) (r85533)
@@ -0,0 +1,96 @@
+// Copyright Louis Dionne 2013
+
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy
+// at http://www.boost.org/LICENSE_1_0.txt)
+
+#include <boost/assert.hpp>
+#include <boost/graph/directed_graph.hpp>
+#include <boost/graph/graph_traits.hpp>
+#include <boost/graph/hawick_circuits.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/next_prior.hpp>
+#include <boost/property_map/property_map.hpp>
+#include <cstdlib>
+#include <iostream>
+#include <iterator>
+#include <map>
+
+
+template <typename OutputStream>
+struct cycle_printer
+{
+ cycle_printer(OutputStream& stream)
+ : os(stream)
+ { }
+
+ template <typename Path, typename Graph>
+ void cycle(Path const& p, Graph const& g)
+ {
+ if (p.empty())
+ return;
+
+ // Get the property map containing the vertex indices
+ // so we can print them.
+ typedef typename boost::property_map<
+ Graph, boost::vertex_index_t
+ >::const_type IndexMap;
+
+ IndexMap indices = get(boost::vertex_index, g);
+
+ // Iterate over path printing each vertex that forms the cycle.
+ typename Path::const_iterator i, before_end = boost::prior(p.end());
+ for (i = p.begin(); i != before_end; ++i) {
+ os << get(indices, *i) << " ";
+ }
+ os << get(indices, *i) << '\n';
+ }
+ OutputStream& os;
+};
+
+
+// VertexPairIterator is an iterator over pairs of whitespace separated
+// vertices `u` and `v` representing a directed edge from `u` to `v`.
+template <typename Graph, typename VertexPairIterator>
+void build_graph(Graph& graph, unsigned int const nvertices,
+ VertexPairIterator first, VertexPairIterator last) {
+ typedef boost::graph_traits<Graph> Traits;
+ typedef typename Traits::vertex_descriptor vertex_descriptor;
+ std::map<unsigned int, vertex_descriptor> vertices;
+
+ for (unsigned int i = 0; i < nvertices; ++i)
+ vertices[i] = add_vertex(graph);
+
+ for (; first != last; ++first) {
+ unsigned int u = *first++;
+
+ BOOST_ASSERT_MSG(first != last,
+ "there is a lonely vertex at the end of the edge list");
+
+ unsigned int v = *first;
+
+ BOOST_ASSERT_MSG(vertices.count(u) == 1 && vertices.count(v) == 1,
+ "specified a vertex over the number of vertices in the graph");
+
+ add_edge(vertices[u], vertices[v], graph);
+ }
+ BOOST_ASSERT(num_vertices(graph) == nvertices);
+}
+
+
+int main(int argc, char const* argv[]) {
+ if (argc < 2) {
+ std::cout << "usage: " << argv[0] << " num_vertices < input\n";
+ return EXIT_FAILURE;
+ }
+
+ unsigned int num_vertices = boost::lexical_cast<unsigned int>(argv[1]);
+ std::istream_iterator<unsigned int> first_vertex(std::cin), last_vertex;
+ boost::directed_graph<> graph;
+ build_graph(graph, num_vertices, first_vertex, last_vertex);
+
+ cycle_printer<std::ostream> visitor(std::cout);
+ boost::hawick_circuits(graph, visitor);
+
+ return EXIT_SUCCESS;
+}

Modified: trunk/libs/graph/test/Jamfile.v2
==============================================================================
--- trunk/libs/graph/test/Jamfile.v2 Sat Aug 31 07:44:25 2013 (r85532)
+++ trunk/libs/graph/test/Jamfile.v2 2013-08-31 15:29:22 EDT (Sat, 31 Aug 2013) (r85533)
@@ -127,6 +127,7 @@
     [ run stoer_wagner_test.cpp ../../test/build//boost_unit_test_framework/<link>static : $(TEST_DIR) ]
     [ compile filtered_graph_properties_dijkstra.cpp ]
     [ run vf2_sub_graph_iso_test.cpp ]
+ [ run hawick_circuits.cpp ]
     ;
 
 # Run SDB tests only when -sSDB= is set.

Added: trunk/libs/graph/test/cycle_test.hpp
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ trunk/libs/graph/test/cycle_test.hpp 2013-08-31 15:29:22 EDT (Sat, 31 Aug 2013) (r85533)
@@ -0,0 +1,80 @@
+// (C) Copyright 2013 Louis Dionne
+//
+// Modified from `tiernan_all_cycles.cpp`.
+//
+// Use, modification and distribution are subject to the
+// Boost Software License, Version 1.0 (See accompanying file
+// LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef BOOST_GRAPH_TEST_CYCLE_TEST_HPP
+#define BOOST_GRAPH_TEST_CYCLE_TEST_HPP
+
+#include <boost/assert.hpp>
+#include <boost/graph/directed_graph.hpp>
+#include <boost/graph/erdos_renyi_generator.hpp>
+#include <boost/graph/graph_traits.hpp>
+#include <boost/graph/graph_utility.hpp>
+#include <boost/graph/undirected_graph.hpp>
+#include <boost/next_prior.hpp>
+#include <boost/random/linear_congruential.hpp>
+#include <cstddef>
+#include <iostream>
+
+
+namespace cycle_test_detail {
+ using namespace boost;
+
+ struct cycle_validator {
+ explicit cycle_validator(std::size_t& number_of_cycles)
+ : cycles(number_of_cycles)
+ { }
+
+ template <typename Path, typename Graph>
+ void cycle(Path const& p, Graph const& g) {
+ ++cycles;
+ // Check to make sure that each of the vertices in the path
+ // is truly connected and that the back is connected to the
+ // front - it's not validating that we find all paths, just
+ // that the paths are valid.
+ typename Path::const_iterator i, j, last = prior(p.end());
+ for (i = p.begin(); i != last; ++i) {
+ j = next(i);
+ BOOST_ASSERT(edge(*i, *j, g).second);
+ }
+ BOOST_ASSERT(edge(p.back(), p.front(), g).second);
+ }
+
+ std::size_t& cycles;
+ };
+
+ template <typename Graph, typename Algorithm>
+ void test_one(Algorithm algorithm) {
+ typedef erdos_renyi_iterator<minstd_rand, Graph> er;
+
+ // Generate random graphs with 15 vertices and 15% probability
+ // of edge connection.
+ static std::size_t const N = 20;
+ static double const P = 0.1;
+ minstd_rand rng;
+
+ Graph g(er(rng, N, P), er(), N);
+ renumber_indices(g);
+ print_edges(g, get(vertex_index, g));
+
+ std::size_t cycles = 0;
+ cycle_validator vis(cycles);
+ algorithm(g, vis);
+ std::cout << "# cycles: " << vis.cycles << "\n";
+ }
+} // end namespace cycle_test_detail
+
+template <typename Algorithm>
+void cycle_test(Algorithm const& algorithm) {
+ std::cout << "*** undirected ***\n";
+ cycle_test_detail::test_one<boost::undirected_graph<> >(algorithm);
+
+ std::cout << "*** directed ***\n";
+ cycle_test_detail::test_one<boost::directed_graph<> >(algorithm);
+}
+
+#endif // !BOOST_GRAPH_TEST_CYCLE_TEST_HPP

Added: trunk/libs/graph/test/hawick_circuits.cpp
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ trunk/libs/graph/test/hawick_circuits.cpp 2013-08-31 15:29:22 EDT (Sat, 31 Aug 2013) (r85533)
@@ -0,0 +1,32 @@
+// (C) Copyright 2013 Louis Dionne
+//
+// Use, modification and distribution are subject to the
+// Boost Software License, Version 1.0 (See accompanying file
+// LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
+
+#include "cycle_test.hpp"
+#include <boost/graph/hawick_circuits.hpp>
+#include <iostream>
+
+
+struct call_hawick_circuits {
+ template <typename Graph, typename Visitor>
+ void operator()(Graph const& g, Visitor const& v) const {
+ boost::hawick_circuits(g, v);
+ }
+};
+
+struct call_hawick_unique_circuits {
+ template <typename Graph, typename Visitor>
+ void operator()(Graph const& g, Visitor const& v) const {
+ boost::hawick_unique_circuits(g, v);
+ }
+};
+
+int main() {
+ std::cout << "---------hawick_circuits---------\n";
+ cycle_test(call_hawick_circuits());
+
+ std::cout << "\n\n---------hawick_unique_circuits---------\n";
+ cycle_test(call_hawick_unique_circuits());
+}


Boost-Commit list run by bdawes at acm.org, david.abrahams at rcn.com, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk