Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,8 @@ using vertex_descriptor = boost::graph_traits<Graph>::vertex_descriptor;
using weight_type = int;

// records the vertices in the order the search visits them
struct order_recorder : boost::default_mas_visitor {
std::vector<vertex_descriptor>& order;
explicit order_recorder(std::vector<vertex_descriptor>& o) : order(o) {}
struct order_recorder : boost::graph::default_mas_visitor {
std::vector<vertex_descriptor> order;
void finish_vertex(vertex_descriptor u, const Graph&) { order.push_back(u); }
};

Expand All @@ -41,13 +40,13 @@ int main() {
auto indices_map = boost::make_shared_array_property_map(boost::num_vertices(g), index_in_heap_type(-1), boost::get(boost::vertex_index, g));
max_priority_queue_type pq(distances_map, indices_map);

std::vector<vertex_descriptor> order;
order_recorder visitor(order);
order_recorder visitor;
vertex_descriptor start = *boost::vertices(g).first;

boost::graph::maximum_adjacency_search(g, weight_map, visitor, start, pq);
// std::ref lets the visitor keep its state across the copy the algorithm makes
boost::graph::maximum_adjacency_search(g, weight_map, std::ref(visitor), start, pq);

std::cout << "Visit order:";
for (vertex_descriptor v : order) std::cout << ' ' << v;
std::cout << "\nLast visited vertex (highest connectivity): " << order.back() << '\n';
for (vertex_descriptor v : visitor.order) std::cout << ' ' << v;
std::cout << "\nLast visited vertex (highest connectivity): " << visitor.order.back() << '\n';
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/maximum_adjacency_search.hpp>
#include <functional>
#include <iostream>
#include <vector>

Expand All @@ -9,9 +10,8 @@ using Graph = boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS
using vertex_descriptor = boost::graph_traits<Graph>::vertex_descriptor;

// records the vertices in the order the search visits them
struct order_recorder : boost::default_mas_visitor {
std::vector<vertex_descriptor>& order;
explicit order_recorder(std::vector<vertex_descriptor>& o) : order(o) {}
struct order_recorder : boost::graph::default_mas_visitor {
std::vector<vertex_descriptor> order;
void finish_vertex(vertex_descriptor u, const Graph&) { order.push_back(u); }
};

Expand All @@ -26,12 +26,12 @@ int main() {

auto weight_map = boost::get(&Edge::weight, g);

std::vector<vertex_descriptor> order;
order_recorder visitor(order);
order_recorder visitor;

boost::graph::maximum_adjacency_search(g, weight_map, visitor, *vertices(g).first);
// std::ref lets the visitor keep its state across the copy the algorithm makes
boost::graph::maximum_adjacency_search(g, weight_map, std::ref(visitor), *vertices(g).first);

std::cout << "Visit order:";
for (vertex_descriptor v : order) std::cout << ' ' << v;
std::cout << "\nLast visited vertex (highest connectivity): " << order.back() << '\n';
for (vertex_descriptor v : visitor.order) std::cout << ' ' << v;
std::cout << "\nLast visited vertex (highest connectivity): " << visitor.order.back() << '\n';
}
Original file line number Diff line number Diff line change
Expand Up @@ -286,17 +286,21 @@ void maximum_adjacency_search(

|===

== Throws

`bad_graph`:: If `num_vertices(g)` is less than 2.
`std::invalid_argument`:: If a max-priority queue is given as an argument and it is not empty.

== Visitor

The `maximum_adjacency_search()` function invokes user-defined actions at four
event points during the traversal. You supply these actions through a visitor
object whose type models the <<mas-visitor-concept,MAS Visitor concept>>.

Since the visitor parameter is passed by value, if the
visitor contains state then any changes to the state during the algorithm
will be made to a copy of the visitor object, not the visitor object
passed in. Therefore you may want the visitor to hold this state by
pointer or reference.
The visitor is taken by value, so the algorithm works on a copy. To keep state,
give the visitor ordinary data members and pass it with `std::ref`.
The algorithm then operates on the referenced object and its
state survives the call.

[WARNING]
====
Expand Down Expand Up @@ -444,11 +448,6 @@ struct my_mas_visitor
};
----

== Throws

`bad_graph`:: If `num_vertices(g)` is less than 2.
`std::invalid_argument`:: If a max-priority queue is given as an argument and it is not empty.

== References

* David Matula (1993). "http://dl.acm.org/citation.cfm?id=313872&dl=ACM&coll=DL&CFID=85991501&CFTOKEN=44461131[A linear time 2 + epsilon approximation algorithm for edge connectivity]"
Expand Down
26 changes: 20 additions & 6 deletions include/boost/graph/maximum_adjacency_search.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
//=======================================================================
// Copyright 2012 Fernando Vilas
// 2010 Daniel Trebbien
// 2026 Arnaud Becheler
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
Expand Down Expand Up @@ -122,6 +123,12 @@ using default_mas_visitor = mas_visitor<>;
namespace mas_detail
{

// Unwrap a visitor that may be passed with std::ref.
template < class Visitor > struct unwrap_visitor { using type = Visitor; };
template < class Visitor > struct unwrap_visitor< std::reference_wrapper< Visitor > > { using type = Visitor; };
template < class Visitor > Visitor& deref_visitor(Visitor& vis) { return vis; }
template < class Visitor > Visitor& deref_visitor(std::reference_wrapper< Visitor > vis) { return vis.get(); }

// Maximum adjacency sweep over an already populated queue.
// Shared engine behind both maximum_adjacency_search and stoer_wagner_min_cut.
// The graph may be contracted through assignments (each vertex maps to its representative)
Expand All @@ -143,16 +150,19 @@ void mas_sweep(
// reach counts are the queue keys.
auto key_map = pq.keys();

// resolve a std::ref-wrapped visitor to the referenced object
auto& vis_ref = deref_visitor(vis);

while (!pq.empty())
{
// extract max: top then pop
const auto u = pq.top();
vis.start_vertex(u, g);
vis_ref.start_vertex(u, g);
pq.pop();

for (const auto& e : make_iterator_range(out_edges(u, g)))
{
vis.examine_edge(e, g);
vis_ref.examine_edge(e, g);
// map the target to itself or its super node
const auto v = get(assignment_map, target(e, g));
// in the queue means still unvisited
Expand All @@ -173,7 +183,7 @@ void mas_sweep(

for (const auto& e : make_iterator_range(out_edges(member, g)))
{
vis.examine_edge(e, g);
vis_ref.examine_edge(e, g);
// map the target to itself or its super node
const auto v = get(assignment_map, target(e, g));
if (pq.contains(v))
Expand All @@ -183,7 +193,7 @@ void mas_sweep(
}
}
}
vis.finish_vertex(u, g);
vis_ref.finish_vertex(u, g);
}
}
} // namespace mas_detail
Expand All @@ -208,7 +218,8 @@ void maximum_adjacency_search(
BOOST_CONCEPT_ASSERT((boost::VertexListGraphConcept< Graph >));
BOOST_CONCEPT_ASSERT((boost::Convertible< directed_category, boost::undirected_tag >));
BOOST_CONCEPT_ASSERT((boost::ReadablePropertyMapConcept< WeightMap, edge_descriptor >));
boost::function_requires< MASVisitorConcept< MASVisitor, Graph > >();
using visitor_type = typename mas_detail::unwrap_visitor< MASVisitor >::type;
boost::function_requires< MASVisitorConcept< visitor_type, Graph > >();
BOOST_CONCEPT_ASSERT((boost::KeyedUpdatableQueueConcept< KeyedUpdatablePriorityQueue >));

if (num_vertices(g) < 2)
Expand All @@ -219,11 +230,14 @@ void maximum_adjacency_search(
// reach counts are the queue keys
auto key_map = pq.keys();

// resolve a std::ref-wrapped visitor to the referenced object
auto& vis_ref = mas_detail::deref_visitor(vis);

// seed every vertex with reach count 0
for (const auto& v : make_iterator_range(vertices(g)))
{
put(key_map, v, static_cast< weight_type >(0));
vis.initialize_vertex(v, g);
vis_ref.initialize_vertex(v, g);
pq.push(v);
}
BOOST_ASSERT(pq.size() >= 2);
Expand Down
32 changes: 32 additions & 0 deletions test/mas_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,37 @@ void test_exceptions()
}
}

// A stateful visitor that owns its state as a plain data member.
class counting_visitor : public boost::graph::default_mas_visitor
{
public:
std::size_t finish_count = 0;
void finish_vertex(vertex_descriptor, const undirected_graph&) { ++finish_count; }
};

// A visitor passed with std::ref keeps its state across the algorithm
void test_stateful_visitor_with_ref()
{
const undirected_graph g = mas_sw_oracle::make_weighted_graph(4, { { 0, 1, 1 }, { 1, 2, 1 }, { 2, 3, 1 } });
auto weight_map = get(boost::edge_weight, g);

// std::ref makes the algorithm operate on the caller's visitor
{
cv_maxheap_type pq = make_weighted_maxheap(g);
counting_visitor vis;
boost::graph::maximum_adjacency_search(g, weight_map, std::ref(vis), *vertices(g).first, pq);
BOOST_TEST_EQ(vis.finish_count, static_cast< std::size_t >(num_vertices(g)));
}

// by value the caller's visitor is left unchanged
{
cv_maxheap_type pq = make_weighted_maxheap(g);
counting_visitor vis;
boost::graph::maximum_adjacency_search(g, weight_map, vis, *vertices(g).first, pq);
BOOST_TEST_EQ(vis.finish_count, static_cast< std::size_t >(0));
}
}

int main()
{
test0();
Expand All @@ -537,6 +568,7 @@ int main()
test9_weights_start_vertex();
test_maxflow_crossvalidation();
test_visitor_events();
test_stateful_visitor_with_ref();
test_exceptions();
return boost::report_errors();
}
Loading