I am new to C++ and the boost graph library. I am trying to use iterators to access information already stored within my graph "lattuce", more specifically, the weight an edge between two specific nodes.
This data will then be used by a A* algorithm (not the one in Boost graph). I am not sure if iterators are the solution to this either, so any guidance or criticism would be appreciated.
struct Point {//struct point with vertex properties
int x, y;
int parentx, parenty;
double g;
double h;
friend std::ostream& operator<<(std::ostream& os, Point p) {
return os << "[" << p.x << "," << p.y << "]";
}
};
int main() {
//declarations
typedef property < edge_weight_t, double >Weight;
using std::vector;//?
using Graph = adjacency_list<setS, vecS, undirectedS, Point, Weight>;//graph includes our created point struct property<edge_weight_t
using vertex_descriptor = Graph::vertex_descriptor;
Graph lattuce;
//lattuce graph is created with weighted edges value 1 or 1,41 if diagonal. The functions used on a loop are:
//add_edge(nodes[p.x][p.y],nodes[neighbour.x][neighbour.y], Weight(1.0), lattuce);
//add_edge(nodes[p.x][p.y],nodes[neighbour.x][neighbour.y], Weight(1.4), lattuce);
If more information about the code that generates the graph is needed I'll provide it. Thanks
It is possible to obtain link edge weights in directed and undirected graphs by means of the boost::property_map:
boost::property_map<UndirectedGraph, boost::edge_weight_t>::type EdgeWeightMap = get(boost::edge_weight_t(), g);
Example implementation given below, that first builds the following simple graph (specifically a tree with no cycles):
... then uses the boost::property_map to obtain the weight of each edge, and prints it out:
#include <iostream>
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/adjacency_list.hpp>
typedef boost::property<boost::edge_weight_t, double> EdgeWeight;
typedef boost::adjacency_list<boost::listS, boost::vecS, boost::undirectedS, boost::no_property, EdgeWeight> UndirectedGraph;
typedef boost::graph_traits<UndirectedGraph>::edge_iterator edge_iterator;
int main(int, char*[])
{
// 1. Undirected graph - print out the edge weights
UndirectedGraph g;
boost::add_edge(0, 1, 8, g);
boost::add_edge(0, 5, 2, g);
boost::add_edge(5, 6, 1, g);
boost::add_edge(4, 5, 5, g);
boost::add_edge(3, 5, 7, g);
boost::property_map<UndirectedGraph, boost::edge_weight_t>::type EdgeWeightMap = get(boost::edge_weight_t(), g);
std::pair<edge_iterator, edge_iterator> edgePair;
for (edgePair = edges(g); edgePair.first != edgePair.second; ++edgePair.first)
{
std::cout << *edgePair.first << " " << EdgeWeightMap[*edgePair.first] << std::endl;
}
return 0;
}
Giving the following console output, showing the edges as (start,end) node pairs plus their respective weights:
Related
So I'm posting this because I'm currently working on an algorithm project and might gonna use the boost library for building a graph from an input text file. So I have noticed that there is a descriptor for the vertex in a graph, but since I have a very large graph to be built, do I need to allocate a descriptor for every vertex in that graph? If I don't, can I traverse the whole graph after it's been built?
I'm a newbie to the boost library and this is a little emergent so if anybody can explain this I will be very grateful!
A vertex descriptor describes a vertex (in cheap, graph model independent way).
A graph model describes a graph.
Iterators
When you want to traverse a graph, you traverse the graph, using the iterators:
typedef boost::adjacency_list<> Graph;
typedef Graph::vertex_iterator Vit;
Vit begin, end;
boost::tie(begin, end) = vertices(g);
Dereferencing a valid iterator gives you the descriptor (same thing goes for edge iterators).
Simple demo:
Live On Coliru
#include <boost/graph/adjacency_list.hpp>
#include <iostream>
int main () {
typedef boost::adjacency_list<> Graph;
typedef Graph::vertex_iterator Vit;
Graph g(10);
add_edge(2,5,g);
add_edge(5,3,g);
add_edge(3,8,g);
Vit begin, end;
boost::tie(begin, end) = vertices(g);
for (Vit it = begin; it != end; ++it) {
unsigned edges = out_degree(*it, g);
if (edges)
std::cout << "vertex #" << *it << " has " << edges << " outgoing edge(s)\n";
}
}
Prints:
vertex #2 has 1 outgoing edge(s)
vertex #3 has 1 outgoing edge(s)
vertex #5 has 1 outgoing edge(s)
Graph Model With Node-Based Vertex Containers
Not all graph have integral vertex descriptors, so adding edges becomes more complicated, and printing them doesn't look so "friendly"
Live On Coliru
#include <boost/graph/adjacency_list.hpp>
#include <iostream>
int main () {
typedef boost::adjacency_list<boost::setS, boost::listS/*, boost::undirectedS*/> Graph;
typedef Graph::vertex_iterator Vit;
Graph g(10);
Vit begin, end;
boost::tie(begin, end) = vertices(g);
{
std::vector<Graph::vertex_descriptor> vindex(begin, end);
add_edge(vindex[2], vindex[5], g);
add_edge(vindex[5], vindex[3], g);
add_edge(vindex[3], vindex[8], g);
}
for (Vit it = begin; it != end; ++it) {
unsigned edges = out_degree(*it, g);
if (edges)
std::cout << "vertex #" << *it << " has " << edges << " outgoing edge(s)\n";
}
}
Printing
vertex #0x994d00 has 1 outgoing edge(s)
vertex #0x994d70 has 1 outgoing edge(s)
vertex #0x994e50 has 1 outgoing edge(s)
Properties
In such cases, consider adding a property bundle.
This way, arbitrary application-specific information can be attached to model vertices (or edges, or graphs).
The main downside, in my opinion, of this is that there's no natural indexing for the properties, so looking up a graph entity by its (bundled) property could get bad performance (linear search) unless you keep an extra index outside of the graph manually.
Live On Coliru
#include <boost/graph/adjacency_list.hpp>
#include <iostream>
struct VertexProperties {
std::string name;
VertexProperties(std::string name) : name(name) {}
};
int main () {
typedef boost::adjacency_list<boost::setS, boost::listS, boost::directedS, VertexProperties> Graph;
typedef Graph::vertex_iterator Vit;
Graph g;
add_vertex(VertexProperties ("zero"), g);
add_vertex(VertexProperties ("one"), g);
add_vertex(VertexProperties ("two"), g);
add_vertex(VertexProperties ("three"), g);
add_vertex(VertexProperties ("four"), g);
add_vertex(VertexProperties ("five"), g);
add_vertex(VertexProperties ("six"), g);
add_vertex(VertexProperties ("seven"), g);
add_vertex(VertexProperties ("eight"), g);
add_vertex(VertexProperties ("nine"), g);
Vit begin, end;
boost::tie(begin, end) = vertices(g);
{
std::vector<Graph::vertex_descriptor> vindex(begin, end);
add_edge(vindex[2], vindex[5], g);
add_edge(vindex[5], vindex[3], g);
add_edge(vindex[3], vindex[8], g);
}
for (Vit it = begin; it != end; ++it) {
unsigned edges = out_degree(*it, g);
if (edges)
std::cout << "vertex '" << g[*it].name << "' has " << edges << " outgoing edge(s)\n";
}
}
Prints
vertex 'two' has 1 outgoing edge(s)
vertex 'three' has 1 outgoing edge(s)
vertex 'five' has 1 outgoing edge(s)
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.
I'm trying to use the dijkstra shortest path algorithm in BGL to compute a simple ST path on an unweighted undirected graph. I may care about edge weights in the future, but for now I just want to consider edge traversals to be a uniform cost.
I am also tracking multiple edge and vertex properties so I've based what I've done so far on the bundled properties example that seemed to be the closest to what I'm attempting to do.
Now I'm trying to figure out how to get dijkstra working so I can do my ST search but I am getting stuck on getting the right parameters set up for it.
Here's a simplified example of the code I have so far:
#include <iostream>
#include <vector>
#include <boost/config.hpp>
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <boost/property_map/property_map.hpp>
// Create a struct to hold properties for each vertex
typedef struct VertexProperties
{
int p1;
} VertexProperties;
// Create a struct to hold properties for each edge
typedef struct EdgeProperties
{
int p1;
} EdgeProperties;
// Define the type of the graph
typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS, VertexProperties, EdgeProperties> Graph;
int main(int,char*[])
{
// Create a graph object
Graph g;
// Add vertices
Graph::vertex_descriptor v0 = boost::add_vertex(g);
Graph::vertex_descriptor v1 = boost::add_vertex(g);
Graph::vertex_descriptor v2 = boost::add_vertex(g);
// Set vertex properties
g[v0].p1 = 1;
g[v1].p1 = 2;
g[v2].p1 = 3;
// Add edges
std::pair<Graph::edge_descriptor, bool> e01 = boost::add_edge(v0, v1, g);
std::pair<Graph::edge_descriptor, bool> e02 = boost::add_edge(v1, v2, g);
// Set edge properties
g[e01.first].p1 = 1;
g[e02.first].p1 = 2;
std::cout << "num_verts: " << boost::num_vertices(g) << std::endl;
std::cout << "num_edges: " << boost::num_edges(g) << std::endl;
// compute ST shortest paths here...
return 0;
}
I'm getting tripped up on the right parameters for the call to dijkstra's algorithm. They take the graph, a starting vertex, and then a predecessor map and distance map. The examples I've seen so far, like this one set up their graph with just an edge weight without the bundled edge properties, which simplifies things.
Ultimately, I'm after the ST shortest path so I'd need to recover the path from S to T. From the looks of things, we need to set up a predecessor map and then we can use that to extract the path from a particular T back to S?
I should also note that the environment I'm in does not allow C++11 language features. :(
Any help here would be greatly appreciated!
So the question was "how to use a bundled property as weight map with Boost Graph Library?".
Good. You use property maps. The bundled property can be accessed with a little bit of funky syntax documented right on the "Bundled Properties" page: http://www.boost.org/doc/libs/1_58_0/libs/graph/doc/bundles.html, see heading "Property maps from bundled properties".
Now for a quick demo:
// set up a weight map:
auto weights = boost::get(&EdgeProperties::p1, g);
Passing the minimum amount of arguments to dijkstra:
// you can pass it to dijkstra using direct or named params. Let's do the simplest
boost::dijkstra_shortest_paths(g, v0, boost::no_named_parameters() .weight_map(weights));
You will want to add more parameters, but hey, this is your start :)
Live On Coliru
#include <iostream>
#include <vector>
#include <boost/config.hpp>
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <boost/property_map/property_map.hpp>
#include <boost/graph/graph_utility.hpp>
// Create a struct to hold properties for each vertex
struct VertexProperties { int p1; };
// Create a struct to hold properties for each edge
struct EdgeProperties { int p1; };
// Define the type of the graph
typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS, VertexProperties, EdgeProperties> Graph;
int main() {
// Create a graph object
Graph g;
// Add vertices
auto v0 = boost::add_vertex({1}, g),
v1 = boost::add_vertex({2}, g),
v2 = boost::add_vertex({3}, g);
// Add edges
boost::add_edge(v0, v1, EdgeProperties{1}, g);
boost::add_edge(v1, v2, EdgeProperties{2}, g);
boost::print_graph(g, boost::get(&VertexProperties::p1, g));
// set up a weight map:
auto weights = boost::get(&EdgeProperties::p1, g);
// you can pass itprint_graph`enter code here` to dijkstra using direct or named params. Let's do the simplest
boost::dijkstra_shortest_paths(g, v0, boost::no_named_parameters() .weight_map(weights));
}
You'll note that I simplified the initialization of the vertex/edge properties as well. The print_graph utility is neat if you want to have an idea of what the graph "looks" like (short of using Graphviz).
The output on Coliru is:
1 <--> 2
2 <--> 1 3
3 <--> 2
I'm adding a 'finished' version of the dijkstra shortest paths search that computes the shortest path from S to T for archival purposes.
I'm sure there are better "boost" ways to do this, but it works on my end.
http://www.boost.org/doc/libs/1_58_0/libs/graph/doc/bundles.html was a really helpful link.
///
/// #file bgl_st_example.cpp
///
/// #brief bundled property example
///
/// #ref http://programmingexamples.net/wiki/CPP/Boost/BGL/BundledProperties
///
#include <iostream>
#include <vector>
#include <boost/config.hpp>
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <boost/property_map/property_map.hpp>
#include <boost/graph/graph_utility.hpp>
// Create a struct to hold properties for each vertex
typedef struct vertex_properties
{
std::string label;
int p1;
} vertex_properties_t;
// Create a struct to hold properties for each edge
typedef struct edge_properties
{
std::string label;
int p1;
int weight;
} edge_properties_t;
// Define the type of the graph
typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS, vertex_properties_t, edge_properties_t> graph_t;
typedef graph_t::vertex_descriptor vertex_descriptor_t;
typedef graph_t::edge_descriptor edge_descriptor_t;
typedef boost::property_map<graph_t, boost::vertex_index_t>::type index_map_t;
typedef boost::iterator_property_map<vertex_descriptor_t*, index_map_t*, vertex_descriptor_t, vertex_descriptor_t&> predecessor_map_t;
// The graph, with edge weights labeled.
//
// v1 --(1)-- v2
// | \_ |
// | \ |
// (1) (3) (2)
// | \_ |
// | \ |
// v4 --(1)-- v3
//
//
int main(int,char*[])
{
// Create a graph object
graph_t g;
// Add vertices
vertex_descriptor_t v1 = boost::add_vertex(g);
vertex_descriptor_t v2 = boost::add_vertex(g);
vertex_descriptor_t v3 = boost::add_vertex(g);
vertex_descriptor_t v4 = boost::add_vertex(g);
// Set vertex properties
g[v1].p1 = 1; g[v1].label = "v1";
g[v2].p1 = 2; g[v2].label = "v2";
g[v3].p1 = 3; g[v3].label = "v3";
g[v4].p1 = 4; g[v4].label = "v4";
// Add edges
std::pair<edge_descriptor_t, bool> e01 = boost::add_edge(v1, v2, g);
std::pair<edge_descriptor_t, bool> e02 = boost::add_edge(v2, v3, g);
std::pair<edge_descriptor_t, bool> e03 = boost::add_edge(v3, v4, g);
std::pair<edge_descriptor_t, bool> e04 = boost::add_edge(v4, v1, g);
std::pair<edge_descriptor_t, bool> e05 = boost::add_edge(v1, v3, g);
// Set edge properties
g[e01.first].p1 = 1; g[e01.first].weight = 1; g[e01.first].label = "v1-v2";
g[e02.first].p1 = 2; g[e02.first].weight = 2; g[e02.first].label = "v2-v3";
g[e03.first].p1 = 3; g[e03.first].weight = 1; g[e03.first].label = "v3-v4";
g[e04.first].p1 = 4; g[e04.first].weight = 1; g[e04.first].label = "v4-v1";
g[e05.first].p1 = 5; g[e05.first].weight = 3; g[e05.first].label = "v1-v3";
// Print out some useful information
std::cout << "Graph:" << std::endl;
boost::print_graph(g, boost::get(&vertex_properties_t::label,g));
std::cout << "num_verts: " << boost::num_vertices(g) << std::endl;
std::cout << "num_edges: " << boost::num_edges(g) << std::endl;
// BGL Dijkstra's Shortest Paths here...
std::vector<int> distances( boost::num_vertices(g));
std::vector<vertex_descriptor_t> predecessors(boost::num_vertices(g));
boost::dijkstra_shortest_paths(g, v1,
boost::weight_map(boost::get(&edge_properties_t::weight,g))
.distance_map(boost::make_iterator_property_map(distances.begin(), boost::get(boost::vertex_index,g)))
.predecessor_map(boost::make_iterator_property_map(predecessors.begin(), boost::get(boost::vertex_index,g)))
);
// Extract the shortest path from v1 to v3.
typedef std::vector<edge_descriptor_t> path_t;
path_t path;
vertex_descriptor_t v = v3;
for(vertex_descriptor_t u = predecessors[v]; u != v; v=u, u=predecessors[v])
{
std::pair<edge_descriptor_t,bool> edge_pair = boost::edge(u,v,g);
path.push_back( edge_pair.first );
}
std::cout << std::endl;
std::cout << "Shortest Path from v1 to v3:" << std::endl;
for(path_t::reverse_iterator riter = path.rbegin(); riter != path.rend(); ++riter)
{
vertex_descriptor_t u_tmp = boost::source(*riter, g);
vertex_descriptor_t v_tmp = boost::target(*riter, g);
edge_descriptor_t e_tmp = boost::edge(u_tmp, v_tmp, g).first;
std::cout << " " << g[u_tmp].label << " -> " << g[v_tmp].label << " (weight: " << g[e_tmp].weight << ")" << std::endl;
}
return 0;
}
Here's a CMakeLists.txt file that works for me:
cmake_minimum_required(VERSION 2.8)
project ( bgl_example )
find_package( Boost REQUIRED COMPONENTS )
include_directories( ${Boost_INCLUDE_DIR} )
add_executable( bgl_st_example bgl_st_example.cpp)
target_link_libraries( bgl_st_example ${Boost_LIBRARIES} )
The resulting output that I see:
Graph:
v1 <--> v2 v4 v3
v2 <--> v1 v3
v3 <--> v2 v4 v1
v4 <--> v3 v1
num_verts: 4
num_edges: 5
Shortest Path from v1 to v3:
v1 -> v4 (weight: 1)
v4 -> v3 (weight: 1)
Can someone explain to a Boost beginner like me what is a property map is in Boost?
I came across this when trying to use the BGL for calculating strong connected components.
I went through the documentation for the property map and graph module and still don't know what to make of it.
Take this code, for example:
what is the make_iterator_property_map function doing?
and what is the meaning of this code: get(vertex_index, G)?
#include <boost/config.hpp>
#include <vector>
#include <iostream>
#include <boost/graph/strong_components.hpp>
#include <boost/graph/adjacency_list.hpp>
int main()
{
using namespace boost;
typedef adjacency_list < vecS, vecS, directedS > Graph;
const int N = 6;
Graph G(N);
add_edge(0, 1, G);
add_edge(1, 1, G);
add_edge(1, 3, G);
add_edge(1, 4, G);
add_edge(3, 4, G);
add_edge(3, 0, G);
add_edge(4, 3, G);
add_edge(5, 2, G);
std::vector<int> c(N);
int num = strong_components
(G, make_iterator_property_map(c.begin(), get(vertex_index, G), c[0]));
std::cout << "Total number of components: " << num << std::endl;
std::vector < int >::iterator i;
for (i = c.begin(); i != c.end(); ++i)
std::cout << "Vertex " << i - c.begin()
<< " is in component " << *i << std::endl;
return EXIT_SUCCESS;
}
PropertyMaps at their core are an abstraction of data access. A problem that comes up very quickly in generic programming is: How do I get data associated with some object? It could be stored in the object itself, the object could be a pointer, it could be outside of the object in some mapping structure.
You can of course encapsulate data-access in a functor, but that becomes tedious very quickly and you look for a more narrow solution, the one chosen in Boost are PropertyMaps.
Remember this is just the concept. Concrete instances are for example an std::map (with some syntactic adaption), a function returning a member of the key (again, with some syntactic adaption).
Towards your edit: make_iterator_property_map builds an iterator_property_map. The first argument provides an iterator for a basis of offset calculations. The second argument is again a property_map to do the offset calculation. Together this provides a way to use an vertex_descriptor to write data to the vector based on the index of the vertex_descriptor.
I have a directed graph. New edges are added and removed dynamically at run time. If an edge that is about to be added to the graph creates a cycle, then that edge should not be added. How would I do this with BGL?
typedef boost::adjacency_list<
boost::listS, boost::vecS,
boost::directedS
> Graph;
int main(int, char*[]){
Graph G;
add_edge(0, 1, G);
add_edge(1, 2, G);
add_edge(2, 3, G);
add_edge(3, 0, G); //creates cycle, should abort.
}
You will want to run a breadth- or depth-first search before each addition, to see if a cycle will be formed. It will be formed if and only if you are adding an edge (u->v) and u is already reachable from v.
Here is a (hopefully working) code i stole from here
bool should_we_add(Graph &G) {
typedef Graph::vertex_descriptor Vertex;
std::vector<int> distances(N, 0); // where N is number of vertices in your graph
// The source vertex
Vertex s = boost::vertices(G)[u]; // this is your starting vertex
boost::breadth_first_search(G, s,
boost::visitor(boost::make_bfs_visitor(boost::record_distances(&distances[0], boost::on_tree_edge()))));
if (distances[v] != 0) {
// it is reachable, do NOT add the edge
cout << "Cycle!" << endl;
return false;
}
return true;
}
I edited evgeny's code because it wouldn't compile, and u and v were mixed up. The changes were not accepted, so here is the solution that works for my question.
bool should_we_add(Graph &G, int u, int v){
std::vector<int> distances(num_vertices(G), 0);
breadth_first_search(G, vertex(v, G),
visitor(make_bfs_visitor(record_distances(&distances[0], on_tree_edge()))));
if(distances[u] != 0){
// it is reachable, do NOT add the edge
cout << "Cycle!" << endl;
return false;
}
return true;
}