Optionally specify a maximum circuit length in Hawick circuits algorithm

This commit is contained in:
Sven Gato Redsun 2022-11-26 10:02:08 -07:00
parent c9f515347b
commit 3f75ea7aa3
3 changed files with 53 additions and 20 deletions

View File

@ -13,13 +13,13 @@
<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));
void hawick_circuits(Graph const&amp; graph, Visitor visitor, VertexIndexMap const&amp; vim = get(vertex_index, graph), unsigned int const maxLength = 0);
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));
void hawick_unique_circuits(Graph const&amp; graph, Visitor visitor, VertexIndexMap const&amp; vim = get(vertex_index, graph), unsigned int const maxLength = 0);
</code></pre>
<p>Enumerate all the elementary circuits in a directed multigraph. Specifically,
<p>Enumerate all the elementary circuits (of length &le; <code>maxLength</code>, if nonzero) 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>
@ -59,6 +59,12 @@ edges are not desired.</p>
the vertex index map provided by the <code>graph</code>.</p>
</blockquote>
<p><strong>IN:</strong> <code>unsigned int const maxLength = 0</code></p>
<blockquote>
<p>The maximum circuit length to consider. Beyond this it truncates the depth-first search, reducing the computation time by ignoring longer circuits. The default value of <code>maxLength = 0</code> implies no maximum.</p>
</blockquote>
<hr />
<div class="footer">

View File

@ -78,7 +78,7 @@ int main(int argc, char const* argv[])
{
if (argc < 2)
{
std::cout << "usage: " << argv[0] << " num_vertices < input\n";
std::cout << "usage: " << argv[0] << " <num_vertices> <max_length (optional)>\n";
return EXIT_FAILURE;
}
@ -88,7 +88,12 @@ int main(int argc, char const* argv[])
build_graph(graph, num_vertices, first_vertex, last_vertex);
cycle_printer< std::ostream > visitor(std::cout);
boost::hawick_circuits(graph, visitor);
if (argc == 2) {
boost::hawick_circuits(graph, visitor);
} else {
unsigned int max_length = boost::lexical_cast< unsigned int >(argv[2]);
boost::hawick_circuits(graph, visitor, max_length);
}
return EXIT_SUCCESS;
}

View File

@ -153,13 +153,14 @@ namespace hawick_circuits_detail
public:
hawick_circuits_from(Graph const& graph, Visitor& visitor,
VertexIndexMap const& vim, Stack& stack, ClosedMatrix& closed,
VerticesSize n_vertices)
VerticesSize n_vertices, unsigned int const maxLength)
: graph_(graph)
, visitor_(visitor)
, vim_(vim)
, stack_(stack)
, closed_(closed)
, blocked_(n_vertices, vim_)
, maxLength_(maxLength)
{
BOOST_ASSERT(blocked_map_starts_all_unblocked());
@ -224,6 +225,10 @@ namespace hawick_circuits_detail
stack_.push_back(v);
block(v);
// Truncate the search if any circuits would exceed maxLength_.
bool const truncate_search =
(maxLength_ > 0 && stack_.size() >= maxLength_);
// Cache some values that are used more than once in the function.
VertexIndex const index_of_start = index_of(start);
AdjacentVertices const adj_vertices
@ -249,13 +254,19 @@ namespace hawick_circuits_detail
found_circuit = true;
}
// If required, truncate the search before the subsequent
// recursive call to circuit().
else if (truncate_search)
continue;
// 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)
bool const finish_circuit = (found_circuit || truncate_search);
if (finish_circuit)
unblock(v);
else
for (AdjacencyIterator w_it = boost::begin(adj_vertices);
@ -274,7 +285,7 @@ namespace hawick_circuits_detail
BOOST_ASSERT(v == stack_.back());
stack_.pop_back();
return found_circuit;
return finish_circuit;
}
public:
@ -287,12 +298,14 @@ namespace hawick_circuits_detail
Stack& stack_;
ClosedMatrix& closed_;
BlockedMap blocked_;
unsigned int const maxLength_;
};
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)
Visitor /* by value */ visitor, VertexIndexMap const& vertex_index_map,
unsigned int const maxLength)
{
typedef graph_traits< Graph > Traits;
typedef typename Traits::vertex_descriptor Vertex;
@ -322,7 +335,8 @@ namespace hawick_circuits_detail
// 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);
graph, visitor, vertex_index_map, stack, closed, n_vertices,
maxLength);
sub_algo(*start);
stack.clear();
typename ClosedMatrix::iterator row, last_row = closed.end();
@ -333,30 +347,35 @@ namespace hawick_circuits_detail
template < typename GetAdjacentVertices, typename Graph, typename Visitor >
void call_hawick_circuits(
Graph const& graph, BOOST_FWD_REF(Visitor) visitor)
Graph const& graph, BOOST_FWD_REF(Visitor) visitor,
unsigned int const maxLength)
{
call_hawick_circuits< GetAdjacentVertices >(graph,
boost::forward< Visitor >(visitor), get(vertex_index, graph));
boost::forward< Visitor >(visitor), get(vertex_index, graph),
maxLength);
}
} // 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)
BOOST_FWD_REF(VertexIndexMap) vertex_index_map,
unsigned int const maxLength = 0)
{
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));
boost::forward< VertexIndexMap >(vertex_index_map), maxLength);
}
template < typename Graph, typename Visitor >
void hawick_circuits(BOOST_FWD_REF(Graph) graph, BOOST_FWD_REF(Visitor) visitor)
void hawick_circuits(BOOST_FWD_REF(Graph) graph, BOOST_FWD_REF(Visitor) visitor,
unsigned int const maxLength = 0)
{
hawick_circuits_detail::call_hawick_circuits<
hawick_circuits_detail::get_all_adjacent_vertices >(
boost::forward< Graph >(graph), boost::forward< Visitor >(visitor));
boost::forward< Graph >(graph), boost::forward< Visitor >(visitor),
maxLength);
}
/*!
@ -366,21 +385,24 @@ void hawick_circuits(BOOST_FWD_REF(Graph) graph, BOOST_FWD_REF(Visitor) visitor)
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)
BOOST_FWD_REF(VertexIndexMap) vertex_index_map,
unsigned int const maxLength = 0)
{
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));
boost::forward< VertexIndexMap >(vertex_index_map), maxLength);
}
template < typename Graph, typename Visitor >
void hawick_unique_circuits(
BOOST_FWD_REF(Graph) graph, BOOST_FWD_REF(Visitor) visitor)
BOOST_FWD_REF(Graph) graph, BOOST_FWD_REF(Visitor) visitor,
unsigned int const maxLength = 0)
{
hawick_circuits_detail::call_hawick_circuits<
hawick_circuits_detail::get_unique_adjacent_vertices >(
boost::forward< Graph >(graph), boost::forward< Visitor >(visitor));
boost::forward< Graph >(graph), boost::forward< Visitor >(visitor),
maxLength);
}
} // end namespace boost