Boost graph libraries: setting edge weight values - c++

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;

Related

Adding edges to a graph in Boost.Graph

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.

Boost graph library: Get edge_descriptor or access edge by index of type int

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.

boost graph library directed multigraph edge_range bug

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.

how to access BGL's vertex_descriptor as an int

I have an adjacency list defined as shown below. At this point I need to access vertex_descriptor as an int type. How can I do that tvertex source = ...; int source_as_int = ???source??? I remember bumping into this same question before and solved it but don't remember how and the BGL documentation is useless using it as reference, they should try to take a look at and learn from Javadocs.
Another possibility is to use a possible member function of the vertex_descriptor type or otherwise some of the global BGL functions for this purpose ... one never knows where to look for this and they seem to randomly choose between making global functions or member functions, a total fail of an intuitive design if you ask me.
typedef adjacency_list_traits<setS, setS, bidirectionalS> ttraits;
typedef adjacency_list<setS, setS, bidirectionalS,
// vertex properties
property<vertex_color_t, default_color_type>,
// edge properties
property<edge_capacity_t, int,
property<edge_residual_capacity_t, int,
property<edge_reverse_t, ttraits::edge_descriptor> > >, no_property, vecS> tbgl_adjlist_bidir;
typedef graph_traits<tbgl_adjlist_bidir>::vertex_descriptor tvertex;
typedef graph_traits<tbgl_adjlist_bidir>::edge_descriptor tedge;
typedef property_map<tbgl_adjlist_bidir, edge_capacity_t>::type tedge_capacity_map;
typedef property_map<tbgl_adjlist_bidir, edge_reverse_t>::type treverse_edge_map;
typedef property_map<tbgl_adjlist_bidir, vertex_color_t>::type tvertex_color_map;
typedef graph_traits<tbgl_adjlist_bidir>::out_edge_iterator tout_edge_iterator;
typedef graph_traits<tbgl_adjlist_bidir>::in_edge_iterator tin_edge_iterator;
OK I figured it out. Adding the vertex property vertex_index_t solves the problem. I can then access the int index of the vertex like this:
typedef adjacency_list_traits<setS, vecS, bidirectionalS> ttraits;
typedef adjacency_list<setS, vecS, bidirectionalS,
// vertex properties
property<vertex_index_t, int,
property<vertex_color_t, default_color_type> >,
// edge properties
property<edge_capacity_t, int,
property<edge_residual_capacity_t, int,
property<edge_reverse_t, ttraits::edge_descriptor> > >, no_property, vecS> tbgl_adjlist_bidir;
typedef graph_traits<tbgl_adjlist_bidir>::vertex_descriptor tvertex;
typedef graph_traits<tbgl_adjlist_bidir>::edge_descriptor tedge;
typedef property_map<tbgl_adjlist_bidir, edge_capacity_t>::type tedge_capacity_map;
typedef property_map<tbgl_adjlist_bidir, edge_reverse_t>::type treverse_edge_map;
typedef property_map<tbgl_adjlist_bidir, vertex_color_t>::type tvertex_color_map;
typedef property_map<tbgl_adjlist_bidir, vertex_index_t>::type tvertex_index_map;
typedef graph_traits<tbgl_adjlist_bidir>::vertex_iterator tvertex_iterator;
typedef graph_traits<tbgl_adjlist_bidir>::edge_iterator tedge_iterator;
typedef graph_traits<tbgl_adjlist_bidir>::out_edge_iterator tout_edge_iterator;
typedef graph_traits<tbgl_adjlist_bidir>::in_edge_iterator tin_edge_iterator;
then I use it like this:
tbgl_adjlist_bidir bgl_adjlist_bidir;
// ...
tvertex_index_map indices = get(vertex_index, bgl_adjlist_bidir);
// ...
tvertex source;
// ...
int source_as_int = indices[source];
The type of a vertex_descriptor depends on the underlying structure of the VertexListS-template parameter of adjacency_list. The only case i know of, where the descriptor is an int is, when the VertexList-Type is vecS. But keep in mind: If you choose vecS as your VertexList-Type all (stored) descriptors can become invalid, when you change the structure of your graph(as described in Iterator and Descriptor Stability/Invalidation).
Could I interest you in using custom vertex and edge types instead? They're much, much easier to work with in your own code. And as for calling on BGL algorithms, you can use Bundled Properties.

BGL Adding an edge with multiple properties

I want to have all edges have to properties, weight and capacity. I found that BGL has these both already defined. So I define Edge and Vertex properties for the Graph
typedef property<vertex_name_t, string> VertexProperty;
typedef property<edge_weight_t, int, property<edge_capacity_t, int> > EdgeProperty;
typedef adjacency_list<listS,vecS, undirectedS, VertexProperty, EdgeProperty > Graph;
Here is where I am trying to add the edges to the graph:
172: EdgeProperty prop = (weight, capacity);
173: add_edge(vertex1,vertex2, prop, g);
If I had just 1 property I know it would be prop = 5; However, with two I am confused about the formatting.
Here is the error I am receiving:
graph.cc: In function ‘void con_graph()’:
graph.cc:172: warning: left-hand operand of comma has no effect
If you look at the implementation of boost::property you'll see that a property value cannot be initialized this way. And even then, the syntax you have (weight, capacity) is not valid anyways, so, if it was possible to initialize the property like that, it would be written EdgeProperty prop = EdgeProperty(weight, capacity); or just EdgeProperty prop(weight, capacity);. But, again, that won't work. Technically, this is the way you would need to initialize the property value:
EdgeProperty prop = EdgeProperty(weight, property<edge_capacity_t, int>(capacity));
But this is kind of ugly as the number of properties increase. So, it would be cleaner to default-construct the edge-property and then manually set each individual property:
EdgeProperty prop;
get_property_value(prop, edge_weight_t) = weight;
get_property_value(prop, edge_capacity_t) = capacity;
Of course, the better alternative is to use bundled properties instead of the older boost::property chains.
The correct form is:
EdgeProperty prop;
get_property_value(prop, edge_weight) = weight;
get_property_value(prop, edge_capacity) = capacity;