Find distances to vertices using BFS - c++

using namespace boost;
typedef adjacency_list<setS, vecS, bidirectionalS,
no_property,
property<edge_weight_t, float>> Graph_type;
typedef graph_traits<Graph_type>::edge_iterator edge_iterator;
int main() {
// create graph
Graph_type g;
add_edge(0,1,g);
add_edge(1,2,g);
add_edge(2,0,g);
// output graph
std::pair<edge_iterator, edge_iterator> ei = edges(g);
for (edge_iterator edge_iter = ei.first; edge_iter != ei.second; ++edge_iter)
std::cout << source(*edge_iter, g) << " - " << target(*edge_iter, g) << "\n";
// attempt to find shortest distance from vetex 0 to all other vertices
breadth_first_search(g,0);
return 0;
}
I am attempting to call breadth_first_search, starting from vertex 0, and obtain the shortest distance to all other vertices in the graph. How am I calling the function incorrectly? And what container is recommended to store the result?

Modernizing the code first:
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/breadth_first_search.hpp>
#include <iostream>
using Graph_type =
boost::adjacency_list<boost::setS, boost::vecS, boost::bidirectionalS,
boost::no_property,
boost::property<boost::edge_weight_t, float>>;
int main() {
// create graph
Graph_type g;
add_edge(0, 1, g);
add_edge(1, 2, g);
add_edge(2, 0, g);
// output graph
for (auto e : boost::make_iterator_range(edges(g)))
std::cout << e << "\n";
// attempt to find shortest distance from vetex 0 to all other vertices
breadth_first_search(g, 0);
}
Clang and GCC give different diagnostics, GCC's are most elaborate:
<source>: In function 'int main()':
<source>:24:32: error: no matching function for call to 'breadth_first_search(Graph_type&, int)'
24 | boost::breadth_first_search(g, 0);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~
In file included from <source>:2:
/opt/compiler-explorer/libs/boost_1_78_0/boost/graph/breadth_first_search.hpp:123:6: note: candidate: 'template<class VertexListGraph, class SourceIterator, class Buffer, class BFSVisitor, class ColorMap> void boost::breadth_first_search(const VertexListGraph&, SourceIterator, SourceIterator, Buffer&, BFSVisitor, ColorMap)'
123 | void breadth_first_search(const VertexListGraph& g,
| ^~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/libs/boost_1_78_0/boost/graph/breadth_first_search.hpp:123:6: note: template argument deduction/substitution failed:
<source>:24:32: note: candidate expects 6 arguments, 2 provided
24 | boost::breadth_first_search(g, 0);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~
In file included from <source>:2:
/opt/compiler-explorer/libs/boost_1_78_0/boost/graph/breadth_first_search.hpp:141:6: note: candidate: 'template<class VertexListGraph, class Buffer, class BFSVisitor, class ColorMap> void boost::breadth_first_search(const VertexListGraph&, typename boost::graph_traits<Graph>::vertex_descriptor, Buffer&, BFSVisitor, ColorMap)'
141 | void breadth_first_search(const VertexListGraph& g,
| ^~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/libs/boost_1_78_0/boost/graph/breadth_first_search.hpp:141:6: note: template argument deduction/substitution failed:
<source>:24:32: note: candidate expects 5 arguments, 2 provided
24 | boost::breadth_first_search(g, 0);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~
In file included from <source>:2:
/opt/compiler-explorer/libs/boost_1_78_0/boost/graph/breadth_first_search.hpp:328:6: note: candidate: 'template<class VertexListGraph, class P, class T, class R> void boost::breadth_first_search(const VertexListGraph&, typename boost::graph_traits<Graph>::vertex_descriptor, const boost::bgl_named_params<T, Tag, Base>&)'
328 | void breadth_first_search(const VertexListGraph& g,
| ^~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/libs/boost_1_78_0/boost/graph/breadth_first_search.hpp:328:6: note: template argument deduction/substitution failed:
<source>:24:32: note: candidate expects 3 arguments, 2 provided
24 | boost::breadth_first_search(g, 0);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~
Compiler returned: 1
Indeed checking the documentation shows no overload that takes 2 arguments. You need at least a third, boost::named_parameters object
Let's stick in a stub:
breadth_first_search(g, 0, boost::no_named_parameters{});
Now it should tell us which params are missing, if any: Success!
The reason you might want to pass parameters is in case your graph doesn't have implicit property maps (like vertex_index_map) for mandatory arguments, or when you want to override the default for a UTIL/OUT, like visitor. In your case it is very likely that you want to override the it because otherwise you won't be able to access the answer to your question:
std::vector<size_t> distances(num_vertices(g));
auto recorder = record_distances(distances.data(), boost::on_tree_edge{});
breadth_first_search(g, 0, visitor(make_bfs_visitor(recorder)));
for (auto v : boost::make_iterator_range(vertices(g)))
std::cout << "distance 0 .. " << v << ": " << distances.at(v) << "\n";
Prints Live On Compiler Explorer:
(0,1)
(1,2)
(2,0)
distance 0 .. 0: 0
distance 0 .. 1: 1
distance 0 .. 2: 2
BONUS/Caveats
Caution: your graph model "has" edge_weight (although your example never initializes it to non-zero values). If they are relevant, then BFS is not enough. For non-negative weights you should use Dijkstra, which happens to be a bit easier to use in my opinion:
Live
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <iostream>
using G = boost::adjacency_list<boost::setS, boost::vecS, boost::bidirectionalS,
boost::no_property,
boost::property<boost::edge_weight_t, float>>;
using V = G::vertex_descriptor;
int main() {
// create graph
G g;
add_edge(0, 1, 30, g);
add_edge(1, 2, 43, g);
add_edge(2, 0, 5, g);
// output graph
auto wmap = get(boost::edge_weight, g);
for (auto e : boost::make_iterator_range(edges(g)))
std::cout << e << " weight " << wmap[e] << "\n";
// attempt to find shortest distance from vetex 0 to all other vertices
auto s = vertex(0, g);
assert(s == 0);
std::vector<size_t> distances(num_vertices(g));
boost::dijkstra_shortest_paths(g, s, boost::distance_map(distances.data()));
for (auto v : boost::make_iterator_range(vertices(g)))
std::cout << "distance 0 .. " << v << ": " << distances.at(v) << "\n";
}
Prints
(0,1) weight 30
(1,2) weight 43
(2,0) weight 5
distance 0 .. 0: 0
distance 0 .. 1: 30
distance 0 .. 2: 73

Related

Boost graph non contiguous vertex indices

#include <boost/graph/adjacency_list.hpp>
typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS,
boost::no_property,
boost::property<boost::edge_weight_t, double>>
DiGraph;
typedef boost::graph_traits<DiGraph>::vertex_descriptor vertex_descriptor;
int main () {
std::vector<std::size_t> vertices = { 1, 5, 10};
std::vector<std::pair<std::size_t, std::size_t>> edges = {std::make_pair(1, 5),
std::make_pair(5, 10)};
std::vector<double> weights = {2., 2.};
DiGraph di_graph (edges.begin(), edges.end(), weights.begin(), vertices.size());
DiGraph::vertex_descriptor v_start = boost::vertex(1, di_graph);
std::vector<vertex_descriptor> parents(boost::num_vertices(di_graph));
boost::dijkstra_shortest_paths(di_graph, v_start,
boost::predecessor_map(boost::make_iterator_property_map(parents.begin(), boost::get(boost::vertex_index, di_graph))));
}
This allocates a vector parents of size 11, since boost uses contiguous vertex indices.
I want the non-contiguous vertices (1, 5, 10..) but don't want the unnecessary memory space for the vector parents.
How can I make a mapping from my vertex indices to the vertex indices 1, 2, 3 and pass it to boost::dijkstra_shortest_paths?
On top of that it would be even more convenient to receive the result of dijkstra in a struct parents and access the predecessor of an element with my index, e.g.
parents[10]
but without a vector of length 11 or just have an easy conversion function f I could use
parents[f(10)]
I did take a look at the documentation of boost graph and thought the IndexMap could make this possible, but I don't understand the concept and can't make it work.
With the boost::vecS vertex container selection, the vertex index is implicit, and the call
DiGraph di_graph(
edges.begin(), edges.end(), weights.begin(), vertices.size());
is a lie: you tell it that there are 3 vertices, but then you index out of bounds (5, 10 are outside [0,1,2]).
Note also that
V v_start = boost::vertex(1, di_graph);
selects the second vertex, not vertex 1.
Internal Names
I'd probably suggest a more recent addition: internal vertex names. If we add a vertex property bundle, like simply int:
using DiGraph = boost::adjacency_list<
boost::vecS,
boost::vecS,
boost::directedS,
int,
boost::property<boost::edge_weight_t, double>>;
And then also teach BGL that we can use it as the vertex internal name:
template<> struct boost::graph::internal_vertex_name<int> {
struct type : std::identity {
using result_type = int;
};
};
Now creating the equivalent graph is simply:
DiGraph g;
add_edge(1, 5, 2., g);
add_edge(5, 10, 2., g);
That's all. You can see that it created vertices with implicit indices as the descriptors:
for (auto e : make_iterator_range(edges(g))) {
std::cout << "edge: " << e << "\n";
}
Prints:
edge: (0,2)
edge: (1,0)
To get the names, use property maps like so:
for (auto v : make_iterator_range(vertices(g))) {
std::cout << "vertex at index " << v << " named " << g[v] << "\n";
}
Printing
vertex at index 0 named 5
vertex at index 1 named 1
vertex at index 2 named 10
Using internal vertex names, you can query vertices by property bundles:
boost::optional<V> v_start = g.vertex_by_property(1);
Now, all I can suggest is using safe iterator maps:
dijkstra_shortest_paths(
g,
v_start.value(),
boost::predecessor_map(boost::make_safe_iterator_property_map(
parents.begin(), parents.size(), get(boost::vertex_index, g))));
for (size_t i = 0; i < parents.size(); ++i) {
std::cout << "Parent for '" << g[i] << "' is '" << g[parents[i]] << "'\n";
}
Live Demo
Live On Coliru
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <iostream>
template<> struct boost::graph::internal_vertex_name<int> {
struct type : std::identity {
using result_type = int;
};
};
using DiGraph = boost::adjacency_list<
boost::vecS,
boost::vecS,
boost::directedS,
int,
boost::property<boost::edge_weight_t, double>>;
using V = DiGraph::vertex_descriptor;
using boost::make_iterator_range;
int main()
{
DiGraph g;
add_edge(1, 5, 2., g);
add_edge(5, 10, 2., g);
for(auto e : make_iterator_range(edges(g)))
std::cout << "edge: " << e << "\n";
for(auto v : make_iterator_range(vertices(g)))
std::cout << "vertex at index " << v << " named " << g[v] << "\n";
boost::optional<V> v_start = g.vertex_by_property(1);
std::vector<V> parents(num_vertices(g));
dijkstra_shortest_paths(
g,
v_start.value(),
boost::predecessor_map(boost::make_safe_iterator_property_map(
parents.begin(), parents.size(), get(boost::vertex_index, g))));
for (size_t i = 0; i < parents.size(); ++i) {
std::cout << "Parent for '" << g[i] << "' is '" << g[parents[i]] << "'\n";
}
}
Prints
edge: (0,2)
edge: (1,0)
vertex at index 0 named 5
vertex at index 1 named 1
vertex at index 2 named 10
Parent for '5' is '1'
Parent for '1' is '1'
Parent for '10' is '5'
First step: take a look at bundled properties https://www.boost.org/doc/libs/1_79_0/libs/graph/doc/bundles.html
Second:
the non-contiguous vertices (1, 5, 10..) " these should be regarded as properties of the vertex. e.g "1" is a property of vertex 0.
Third: create a vertex class with 1, 5, 10.. as public attributes
Four: Create a boost graph using the your vertex class, setting and getting 1, 5, 10.. as described in the bundled properties page.

How to configure boost::graph to use my own (stable) index for vertices?

The simplest boost::adjacency_list uses std::size_t as the underlying vertex_descriptor (index).
boost::adjacency_list<
boost::vecS,
boost::vecS,
boost::directedS,
boost::no_property,
boost::no_property
> graph;
Once I know the vertex descriptor, I can quickly access the desired vertex.
auto vertex = graph[idx]; // idx is the veretx descriptor
However, when the graph is mutated, there is no guarantee that the vertex_decriptor will be stable.
auto v0 = boost::add_vertex(graph);
auto v1 = boost::add_vertex(graph);
auto v2 = boost::add_vertex(graph);
boost::remove_vertex(v0, graph); // v1 and v2 are no longer valid
I would like to be able to find a specific vertex quickly - meaning I wish to avoid traversing the graph structure in search of a vertex I know exists.
My thinking is that I can somehow tweak boost::adjacency_list with a different selector to the VertexListS template parameter, that will allow me to use my own provided vertex_descripor (index).
I explored the possibility of using an associative container selector such as the builtin boost::hash_mapS but it seems I can't control the exact id it returns when calling add_vertex.
I would like to be able to use my own id (vertex_descriptor) for each vertex.
I'll try to be a bit more clear, with the code I wish would work:
// the following type will serve as the vertex_descriptor:
using my_id_type = std::size_t;
struct MyVertex
{
my_id_type id;
// some other fields
}
// add vertices with unique identifiers that will also serve as the vertex_descriptors
boost::add_vertex(MyVertex{1111}, graph); // I expect this to return 1111
boost::add_vertex(MyVertex{2222}, graph);
boost::add_vertex(MyVertex{1234}, graph);
boost::remove_vertex(1111, graph); // I expect this to remove the first vertex
// access a specific vertex using its unique, stable descriptor:
auto vertex = graph[1234];
Can this be achieved using boost::graph?
Can this be achieved using boost::graph?
The answer is nearly always "yes" with BGL. It's one of the most profoundly generic library designs in existence.
To my surprise, something new appeared in the type-hierarchy for adjacency_list. Apparently these days there's a named_graph mixin (actually maybe_name_graph) which uses traits on the vertex bundle to detect an "internal name".
This means you can get close. Though the vertex descriptor will not become your id, you can have O(1) lookup. And the interface has some conveniences, so you can write:
add_edge(1111, 2222, g);
add_edge(2222, 1111, g);
Notes:
the lookup is amortized O(1) because the name-vertex lookup is based on an unordered (hash) index
you run into problems (e.g. ambiguous overloads for add_edge) if you make the id type accidentally the same as the vertex_descriptor type (or if your argument have equally "far" conversions to either).
as far as I can tell, the internal name property is not automatically picked up as the vertex_index_t nor vertex_name_t property.
Step #1: The Graph
struct Vertex {
size_t id;
std::string other = "default-constructed";
};
using Graph =
boost::adjacency_list<boost::vecS, boost::listS, boost::directedS, Vertex>;
So far no surprises. I
opted to add a second member other just to show when/how it gets default constructed
opted listS because it
a. offers reference/descriptor stability (except for removed vertices)
b. leads to opaque vertex_descriptor (void*) which does not conflict with the id type (size_t) in overload resolution
Step #2: Name Traits
Next we teach BGL about our Internal Vertex Name.
This is purely parameterized by Vertex bundle, so keep in mind that different graphs using the same bundle would use the same name traits.
// traits
template <> struct boost::graph::internal_vertex_name<Vertex> {
struct type {
using result_type = size_t;
result_type const& operator()(Vertex const& bundle) const {
return bundle.id;
}
};
};
template <> struct boost::graph::internal_vertex_constructor<Vertex> {
struct type {
private:
using extract_name_type = typename internal_vertex_name<Vertex>::type;
using vertex_name_type = typename remove_cv<typename remove_reference<
typename extract_name_type::result_type>::type>::type;
public:
using argument_type = vertex_name_type;
using result_type = Vertex;
result_type operator()(const vertex_name_type& id) const {
return {id};
}
};
};
Note
We could of course hard-code the knowns in the second specialization:
template <> struct boost::graph::internal_vertex_constructor<Vertex> {
struct type {
Vertex operator()(size_t id) const { return {id}; }
};
};
It is very important to return the id by reference. Failing to do so leads to UB with no diagnostics from the library/compiler
Step #3 Adding/Finding Vertices
Now you can add vertices. Either by "name" (your id):
auto x = add_vertex(1111, g); // by id
Or the old-fashioned way you anticipated in the question:
add_vertex(Vertex{2222, "twotwotwotwo"}, g); // or with full bundle
Duplicate additions have no effect:
assert(add_vertex(1111, g) == x);
Different lookups exist. The vertex_by_property returns a optional<vertex_descriptor> given a vertex bundle.
assert(x == *g.vertex_by_property(g[x]));
It does so by extracting the "internal name" from the bundle passed, so there is no need for the bundle to contain any other state outside the id:
assert(x == *g.vertex_by_property(Vertex{1111}));
Although it feels like an implementation detail, the actual multi_index_container implementing the name -> descriptor index is also exposed:
assert(x == *g.named_vertices.find(1111));
Step #4 Adding Edges
add_edge(1111, 2222, g);
add_edge(2222, 1111, g);
That borrows a page from your previous question :)
Obviously, you can still add edges by vertex descriptors.
Step #5 Other Operations
Borrowing more pages from that previous answer:
print_graph(g, get(&Vertex::id, g), std::cout << "---\n");
print_graph(g, get(&Vertex::other, g), std::cout << "---\n");
std::cout << "---\n";
for (auto *v : boost::make_iterator_range(vertices(g))) {
auto const& [id, other] = g[v];
std::cout << id << " " << std::quoted(other) << "\n";
}
if (auto v = g.vertex_by_property({1111})) {
std::cout << "==== removing " << g[*v].id << "\n";
clear_vertex(*v, g); // clear edges
remove_vertex(*v, g); // remove the vertex
}
print_graph(g, get(&Vertex::id, g), std::cout << "---\n");
print_graph(g, get(&Vertex::other, g), std::cout << "---\n");
Full Demo
Live On Coliru
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graph_utility.hpp>
#include <iostream>
#include <iomanip>
struct Vertex {
size_t id;
std::string other = "default-constructed";
};
using Graph =
boost::adjacency_list<boost::vecS, boost::listS, boost::directedS, Vertex>;
// traits
template <> struct boost::graph::internal_vertex_name<Vertex> {
struct type {
using result_type = size_t;
result_type const& operator()(Vertex const& bundle) const {
return bundle.id;
}
};
};
template <> struct boost::graph::internal_vertex_constructor<Vertex> {
struct type {
private:
using extractor = typename internal_vertex_name<Vertex>::type;
using name_t = std::decay_t<typename extractor::result_type>;
public:
using argument_type = name_t;
using result_type = Vertex;
result_type operator()(const name_t& id) const { return {id}; }
};
};
int main() {
Graph g;
{
auto x = add_vertex(1111, g); // by id
add_vertex(Vertex{2222, "twotwotwotwo"}, g); // or with full bundle
// duplicate additions have no effect
assert(add_vertex(1111, g) == x);
// different lookups exist
assert(x == *g.named_vertices.find(1111));
assert(x == *g.vertex_by_property(Vertex{1111}));
assert(x == *g.vertex_by_property(g[x]));
}
add_edge(1111, 2222, g);
add_edge(2222, 1111, g);
print_graph(g, get(&Vertex::id, g), std::cout << "---\n");
print_graph(g, get(&Vertex::other, g), std::cout << "---\n");
std::cout << "---\n";
for (auto *v : boost::make_iterator_range(vertices(g))) {
auto const& [id, other] = g[v];
std::cout << id << " " << std::quoted(other) << "\n";
}
if (auto v = g.vertex_by_property({1111})) {
std::cout << "==== removing " << g[*v].id << "\n";
clear_vertex(*v, g); // clear edges
remove_vertex(*v, g); // remove the vertex
}
print_graph(g, get(&Vertex::id, g), std::cout << "---\n");
print_graph(g, get(&Vertex::other, g), std::cout << "---\n");
}
Prints
---
1111 --> 2222
2222 --> 1111
---
default-constructed --> twotwotwotwo
twotwotwotwo --> default-constructed
---
1111 "default-constructed"
2222 "twotwotwotwo"
==== removing 1111
---
2222 -->
---
twotwotwotwo -->
Sidenote:
you cannot use an associative container selector for vertices. Specifying hash_mapS leads to unordered_set<void*, hash<void*>, equal_to<void*>, allocator<void*>> as the m_vertices type.

Boost - Find maximum matching edges in a graph

I am a Boost (and C++) newbie, going through the graph library tutorial. I can create a graph and give it vertices and edges. I would like to use maximum cardinality matching in Boost to return a set of edges that form the maximum matching in the graph.
I have looked through max_cardinality_matching.hpp, but am not quite sure how to use it, or which functions to use, to return the maximum matching set of edges.
Here is my code so far:
#include <iostream>
#include <boost/graph/max_cardinality_matching.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graph_traits.hpp>
using namespace boost;
int main(int argc, const char * argv[]) {
// create a typedef for the Graph type
typedef adjacency_list<vecS, vecS, bidirectionalS> Graph;
// Make convenient labels for the vertices
enum { A, B, C, D, E, F, N };
const int num_vertices = N;
// const char* name = "ABCDE";
// writing out the edges in the graph
typedef std::pair<int, int> Edge;
Edge edge_array[] =
{ Edge(A,B), Edge(B,C), Edge(C,D), Edge(D,E), Edge(E,F) };
// const int num_edges = sizeof(edge_array)/sizeof(edge_array[0]);
// declare a graph object and add the edges
Graph g(edge_array, edge_array + sizeof(edge_array) / sizeof(Edge), num_vertices);
// get the property map for vertex indices
// property_map<Graph, property type>
typedef property_map<Graph, vertex_index_t>::type IndexMap;
IndexMap index = get(vertex_index, g);
// Create an iterator for vertices
typedef graph_traits<Graph>::vertex_iterator vertex_iter;
std::cout << "vertices(g) = ";
// Vertices returns a pair of vertex iterators
// The first iter points to the beginning of the vertices
// The second points past the end
std::pair<vertex_iter, vertex_iter> vp;
// vertices() returns the vertices in graph g
for (vp = vertices(g); vp.first != vp.second; ++vp.first)
std::cout << index[*vp.first] << " ";
std::cout << std::endl;
graph_traits<Graph>::edge_iterator ei, ei_end;
std::cout << "edges(g) = ";
// For each tuple of vertices (an edge), till the end of the edge list ...
for (tie(ei, ei_end) = edges(g); ei != ei_end; ++ei)
// ... print out the source and target vertices in the edge
std::cout << "(" << index[source(*ei, g)] << "," << index[target(*ei, g)] << ") ";
std::cout << std::endl;
// Return the set of edges that form a maximum matching in graph g
return 0;
}
All you need to do is create read-write property map and pass it as second argument to
template <typename Graph, typename MateMap>
bool checked_edmonds_maximum_cardinality_matching(const Graph& g, MateMap mate);
You can create ordinary std::map where key and value are vertex descriptors and adapts it by associative_property_map in order to use it with checked_edmonds_maximum_cardinality_matching. From this map you can read all edges which create maximum cardinality of your graph.
// Return the set of edges that form a maximum matching in graph g
typedef graph_traits<Graph>::vertex_descriptor VD;
std::map<VD, VD> match;
boost::associative_property_map< std::map<VD,VD> > mapAdapter(match);
bool rc = checked_edmonds_maximum_cardinality_matching(g,mapAdapter);
if (rc)
{
std::set<graph_traits<Graph>::edge_descriptor> edges;
for (auto& i : match)
{
std::pair<Graph::edge_descriptor,bool> e = boost::edge(i.first,i.second,g);
if (e.second)
edges.insert(e.first);
std::cout << i.first << " is matched to " << i.second << std::endl;
}
// print edges
for (auto& e : edges)
std::cout << "edge: " << e << std::endl;
}
As output you can see:
0 is matched to 1
1 is matched to 0
2 is matched to 3
3 is matched to 2
4 is matched to 5
5 is matched to 4
edge: (0,1)
edge: (2,3)
edge: (4,5)

What should be the return value of a custom function addEdge in a new class based on BGL?

I try to implement a graph class based on https://stackoverflow.com/a/950173/7558038. When adding an edge I return the edge descriptor of the added edge, but if the edge already exists, it shouldn't be added. What shall I return then? Unfortunately, null_edge() does not exist (unlike null_vertex()). It could be an std::pair<e_it_t,bool> with an appropriate edge iterator type e_it_t, but how can I get an iterator to the new edge?
Don't use that class that is almost 10 years old. It is obsolete.
Bundled properties have come to BGL as long as I know, which is... probably since at least 2010. Nothing there is fundamentally easier than straight boost.
Another weird property is that somehow only complementary edges can be inserted in that graph. This might be what you want, but it doesn't warrant having the complete class, IMO.
In fact, having the custom type removes ADL, which makes things more tedious unless you go and add each other operation (like, you know, out_edges or in_edges, which presumably is what you wanted a bidirectional graph for in the first place; maybe you actually wish to have iterable ranges instead of pair<iterator, iterator> which requires you to write old-fashioned for loops).
Now that I've warmed up a bit, lets demonstrate:
Using The Obsolete Wrapper class
The linked wrapper affords usage like this:
struct VertexProperties { int i; };
struct EdgeProperties { double weight; };
int main() {
using MyGraph = Graph<VertexProperties, EdgeProperties>;
MyGraph g;
VertexProperties vp;
vp.i = 42;
MyGraph::Vertex v1 = g.AddVertex(vp);
g.properties(v1).i = 23;
MyGraph::Vertex v2 = g.AddVertex(vp);
g.properties(v2).i = 67;
g.AddEdge(v1, v2, EdgeProperties{1.0}, EdgeProperties{0.0});
for (auto vr = g.getVertices(); vr.first!=vr.second; ++vr.first) {
auto& vp = g.properties(*vr.first);
std::cout << "Vertex " << vp.i << "\n";
for (auto er = g.getAdjacentVertices(*vr.first); er.first!=er.second; ++er.first) {
auto s = *vr.first;
auto t = *er.first;
// erm how to get edge properties now?
std::cout << "Edge " << g.properties(s).i << " -> " << g.properties(t).i << " (weight?!?)\n";
}
}
}
Which prints:
Vertex 23
Edge 23 -> 67 (weight?!?)
Vertex 67
Edge 67 -> 23 (weight?!?)
Note I didn't exactly bother to solve the problem of getting the edge-weight (we don't readily get edge descriptors from the interface at all).
The for loops throw us back in time at least 6 years. And that's not nearly the worst problem. Presumably, you need your graph for something. Let's assume you want minimum cut, or a shortest path. This means you want to invoke an algorithm that requires the edge weight. This would look like so:
// let's find a shortest path:
// build the vertex index map
boost::property_map<MyGraph::GraphContainer, vertex_properties_t>::const_type vpmap =
boost::get(vertex_properties, g.getGraph());
// oops we need the id from it. No problem, it takes only rocket science:
struct GetId {
int operator()(VertexProperties const& vp) const {
return vp.i;
}
};
GetId get_id;
boost::transform_value_property_map<GetId,
boost::property_map<MyGraph::GraphContainer, vertex_properties_t>::const_type,
int> id_map
= boost::make_transform_value_property_map<int>(get_id, vpmap);
// build the weight map
boost::property_map<MyGraph::GraphContainer, edge_properties_t>::const_type epmap =
boost::get(edge_properties, g.getGraph());
// oops we need the weight from it. No problem, it takes only rocket science:
struct GetWeight {
double operator()(EdgeProperties const& ep) const {
return ep.weight;
}
};
GetWeight get_weight;
boost::transform_value_property_map<GetWeight,
boost::property_map<MyGraph::GraphContainer, edge_properties_t>::const_type,
double> weight_map
= boost::make_transform_value_property_map<double>(get_weight, epmap);
// and now we "simply" use Dijkstra:
MyGraph::vertex_range_t vertices = g.getVertices();
//size_t n_vertices = g.getVertexCount();
MyGraph::Vertex source = *vertices.first;
std::map<MyGraph::Vertex, MyGraph::Vertex> predecessors;
std::map<MyGraph::Vertex, double> distance;
boost::dijkstra_shortest_paths(g.getGraph(), source,
boost::predecessor_map(boost::make_assoc_property_map(predecessors))
.distance_map(boost::make_assoc_property_map(distance))
.weight_map(weight_map)
.vertex_index_map(id_map));
This is not my idea of usability. Just to show it all compiles and runs:
Live On Coliru
Replace The Wrapper In 2 Lines Of C++11
Let's replace the whole Graph class template in modern BGL style:
template <typename VertexProperties, typename EdgeProperties>
using Graph = adjacency_list<setS, listS, bidirectionalS, VertexProperties, EdgeProperties>;
Really. That is a solid replacement, I'll demonstrate it right away.
In fact, let's not do using namespace boost; because it pollutes our namespace with all manner of names we might find really useful (like, you know source or num_vertices) and invites ambiguous symbols:
template <typename VertexProperties, typename EdgeProperties>
using Graph = boost::adjacency_list<boost::setS, boost::listS, boost::bidirectionalS, VertexProperties, EdgeProperties>;
The Same Use-Cases - creation and dijkstra
They are still as simple, or in fact simpler. The full code goes down from 249 lines of code to just 57:
Live On Coliru
#include <boost/graph/adjacency_list.hpp>
namespace MyLib {
template <typename VertexProperties, typename EdgeProperties>
using Graph = boost::adjacency_list<boost::setS, boost::listS, boost::bidirectionalS, VertexProperties, EdgeProperties>;
}
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <iostream>
struct VertexProperties { int i; };
struct EdgeProperties { double weight; };
int main() {
using boost::make_iterator_range;
using MyGraph = MyLib::Graph<VertexProperties, EdgeProperties>;
MyGraph g;
auto v1 = add_vertex({42}, g);
auto v2 = add_vertex({42}, g);
g[v1].i = 23;
g[v2].i = 67;
add_edge(v1, v2, EdgeProperties{ 1.0 }, g);
add_edge(v2, v1, EdgeProperties{ 0.0 }, g);
for (auto v : make_iterator_range(vertices(g))) {
std::cout << "Vertex " << g[v].i << "\n";
}
for (auto e : make_iterator_range(boost::edges(g))) {
auto s = source(e, g);
auto t = target(e, g);
std::cout << "Edge " << g[s].i << " -> " << g[t].i << " (weight = " << g[e].weight << ")\n";
}
// let's find a shortest path:
auto id_map = get(&VertexProperties::i, g);
auto weight_map = get(&EdgeProperties::weight, g);
auto source = *vertices(g).first;
using Vertex = MyGraph::vertex_descriptor;
std::map<Vertex, Vertex> predecessors;
std::map<Vertex, double> distance;
std::map<Vertex, boost::default_color_type> colors;
boost::dijkstra_shortest_paths(
g, source,
boost::vertex_color_map(boost::make_assoc_property_map(colors))
.predecessor_map(boost::make_assoc_property_map(predecessors))
.distance_map(boost::make_assoc_property_map(distance))
.weight_map(weight_map)
.vertex_index_map(id_map));
}
I'd say
that is superior.
it's just as elegant despite not relying on using namespace boost (ADL is the key here)
and we actually printed the edge weight!
And It Can Be Cleaner Still
If you switch to a vertex container selector that has implicit vertex index (like vecS):
Live On Coliru
#include <boost/graph/adjacency_list.hpp>
namespace MyLib {
template <typename VertexProperties, typename EdgeProperties>
using Graph = boost::adjacency_list<boost::setS, boost::vecS, boost::bidirectionalS, VertexProperties, EdgeProperties>;
}
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <iostream>
struct VertexProperties { int i; };
struct EdgeProperties { double weight; };
int main() {
using boost::make_iterator_range;
using MyGraph = MyLib::Graph<VertexProperties, EdgeProperties>;
MyGraph g;
add_vertex({23}, g);
add_vertex({67}, g);
add_edge(0, 1, EdgeProperties{ 1.0 }, g);
add_edge(1, 0, EdgeProperties{ 0.0 }, g);
for (auto v : make_iterator_range(vertices(g))) {
std::cout << "Vertex " << g[v].i << "\n";
}
for (auto e : make_iterator_range(boost::edges(g))) {
auto s = source(e, g);
auto t = target(e, g);
std::cout << "Edge " << g[s].i << " -> " << g[t].i << " (weight = " << g[e].weight << ")\n";
}
// let's find a shortest path:
std::vector<size_t> predecessors(num_vertices(g));
std::vector<double> distance(num_vertices(g));
boost::dijkstra_shortest_paths(g, *vertices(g).first,
boost::predecessor_map(predecessors.data()).distance_map(distance.data())
.weight_map(get(&EdgeProperties::weight, g)));
}
Output:
Vertex 23
Vertex 67
Edge 23 -> 67 (weight = 1)
Edge 67 -> 23 (weight = 0)
WAIT - Don't Forget About The Question!
I won't! I think the above shows the problem was an X/Y problem.
If you hadn't had the handicap of custom class wrapping, detecting duplicate edges was a given (see if add_vertex in BGL checks for the existence of the vertex for background):
struct { size_t from, to; double weight; } edge_data[] = {
{0, 1, 1.0},
{1, 0, 0.0},
{0, 1, 99.999} // oops, a duplicate
};
for(auto request : edge_data) {
auto addition = add_edge(request.from, request.to, { request.weight }, g);
if (!addition.second) {
auto& weight = g[addition.first].weight;
std::cout << "Edge already existed, changing weight from " << weight << " to " << request.weight << "\n";
weight = request.weight;
}
}
This will print Live On Coliru:
Edge already existed, changing weight from 1 to 99.999
If you prefer you can of course write things more expressively:
Graph::edge_descriptor e;
bool inserted;
boost::tie(e, inserted) = add_edge(request.from, request.to, { request.weight }, g);
Or, with some c++17 flair:
auto [e, inserted] = add_edge(request.from, request.to, { request.weight }, g);
More From Here
Also, in all likelihood you need to do uniqueness checks on the vertices too, so you end up with graph creation code like you can see in this answer: Boost BGL BFS Find all unique paths from Source to Target
Graph read_graph() {
std::istringstream iss(R"(
0 1 0.001
0 2 0.1
0 3 0.001
1 5 0.001
2 3 0.001
3 4 0.1
1 482 0.1
482 635 0.001
4 705 0.1
705 5 0.1
1 1491 0.01
1 1727 0.01
1 1765 0.01)");
Graph g;
std::map<int,Vertex> idx; // temporary lookup of existing vertices
auto vertex = [&](int id) mutable {
auto it = idx.find(id);
if (it != idx.end())
return it->second;
return idx.emplace(id, add_vertex(id, g)).first->second;
};
for (std::string line; getline(iss, line);) {
std::istringstream ls(line);
int s,t; double w;
if (ls >> s >> t >> w) {
add_edge(vertex(s), vertex(t), w, g);
} else {
std::cerr << "Skipped invalid line '" << line << "'\n";
}
}
return g;
}
Other examples show how you can insert both a -> b and b -> a while maintaining a mapping between the forward and back edges: Accessing specific edges in boost::graph with integer index
Summary
Coming full circle, I recommend getting acquainted with the newer, more elegant Boost Graph features. In the end, it's perfectly normal to encapsulate your graph, and you might end up with an even more polished interface.

Boost.Graph - algo.is_optimal() assertion

I am trying to implement a program that detects arbitrage trading opportunities using a minimum cost flow algorithm. This algorithm is implemented in Boost.Graph in the form of boost::push_relabel_max_flow() followed by a call to boost::cycle_canceling().
The following is the code I have already, leaving out the boost::cycle_canceling-part because my program dies before reaching the function.
#include <boost/graph/adjacency_list.hpp>
#include <boost/property_map/property_map.hpp>
#include <boost/graph/push_relabel_max_flow.hpp>
#include <boost/graph/cycle_canceling.hpp>
#include <boost/graph/directed_graph.hpp>
#include <boost/config.hpp>
#include <iostream>
#include <string>
typedef boost::adjacency_list_traits<boost::vecS, boost::vecS, boost::directedS> Traits;
struct st_order {
double price;
double amount;
std::string type;
};
struct VertexProps {
unsigned int id;
};
struct EdgeProps {
double capacity;
double residual_capacity;
double weight;
Traits::edge_descriptor reverse;
};
typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS, VertexProps, EdgeProps > DirectedGraph;
int main() {
DirectedGraph g;
// ETH / BTC
std::vector<st_order> trades{
st_order{0.0101,10.0,"SELL"},
st_order{0.01,3.0,"BUY"},
st_order{0.0102,5.0,"SELL"},
st_order{0.2,42.0,"BUY"},
};
std::vector<boost::graph_traits<DirectedGraph>::vertex_descriptor> vertices;
for(unsigned int vertex_index = 0; vertex_index < trades.size(); vertex_index++)
{
vertices.push_back(boost::add_vertex({vertex_index}, g));
}
std::map<DirectedGraph::vertex_descriptor, std::map<DirectedGraph::vertex_descriptor, Traits::edge_descriptor>> edges;
int ifirst = 0;
for(auto& first : vertices)
{
int isecond = 0;
for(auto& second : vertices)
{
if(first == second || trades[ifirst].type.compare(trades[isecond].type) == 0)
{
isecond++;
continue;
}
double amount = trades[isecond].amount;
if(trades[isecond].type.compare("SELL") == 0)
amount = amount * trades[isecond].price;
edges[first][second] = boost::add_edge(first, second, {amount, amount, (trades[isecond].price / trades[ifirst].price)} , g).first;
std::cout << "Add Edge: From " << first << " to " << second << std::endl;
isecond++;
}
ifirst++;
}
for(auto& i : vertices)
{
for(auto& j : vertices)
{
if(i == j)
continue;
if(edges.find(i) != edges.end() && edges[i].find(j) != edges[i].end())
{
if(edges.find(j) == edges.end() || edges[j].find(i) == edges[j].end())
{
throw std::runtime_error("No return edge found: "+std::to_string(i)+" "+std::to_string(j));
}
auto edge = boost::edge(i,j,g).first;
g[edge].reverse = edges[i][j];
}
}
}
double flow = boost::push_relabel_max_flow(g, vertices[0], vertices[1],
boost::get(&EdgeProps::capacity, g),
boost::get(&EdgeProps::residual_capacity, g),
boost::get(&EdgeProps::reverse, g),
boost::get(boost::vertex_index, g)
);
// Now boost::cycle_canceling() would follow
std::cout << "END << std::endl;
return 0;
};
The "normal" output of my program is:
Add Edge: From 0 to 1
Add Edge: From 0 to 3
Add Edge: From 1 to 0
Add Edge: From 1 to 2
Add Edge: From 2 to 1
Add Edge: From 2 to 3
Add Edge: From 3 to 0
Add Edge: From 3 to 2
As flowchart:
My program asserts in the push_relabel_max_flow-function. The following is the complete error code (which is printed at runtime):
/usr/local/include/boost/graph/push_relabel_max_flow.hpp:707: typename
boost::property_traits<IndexMap>::value_type
boost::push_relabel_max_flow(Graph&, typename
boost::graph_traits<Graph>::vertex_descriptor, typename
boost::graph_traits<Graph>::vertex_descriptor, CapacityEdgeMap,
ResidualCapacityEdgeMap, ReverseEdgeMap, VertexIndexMap) [with Graph =
boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS,
VertexProps, EdgeProps>; CapacityEdgeMap =
boost::adj_list_edge_property_map<boost::directed_tag, double, double&, long
unsigned int, EdgeProps, double EdgeProps::*>; ResidualCapacityEdgeMap =
boost::adj_list_edge_property_map<boost::directed_tag, double, double&, long
unsigned int, EdgeProps, double EdgeProps::*>; ReverseEdgeMap =
boost::adj_list_edge_property_map<boost::directed_tag,
boost::detail::edge_desc_impl<boost::directed_tag, long unsigned int>,
boost::detail::edge_desc_impl<boost::directed_tag, long unsigned int>&, long
unsigned int, EdgeProps, boost::detail::edge_desc_impl<boost::directed_tag,
long unsigned int> EdgeProps::*>; VertexIndexMap =
boost::vec_adj_list_vertex_id_map<VertexProps, long unsigned int>; typename
boost::property_traits<IndexMap>::value_type = double; typename
boost::graph_traits<Graph>::vertex_descriptor = long unsigned int]: Assertion
`algo.is_optimal()' failed.
At the very end of the message you can see Assertion: algo.is_optimal() failed. I have absolutly no idea what this means.
In the source file (boost/graph/push_relabel_max_flow.hpp) it is defined as:
bool is_optimal() {
// check if mincut is saturated...
global_distance_update();
return get(distance, src) >= n;
}
I have googled it and didn't find anything. Did I pass my parameters the wrong way? Is it because I use double as capacity (although, if I recall correctly, the "documentation" described it as possible to use a double for the capacity)?
Also, I have discovered this sentence in the documentation:
The CapacityEdgeMap argument cap must map each edge in E to a positive
number, and each edge in E^T to 0.
What does the bold part mean? Does it mean that I have to set the capacity of all outgoing edges from the sink-vertex to 0?
You need to set the reverse edges' capacities to 0.
So you need:
auto edge = boost::edge(i,j,g).first;
g[edge].reverse = edges[i][j];
g[edges[i][j]].capacity = 0;
I'm not sure why this is though. Looking into read_dimacs.hpp I noticed that they create their reverse edges and give them 0 capacity. About 3/4 of the way down the page:
capacity[e1] = cap;
capacity[e2] = 0;
reverse_edge[e1] = e2;
reverse_edge[e2] = e1;
Likely without this constraint, the algorithm will try to treat these as normal edges. The portion of the documentation that you quote describes this, but it's not completely obvious.
There are several special requirements on the input graph and property
map parameters for this algorithm. First, the directed graph G=(V,E)
that represents the network must be augmented to include the reverse
edge for every edge in E. That is, the input graph should be Gin =
(V,{E U E^T}). The ReverseEdgeMap argument rev must map each edge in
the original graph to its reverse edge, that is (u,v) -> (v,u) for all
(u,v) in E. The CapacityEdgeMap argument cap must map each edge in E
to a positive number, and each edge in E^T to 0.
I think here E^T means transpose not target. You have to know that the transpose of a directed adjacency matrix is actually the reverse of the all the edges. That is why they say that the input graph is G = {V, E U E^T}. The edges plus the reverse ones that need to be added.
Side note: changing long to double in the push-relable example worked perfectly fine.