I am trying to use the Boost.Graph Library to run Goldberg's Max-Flow Algorithm. Boost.Graph calls it push_relabel_max_flow.
However, I have a very hard time understanding the library and its type-system. The documentation, which I linked above, gives an example code. But in that example, the graph is read from a file. I want to generate the graph at runtime. This is the code I have so far (mostly copied from the example):
typedef boost::adjacency_list_traits<boost::vecS, boost::vecS, boost::directedS> Traits;
typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS,
boost::property<boost::vertex_name_t, std::string>,
boost::property<boost::edge_capacity_t, long,
boost::property<boost::edge_residual_capacity_t, long,
boost::property<boost::edge_reverse_t, Traits::edge_descriptor>>>> DirectedGraph;
DirectedGraph g;
Traits::vertex_descriptor s, t;
s = boost::add_vertex(g);
t = boost::add_vertex(g);
boost::add_vertex(g);
boost::add_vertex(g);
After I added 4 vertices to the graph I should "connect" them, i.e., making edges with a capacity, residual capacity and the reverse value. The function for this task is boost::add_edge() but I have no clue how I can pass my arguments. The example code does not show it, because as I said, the data is read from a file and then directly parsed to a graph. Maybe someone with experience in the Boost.Graph library can show me how.
You can add an edge between vertices s and t like so:
boost::add_edge(s, t, {33, 44}, g);
Here setting edge_capacity to 33, and edge_residual_capacity to 44.
To actually access the edge properties, as far as I know you must do something like this:
std::cout << boost::get(boost::edge_capacity, g, boost::edge(s,t,g).first) << '\n';
which is annoying. It's easier if you use bundled properties instead, like so:
typedef boost::adjacency_list_traits<boost::vecS, boost::vecS, boost::directedS> Traits;
struct VertexProps {
std::string name;
};
struct EdgeProps {
long capacity;
long residual_capacity;
Traits::edge_descriptor reverse;
};
typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS,
VertexProps, EdgeProps > DirectedGraph;
Then you can add vertices and edges just the same way, but it's easier to access edge properties, e.g.
auto e = boost::edge(s,t,g).first; // get the edge descriptor for an edge from s to t, if any
std::cout << g[e].capacity << '\n';
To add an edge between the anonymous third and fourth vertices you added, you can get away with
boost::add_edge(2, 3, {17, 26}, g);
since the underlying storage is vector, and so vertex_descriptor is just the vector index (aka size_t, aka unsigned long around here). But to be more strictly correct you should do
boost:add_edge(boost::vertex(2, g), boost::vertex(3, g), {17, 26}, g);
in order to get the vertex_descriptor for the 3rd and 4th vertices.
Related
I usually work with vecS as container for boost::adjacency_list:
struct myVertexType { std::vector<Stuff> vec; /* and more */ };
struct myEdgeType { /* some data too */ };
using Graph = boost::adjacency_list<
boost::vecS,
boost::vecS,
boost::directedS,
myVertexType,
myEdgeType
>;
However, I encountered a situation were that raised an issue:
I was referencing some data stored as a bundled property of a vertex and when I created another vertex, that seemed to make my reference invalid (1).
At least that's what I understood from reading this page (section "Iterator and Descriptor Stability/Invalidation").
So I switched to listS, and all went fine:
using Graph = boost::adjacency_list<
boost::listS,
boost::listS,
boost::directedS,
myVertexType,
myEdgeType
>;
Until...
Until I noticed that with listS, boost::target( e1, g ) fails to compile! :
Graph g;
auto e1 = boost::add_edge(1, 0, g).first;
auto t = boost::target( e1, g );
This fails to build too: (see on coliru)
Graph g;
boost::add_edge(1, 0, g);
write_graphviz(std::cout, g );
So I searched a bit and found an answer by Sehe, stating that
vecS has an implicit vertex index.
listS doesn't. Therefore it uses the internal property vertex_index_t
However, the given answer uses Inner properties (?) (or is it dynamic properties?) and I am using my own datatypes for vertices and edges.
So my question is:
How can I build a list-based graph type that enables me to do all the "regular stuff" allowed by VecS?
(1) to be clear, I was referencing a vector that was in a vertex, and when I created another vertex, the vector suddenly became empty!
Edit: clarified what is inside my nodes.
Background
"when I created another vertex, that seemed to make my reference invalid (1)."
Yes, that's possible.
You have to realize that there's are much bigger performance trade-offs underlying your choice of container selectors. Many algorithms can get very different efficiency characteristics.
Also, some semantics subtly change (e.g. when using setS as the edge container selector, you naturally cannot have duplicate edges anymore; this is also why add_edge returns a pair<descriptor, bool>).
Also realize that often you don't need reference or even iterator stability. The typical coding pattern in BGL is not to pass/hold references to property (bundles), but instead pass property maps by value.
Property maps abstract aways access to (mutable) properties.
You can usually pass descriptors which usually are stable (unless you're removing vertices "in the middle" in vecS, as the implied vertex index is obviously changing for all following vertices).
That said, let's move on to your problems:
Questions
Until I noticed that with listS, boost::target( e1, g ) fails to compile!
Nope. That compiles fine.
What ISN'T fine is that you call add_edge with integral arguments. The vertex descriptor isn't integral with lists/setS (node based containers).
Worse, vertices don't get automatically added for non-vecS adjacency_list so you'd be referring to vertices out-of-range anyways.
The general way to refer to these is:
V v0 = add_vertex(g);
V v1 = add_vertex(g);
auto [e1, inserted] = boost::add_edge(v0, v1, g);
assert(inserted);
[[maybe_unused]] V t = boost::target(e1, g);
The graphviz call is also fine, but fails for the same reason on add_edge...
Also, you need to add a vertex index. Either as interior property or passing a property map to the algorithm function.
Here's a complete test demo that shows all three flavours:
Live On Coliru
#include <boost/algorithm/string.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graphviz.hpp>
#include <boost/core/demangle.hpp>
#include <iostream>
#include <numeric>
using boost::core::demangle;
using boost::algorithm::replace_all_copy;
struct myVertexType { /* some data */ };
struct myEdgeType { /* some data too */ };
template <typename containerS> void tests() {
using Graph = boost::adjacency_list<
containerS, containerS,
boost::directedS,
myVertexType,
myEdgeType>;
using V = typename Graph::vertex_descriptor;
std::cout << "\n"
<< std::boolalpha << "tests() with "
<< demangle(typeid(containerS).name()) << " - "
<< "vertex_descriptor integral? " << std::is_integral<V>()
<< "\n";
Graph g;
V v0 = add_vertex(g);
V v1 = add_vertex(g);
auto [e1, inserted] = boost::add_edge(v0, v1, g);
assert(inserted);
[[maybe_unused]] V t = boost::target(e1, g);
std::ostringstream dot;
if constexpr (std::is_same<boost::vecS, containerS>()) {
boost::write_graphviz(dot, g);
} else {
std::map<V, int> index;
for (auto v : boost::make_iterator_range(vertices(g)))
index.emplace(v, index.size());
auto index_map = boost::make_assoc_property_map(index);
boost::dynamic_properties dp;
dp.property("node_id", index_map); // get(boost::vertex_index, g)
boost::write_graphviz_dp(dot, g, dp);
}
std::cout << "dot: " << replace_all_copy(dot.str(), "\n", "") << "\n";
}
int main() {
tests<boost::vecS>();
tests<boost::setS>();
tests<boost::listS>();
}
Prints
tests() with boost::vecS - vertex_descriptor integral? true
dot: digraph G {0;1;0->1 ;}
tests() with boost::setS - vertex_descriptor integral? false
dot: digraph G {0;1;0->1 ;}
tests() with boost::listS - vertex_descriptor integral? false
dot: digraph G {0;1;0->1 ;}
I want to create a graph for an algorithm which requires graph concepts only provided by an adjacency_list. The vertex ids themselves are random size_t's and non-contiguous, so using a vector as the underlying storage is impossible, but this does not compile:
#include <boost/graph/adjacency_list.hpp>
int main()
{
using namespace boost;
using out_edge_storage = setS;
// using vertex_storage = vecS; // compiles Ok
using vertex_storage = setS; // error: no matching function for call to 'add_edge'
using graph = adjacency_list<out_edge_storage, vertex_storage, undirectedS>;
graph g;
add_edge(3, 44, g);
add_edge(1024102400, 3, g); // uses too much space (bad_alloc) with vecS
}
I do not need any extra custom vertex properties nor do I need to modify the graph after creating it. Reading through the documentation [1] I could not find a reason what the extra requirements for add_edge() are.
How would I construct a graph using a set or hash set datatype, and where in the documentation can I find the details I have missed?
1: http://www.boost.org/doc/libs/1_58_0/libs/graph/doc/using_adjacency_list.html + http://www.boost.org/doc/libs/1_63_0/libs/graph/doc/adjacency_list.html
(The other stackoverflow questions regarding adjacency_list+vecS (e.g. here) are far from minimal and did not help.)
I do not need any extra custom vertex properties nor do I need to modify the graph after creating it.
Well, maybe not in your mind, but since the vector index no longer "doubles" as vertex id, you want somewhere to attach these numbers to the vertex descriptors.
This happens to be your reason to require/desire a property. I'd suggest a internal property if you want the algorithms also automatically know how to use that number to identify your indices.
Live On Coliru
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graph_utility.hpp>
using graph = boost::adjacency_list<boost::setS, boost::setS, boost::undirectedS,
boost::property<boost::vertex_index_t, size_t> >;
int main() {
graph g;
auto A = add_vertex(3, g);
auto B = add_vertex(44, g);
auto C = add_vertex(1024102400, g);
add_edge(A, B, g);
add_edge(C, A, g);
print_graph(g);
}
Printing:
3 <--> 44 1024102400
44 <--> 3
1024102400 <--> 3
I'm a BGL newbie with a (possibly) easy question: I have a directed graph and use bundled properties for edges, one of them being an index of type int. Knowing a unique index, I would like to get the corresponding edge_descriptor of that edge in order to perform operations on it. The following example summarizes my problem:
#include <boost/graph/adjacency_list.hpp>
struct EdgeProperties {
EdgeProperties(): distance(10), time_limit(5) {};
int index;
int distance;
int time_limit;
};
typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS, boost::no_property, EdgeProperties> Graph;
int main() {
Graph graph;
EdgeProperties edge_prop1, edge_prop2, edge_prop3, edge_prop4;
// Define edge properties
edge_prop1.index = 0;
edge_prop2.index = 1;
edge_prop3.index = 2;
edge_prop4.index = 3;
// Add edges to graph
boost::add_edge(0, 1, edge_prop1, graph);
boost::add_edge(0, 2, edge_prop2, graph);
boost::add_edge(1, 3, edge_prop3, graph);
boost::add_edge(2, 3, edge_prop4, graph);
// Get vertex_descriptor from an (int) index:
int vertex_index = 2;
boost::graph_traits<Graph>::vertex_descriptor v = boost::vertex(vertex_index, graph);
// I would like to get an edge_descriptor from an (int) index property:
// The following DOES NOT work:
boost::graph_traits<Graph>::edge_descriptor e = boost::edge(edge_prop1.index, graph);
}
I read about property maps as well, but could not find a solution my problem. I would prefer bundled properties over internal properties.
Is there a way of assigning unique int type indices via a bundle property to edges and access edges through these int type values?
Sadly, I don't think boost::graph is of immediate help here.
First, there is no mechanism to find an edge (or vertex, for that matter), based on a field of an edge property - BGL keeps any such mapping, and the 'index' field you have is entirely for your purposes.
Second, there is the boost::edges function that returns an iterator range for all edges of the graph. I though that you could pass vecS as edge container type to adjacency_list template, and then look inside this range, but per http://www.boost.org/doc/libs/1_61_0/libs/graph/doc/EdgeListGraph.html the iterators are only required to be multi-pass input iterators, and the implementation does exactly that -- even with vecS as edge type, you can't do random access.
Therefore, it seems that the only way to accomplish what you want is to keep your own unodered_map from index to edge descriptor.
I have a directed multigraph with vertices A..C and edges E1..E4
A ---E1--> B
A ---E2--> B
A ---E3--> B
B ---E4--> C
I wanted to iterate over the edges that connect A and B.
In BGL, I expressed this as:
#include <boost/graph/adjacency_list.hpp>
struct Vertex
{
std::string code;
};
struct Edge
{
double distance;
std::string code;
};
int main()
{
using namespace boost;
typedef adjacency_list<listS, vecS, directedS, Vertex, Edge> Graph;
Graph g;
auto a= add_vertex(Vertex{ "A" }, g);
auto b= add_vertex(Vertex{ "B" }, g);
auto c= add_vertex(Vertex{ "C" }, g);
add_edge(a, b, Edge{ 10, "E1" }, g);
add_edge(a, b, Edge{ 10, "E2" }, g);
add_edge(a, b, Edge{ 10, "E3" }, g);
add_edge(a, c, Edge{ 10, "E4" }, g);
// checking number of edges
std::cout<< num_edges(g)<< std::endl;
// printing edges branching from A
auto erange= out_edges(a, g);
for(auto i= erange.first; i!= erange.second; ++ i)
std::cout<< g[*i].code<< std::endl;
// now we want to iterate over edges that connect A and B
auto wtf= boost::edge_range(a, b, g);
}
Which results in a compilation error:
In file included from /usr/include/boost/graph/adjacency_list.hpp:246:
/usr/include/boost/graph/detail/adjacency_list.hpp:1617:25: error: no matching constructor for initialization of 'StoredEdge' (aka
'boost::detail::stored_edge_property<unsigned long, Edge>')
equal_range(el, StoredEdge(v, fake_edge_container.end(),
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
I've read the documentation:
std::pair<out_edge_iterator, out_edge_iterator>
edge_range(vertex_descriptor u, vertex_descriptor v,
const adjacency_list& g)
Returns a pair of out-edge iterators that give the range for all the parallel edges from u to v. This function only works when the OutEdgeList for the adjacency_list is a container that sorts the out edges according to target vertex, and allows for parallel edges. The multisetS selector chooses such a container.
( http://www.boost.org/doc/libs/1_54_0/libs/graph/doc/adjacency_list.html )
Modified the graph:
typedef adjacency_list<multisetS, vecS, directedS, Vertex, Edge> Graph;
But the error did not change.
So how do you list edges between two vertices (from-> to) in a directed multigraph using BGL ?
I found a quick and dirty way:
auto erange= out_edges(a, g);$
for(auto i= erange.first; i!= erange.second; ++ i)$
std::cout<< g[*i].code<< " -> "<< g[target(*i, g)].code<< std::endl;$
Which will let me filter edge by target vertex. But how do you use boost::edge_range ?
This bug has been reported before on the Boost mailinglist.
it fails to compile when the Directed Selector template argument to
adjacency_list is set to directedS, but does work if the argument
is either undirectedS, or bidirectionalS. Attached below is a short
program illustrating the problem. The problem is that edge_range()
instantiates a StoredEdge via a constructor taking 3 arguments, but
when the Directed Selector is directedS StoredEdge is typedef'ed to
stored_edge_property, which has no
such constructor. One solution might be to create overloaded
edge_range_dispatch() functions, and dispatch on
Config::on_edge_storage.
Changing directedS to undirectedS in your program works. Live Example. But that might not be what you need for your application, so the simple filter you mentioned before might be better. You could repost this on the Boost mailinglist to get more attention for it.
I am investigating the use of the boost graph libraries in order to apply them to various network problems I have in mind.
In the examples I have been looking at the graph edge values ("weights") are always initialized as integers, such as in these Bellman-Ford and Kruskal algorithms eg:
int weights[] = { 1, 1, 2, 7, 3, 1, 1, 1 };
My problem is if I try and change the weights to double, I get a heap of warning messages about conversions etc, which so far I have not been able to figure out how to overcome.
Does anyone see a way around this?
It's caused by a mismatch between the weights[] array and the type used for edge weights by your boost graph/algorithm.
In the first linked sample, eg, you should also change
struct EdgeProperties {
int weight;
};
[...]
property_map<Graph, int EdgeProperties::*>::type
to
struct EdgeProperties {
double weight;
};
[...]
property_map<Graph, double EdgeProperties::*>::type
In the second
typedef adjacency_list < vecS, vecS, undirectedS,
no_property, property < edge_weight_t, int > > Graph;
to
typedef adjacency_list < vecS, vecS, undirectedS,
no_property, property < edge_weight_t, double > > Graph;