Accessing specific edges and modifying their property in boost::graph [duplicate] - c++

This is related to a question I had yesterday about accessing vertices using integer indices. That thread is here: Accessing specific vertices in boost::graph
The solution there indicated that using vecS as the type for vertices, it is indeed possible to access specific vertices using the integer index. I was wondering if there is a similar method provided by boost to access arbitrary edges efficiently using integer indices.
Attached is a code that depicts the former (valid access of vertices with integer indices) and accessing the edges based on the developer explicitly maintaining two arrays, from[] and to[], that store the source and the target, respectively of the edges.
The code creates the following graph:
#include <boost/config.hpp>
#include <iostream>
#include <fstream>
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/adjacency_list.hpp>
using namespace boost;
typedef adjacency_list_traits<vecS, vecS, directedS> Traits;
typedef adjacency_list<
vecS, vecS, directedS,
property<
vertex_name_t, std::string,
property<vertex_index_t, int,
property<vertex_color_t, boost::default_color_type,
property<vertex_distance_t, double,
property<vertex_predecessor_t, Traits::edge_descriptor> > > > >,
property<
edge_index_t, int,
property<edge_capacity_t, double,
property<edge_weight_t, double,
property<edge_residual_capacity_t, double,
property<edge_reverse_t, Traits::edge_descriptor> > > > > >
Graph;
int main() {
int nonodes = 4;
const int maxnoedges = 4;//I want to avoid using this.
Graph g(nonodes);
property_map<Graph, edge_index_t>::type E = get(edge_index, g);
int from[maxnoedges], to[maxnoedges];//I want to avoid using this.
// Create edges
Traits::edge_descriptor ed;
int eindex = 0;
ed = (add_edge(0, 1, g)).first;
from[eindex] = 0; to[eindex] = 1;//I want to avoid using this.
E[ed] = eindex++;
ed = (add_edge(0, 2, g)).first;
from[eindex] = 0; to[eindex] = 2;//I want to avoid using this.
E[ed] = eindex++;
ed = (add_edge(1, 3, g)).first;
from[eindex] = 1; to[eindex] = 3;//I want to avoid using this.
E[ed] = eindex++;
ed = (add_edge(2, 3, g)).first;
from[eindex] = 2; to[eindex] = 3;//I want to avoid using this.
E[ed] = eindex++;
graph_traits < Graph >::out_edge_iterator ei, e_end;
for (int vindex = 0; vindex < num_vertices(g); vindex++) {
printf("Number of outedges for vertex %d is %d\n", vindex, out_degree(vindex, g));
for (tie(ei, e_end) = out_edges(vindex, g); ei != e_end; ++ei)
printf("From %d to %d\n", source(*ei, g), target(*ei, g));
}
printf("Number of edges is %d\n", num_edges(g));
//Is there any efficient method boost provides
//in lieu of having to explicitly maintain from and to arrays
//on part of the developer?
for (int eindex = 0; eindex < num_edges(g); eindex++)
printf("Edge %d is from %d to %d\n", eindex, from[eindex], to[eindex]);
}
The code builds and compiles without error. The for loop with vindex works fine with out_edges and out_degree working fine taking as parameters integer indices.
Is there a way to do likewise for the next for loop that prints the edges using boost::graph data structures directly?
I looked at the following thread dealing with a similar question:
Boost graph library: Get edge_descriptor or access edge by index of type int
The suggested answer there was to use an unordered_map. Is there any tradeoff in using this as opposed to having the from[] and to[] arrays? Are there any other computationally efficient methods of accessing edges?

You can only do this if you
use a different graph model
an external edge index
Concepts
You could be interested in the AdjacencyMatrix concept. It doesn't exactly sport integral edge ids, but AdjacencyMatrix has lookup of edge by source/target vertices as well.
To get truly integral edge descriptors, you'd probably need write your own graph model class (modeling a set of existing BGL concepts). You might also be interested in grid_graph<> (which has a fixed set of numbered edges per vertex, where the vertices are a grid).
How to access edge_descriptor with given vertex_descriptor in boost::grid_graph - you could devise a "global" numering scheme and thus get linear lookup time
Adjacency List
Here's a modification from the previous answer showing an external index. It's akin to your solution. I chose bimap so at least you get the reverse lookup "automagically".
// Create edges
boost::bimaps::bimap<int, Graph::edge_descriptor> edge_idx;
auto new_edge_pair = [&,edge_id=0](int from, int to) mutable {
auto single = [&](int from, int to) {
auto d = add_edge(from, to, EdgeProperty { edge_id, 4 }, g).first;
if (!edge_idx.insert({edge_id++, d}).second)
throw std::invalid_argument("duplicate key");
return d;
};
auto a = single(from, to), b = single(to, from);
rev[a] = b;
rev[b] = a;
};
new_edge_pair(0, 1);
new_edge_pair(0, 2);
new_edge_pair(1, 3);
new_edge_pair(2, 3);
Now you can do the loop by edge id:
auto& by_id = edge_idx.left;
for (auto const& e : by_id) {
std::cout << "Edge #" << e.first << " is (" << source(e.second, g) << " -> " << target(e.second, g) << ")\n";
}
You can directly lookup an edge by it's id:
auto ed = by_id.at(random);
std::cout << "Random edge #" << random << " is (" << source(ed, g) << " -> " << target(ed, g) << ")\n";
The reverse lookup is a bit redundant, because you can do the same using BGL quite easily:
std::cout << "Reverse lookup: " << by_desc.at(ed) << "\n"; // reverse, though not very spectacular
std::cout << "Classic property lookup: " << g[ed].id << "\n"; // because it can be done using boost easily
Live On Coliru
#include <boost/graph/adjacency_list.hpp>
#include <boost/property_map/transform_value_property_map.hpp>
#include <boost/graph/boykov_kolmogorov_max_flow.hpp>
#include <functional>
#include <iostream>
#include <boost/bimap.hpp>
#include <random>
std::mt19937 prng { std::random_device{}() };
using namespace boost;
struct VertexProperty { std::string name; };
struct EdgeProperty {
int id;
double capacity, residual_capacity;
EdgeProperty(int id, double cap, double res = 0)
: id(id), capacity(cap), residual_capacity(res)
{ }
};
typedef adjacency_list<vecS, vecS, directedS, VertexProperty, EdgeProperty> Graph;
int main() {
int nonodes = 4;
Graph g(nonodes);
// reverse edge map
auto rev = make_vector_property_map<Graph::edge_descriptor>(get(&EdgeProperty::id, g));
// Create edges
boost::bimaps::bimap<int, Graph::edge_descriptor> edge_idx;
auto new_edge_pair = [&,edge_id=0](int from, int to) mutable {
auto single = [&](int from, int to) {
auto d = add_edge(from, to, EdgeProperty { edge_id, 4 }, g).first;
if (!edge_idx.insert({edge_id++, d}).second)
throw std::invalid_argument("duplicate key");
return d;
};
auto a = single(from, to), b = single(to, from);
rev[a] = b;
rev[b] = a;
};
new_edge_pair(0, 1);
new_edge_pair(0, 2);
new_edge_pair(1, 3);
new_edge_pair(2, 3);
// property maps
struct VertexEx {
default_color_type color;
double distance;
Graph::edge_descriptor pred;
};
auto idx = get(vertex_index, g);
auto vex = make_vector_property_map<VertexEx>(idx);
auto pred = make_transform_value_property_map(std::mem_fn(&VertexEx::pred), vex);
auto color = make_transform_value_property_map(std::mem_fn(&VertexEx::color), vex);
auto dist = make_transform_value_property_map(std::mem_fn(&VertexEx::distance), vex);
auto cap = get(&EdgeProperty::capacity, g);
auto rescap = get(&EdgeProperty::residual_capacity, g);
// algorithm
double flow = boykov_kolmogorov_max_flow(g, cap, rescap, rev, pred, color, dist, idx, 0, 3);
std::cout << "Flow: " << flow << "\n";
{
auto& by_id = edge_idx.left;
auto& by_desc = edge_idx.right;
for (auto const& e : edge_idx.left) {
std::cout << "Edge #" << e.first << " is (" << source(e.second, g) << " -> " << target(e.second, g) << ")\n";
}
int random = prng() % num_edges(g);
auto ed = by_id.at(random);
std::cout << "Random edge #" << random << " is (" << source(ed, g) << " -> " << target(ed, g) << ")\n";
std::cout << "Reverse lookup: " << by_desc.at(ed) << "\n"; // reverse, though not very spectacular
std::cout << "Classic property lookup: " << g[ed].id << "\n"; // because it can be done using boost easily
}
}
Printing
Flow: 8
Edge #0 is (0 -> 1)
Edge #1 is (1 -> 0)
Edge #2 is (0 -> 2)
Edge #3 is (2 -> 0)
Edge #4 is (1 -> 3)
Edge #5 is (3 -> 1)
Edge #6 is (2 -> 3)
Edge #7 is (3 -> 2)
Random edge #2 is (0 -> 2)
Reverse lookup: 2
Classic property lookup: 2
Adjacency Matrix
Keeps everything the same, except for changing the model:
#include <boost/graph/adjacency_matrix.hpp>
typedef adjacency_matrix<directedS, VertexProperty, EdgeProperty> Graph;
And now you get the added capability of lookup by vertices:
Live On Coliru
std::cout << "Finding (3, 1) results in Edge #" << by_desc.at(edge(3, 1, g).first) << "\n";
Prints
Finding (3, 1) results in Edge #5

Related

Boost library, how to get neighbouring nodes?

After generating a graph with n nodes, and adding the edges at random, how would I go around getting all the neighbours of a specific node. Is there a function similar to NetworkX's G.neighbors(i)?
This is what I've got so far, creating adjacency list
#include <iostream>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/connected_components.hpp>
using namespace boost;
using namespace std;
int main() {
int N = 10000;
struct status_t{
typedef vertex_property_tag kind;
};
typedef
property <status_t, string> status;
typedef
adjacency_list<vecS, vecS, undirectedS, status> MyGraph;
MyGraph g (N);
// add some random edges
add_edge(0, 1, g);
add_edge(100, 153, g);
add_edge(634, 12, g);
add_edge(94, 3, g);
property_map<MyGraph, status_t>::type status_map = get(status_t(), g);
for (int i = 0; i < 10; i++){
status_map[i] = "S";
}
return 0;
}
auto neighbours = boost::adjacent_vertices(94, g);
Print them like e.g.
for (auto vd : make_iterator_range(neighbours))
std::cout << "94 has adjacent vertex " << vd << "\n";
Prints
94 has adjacent vertex 93
94 has adjacent vertex 3
If you wanted outgoing edges only, that assumes directedS or bidirectionalS, in which case you can also do:
for (auto ed : make_iterator_range(boost::out_edges(94, g)))
std::cout << "outgoing: " << ed << "\n";
for (auto ed : make_iterator_range(boost::in_edges(94, g)))
std::cout << "incident: " << ed << "\n";
Live Demo
Live On Coliru
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/connected_components.hpp>
#include <iostream>
using namespace boost;
using namespace std;
int main() {
int N = 10000;
struct status_t { typedef vertex_property_tag kind; };
typedef property<status_t, string> status;
typedef adjacency_list<vecS, vecS, bidirectionalS, status> MyGraph;
MyGraph g(N);
// add some random edges
add_edge(0, 1, g);
add_edge(100, 153, g);
add_edge(634, 12, g);
add_edge(93, 94, g);
add_edge(94, 3, g);
property_map<MyGraph, status_t>::type status_map = get(status_t(), g);
for (int i = 0; i < 10; i++) {
status_map[i] = "S";
}
{
auto neighbours = boost::adjacent_vertices(94, g);
for (auto vd : make_iterator_range(neighbours))
std::cout << "94 has adjacent vertex " << vd << "\n";
// prints
// for undirected:
// 94 has adjacent vertex 93
// 94 has adjacent vertex 3
// for directed/bidirectionalS:
// 94 has adjacent vertex 3
}
{ // for bidirectionalS:
for (auto ed : make_iterator_range(boost::out_edges(94, g)))
std::cout << "outgoing: " << ed << "\n";
for (auto ed : make_iterator_range(boost::in_edges(94, g)))
std::cout << "incident: " << ed << "\n";
}
}
Printing
94 has adjacent vertex 3
outgoing: (94,3)
incident: (93,94)

Same weights for different boost graphs

I have just realized that I have not yet understood how to use boost graph library. I have this code:
#include <iostream>
#include <boost/graph/adjacency_list.hpp>
using namespace std;
using namespace boost;
typedef unsigned int WeightType;
typedef adjacency_list<listS, vecS, bidirectionalS,
no_property, property<edge_weight_t, WeightType>> Graph;
typedef graph_traits<Graph>::vertex_descriptor Vertex;
typedef graph_traits<Graph>::edge_descriptor Edge;
typedef property_map<Graph, edge_weight_t>::type WeightMap;
typedef property_map<Graph, edge_weight_t>::const_type ConstWeightMap;
const WeightType infinity = numeric_limits<WeightType>::max();
int main() {
Graph g(4);
Graph g2(4);
for (uint i = 0; i < 3; ++i) {
add_edge(i, i+1, i, g);
add_edge(i, i+1, i*10, g2);
}
WeightMap m = get(edge_weight, g);
WeightMap m2 = get(edge_weight, g2);
for (auto e : make_iterator_range(edges(g))) {
cout << m[e] << endl;
}
cout << endl;
for (auto e : make_iterator_range(edges(g))) {
cout << m2[e] << endl;
}
}
I would expect an output like: "0 1 2 , 0 10 20". But the output is "0 1 2, 0 1 2". Every graph have its weight property map, doesn't it? Where is my mistake?
You made a typo in the second for loop:
for (auto e : make_iterator_range(edges(g))) {
Should be:
for (auto e : make_iterator_range(edges(g2))) {
So you were printing the content of the first graph twice, instead of the first then the second.

Boost::graph Dijkstra and custom objects and properties

I want to use boost's dijkstra algorithm (since I'm using boost in other parts of my program). The problem I'm having is adding custom objects (I believe they are referred to as property) to the adjacency_list.
Essentially I have a custom edge class that maintains all sorts of information regarding the edge and the vertices that are connected through it. I want to store my custom data object with the edge properties that are required by the adjaceny_list
I've successfully implemented the toy example that boost provides. I've tried to use custom properties to no avail (boost example, boost properties). I'm fine with just encapsulating my VEdge data structure in a struct or something, I just need to be able to retrieve it. But I haven't been able to figure out how to include my custom data structure into the boost adjaceny_list structure.
In my case I have the following program:
Main.cpp:
#include <iostream>
#include <fstream>
#include "dijkstra.h"
#include <vector>
int
main(int, char *[])
{
// Generate the vector of edges from elsewhere in the program
std::vector<VEdge*> edges; //someclass.get_edges();
td* test = new td(edges);
test->run_d();
test->print_path();
return EXIT_SUCCESS;
}
Dijkstra.cpp:
#include <iostream>
#include <fstream>
#include "dijkstra.h"
using namespace boost;
td::td() {
kNumArcs = sizeof(kEdgeArray) / sizeof(Edge);
kNumNodes = 5;
}
td::td(std::vector<VEdge*> edges) {
// add edges to the edge property here
for(VEdge* e : edges) {
// for each edge, add to the kEdgeArray variable in some way
// The boost example forces the input to be an array of edge_property type.
// So here is where I will convert my raw VEdge data structure to
// the custom edge_property that I am struggling to understand how to create.
}
kNumArcs = sizeof(kEdgeArray) / sizeof(Edge);
kNumNodes = 5;
}
void td::run_d() {
kGraph = graph_t(kEdgeArray, kEdgeArray + kNumArcs, kWeights, kNumNodes);
kWeightMap = get(edge_weight, kGraph);
kP = std::vector<vertex_descriptor >(num_vertices(kGraph));
kD = std::vector<int>(num_vertices(kGraph));
kS = vertex(A, kGraph);
dijkstra_shortest_paths(kGraph, kS,
predecessor_map(boost::make_iterator_property_map(kP.begin(), get(boost::vertex_index, kGraph))).
distance_map(boost::make_iterator_property_map(kD.begin(), get(boost::vertex_index, kGraph))));
}
void td::print_path() {
std::cout << "distances and parents:" << std::endl;
graph_traits < graph_t >::vertex_iterator vi, vend;
for (boost::tie(vi, vend) = vertices(kGraph); vi != vend; ++vi) {
std::cout << "distance(" << kName[*vi] << ") = " << kD[*vi] << ", ";
std::cout << "parent(" << kName[*vi] << ") = " << kName[kP[*vi]] << std::
endl;
}
}
void td::generate_dot_file() {
std::cout << std::endl;
std::ofstream dot_file("figs/dijkstra-eg.dot");
dot_file << "digraph D {\n"
<< " rankdir=LR\n"
<< " size=\"4,3\"\n"
<< " ratio=\"fill\"\n"
<< " edge[style=\"bold\"]\n" << " node[shape=\"circle\"]\n";
graph_traits < graph_t >::edge_iterator ei, ei_end;
for (boost::tie(ei, ei_end) = edges(kGraph); ei != ei_end; ++ei) {
graph_traits < graph_t >::edge_descriptor e = *ei;
graph_traits < graph_t >::vertex_descriptor
u = source(e, kGraph), v = target(e, kGraph);
dot_file << kName[u] << " -> " << kName[v]
<< "[label=\"" << get(kWeightMap, e) << "\"";
if (kP[v] == u)
dot_file << ", color=\"black\"";
else
dot_file << ", color=\"grey\"";
dot_file << "]";
}
dot_file << "}";
}
Dijkstra.h:
#ifndef _TEMPD_H_
#define _TEMPD_H_
#pragma once
#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>
using namespace boost;
typedef adjacency_list < listS, vecS, directedS,
no_property, property < edge_weight_t, int > > graph_t;
typedef graph_traits < graph_t >::vertex_descriptor vertex_descriptor;
typedef std::pair<int, int> Edge;
struct VEdge{
// custom variables here
VNode start;
VNode end;
int weight;
int id;
// other irrelevant data pertinent to my program that must be preserved
};
struct VNode {
// custom variables here
int x;
int y;
int id;
// other irrelevant data pertinent to my program that must be preserved
}
enum nodes { A, B, C, D, E };
class td {
public:
td();
td(std::vector<VEdge*>);
~td();
void run_d();
void print_path();
void generate_dot_file();
private:
Edge kEdgeArray[9] = { Edge(A, C), Edge(B, B), Edge(B, D), Edge(B, E),
Edge(C, B), Edge(C, D), Edge(D, E), Edge(E, A), Edge(E, B)
};
char kName[5] = {'A','B','C','D','E'};
int kWeights[9] = { 1, 2, 1, 2, 7, 3, 1, 1, 1 };
int kNumArcs;
int kNumNodes;
vertex_descriptor kS;
graph_t kGraph;
std::vector<int> kD;
std::vector<vertex_descriptor> kP;
property_map<graph_t, edge_weight_t>::type kWeightMap;
};
#endif
I know my example is a bit contrived, but it communicates what I'm trying to accomplish. I know I need a custom data structure for my edge_descriptor which gets sent to the graph_t typedef.
So I'm looking to alter my Dijkstra.h file to look something like this:
struct vertex_label_t {vertex_property_tag kind;};
struct edge_label_t {edge_property_tag kind;};
typedef property <vertex_custom_t, VNode*>,
property <vertex_label_t, string>,
property <vertex_root_t, ing> > > vertex_p;
typedef property <edge_custom_t, VEdge*>,
property <edge_label_t, string > > edge_p;
typedef adjacency_list < listS, vecS, directedS,
vertex_p, edge_p > graph_t;
typedef graph_traits < graph_t >::vertex_descriptor vertex_descriptor;
Okay. You've come a long ways since https://stackoverflow.com/questions/28889423/boost-adjacency-list-swap-errors-when-using-boost-dijkstra; the sample is self-contained and can compile¹
I figured I could just connect some dots and hope this would be helpful.
1. Using VEdge
For the simplest option, I'd use Bundled Properties, and define VEdge as follows:
struct VEdge {
int id;
int source, target;
double weight;
// custom variables here
};
Now, we define the graph as
using graph_t = boost::adjacency_list<boost::listS, boost::vecS,
boost::directedS, boost::no_property, VEdge>;
using weight_map_t = boost::property_map<graph_t, double VEdge::*>::type;
As you can see the weight-map has a little more complicated type, as documented under Properties maps from bundled properties. You can get the actual map:
weight_map_t kWeightMap = boost::get(&VEdge::weight, kGraph);
Now, let's recreate the test data from your question in a vector of VEdge (A=0...E=4):
std::vector<VEdge> edges {
{ 2100, 0, 2, 1 },
{ 2101, 1, 1, 2 },
{ 2102, 1, 3, 1 },
{ 2103, 1, 4, 2 },
{ 2104, 2, 1, 7 },
{ 2105, 2, 3, 3 },
{ 2106, 3, 4, 1 },
{ 2107, 4, 0, 1 },
{ 2108, 4, 1, 1 },
};
test_dijkstra test(edges);
The constructor has a little bit of complication to find the number of vertices from just the edges. I used Boost Range algorithms to find the maximum vertex node id and pass that:
test_dijkstra::test_dijkstra(std::vector<VEdge> edges) {
using namespace boost::adaptors;
size_t max_node;
boost::partial_sort_copy(
edges | transformed([](VEdge const &e) -> size_t { return std::max(e.source, e.target); }),
boost::make_iterator_range(&max_node, &max_node + 1),
std::greater<size_t>());
auto e = edges | transformed([](VEdge const &ve) { return std::make_pair(ve.source, ve.target); });
kGraph = graph_t(e.begin(), e.end(), edges.begin(), max_node + 1);
}
Note how edges.begin() can be passed: it is not "forced to be a an array of edge_property type". An iterator will be fine.
Now the dijkstra needs to get the weight_map argument because it's no longer the default internal property:
void test_dijkstra::run_dijkstra() {
weight_map_t kWeightMap = boost::get(&VEdge::weight, kGraph);
vertex_descriptor kS = vertex(0, kGraph);
kP = std::vector<vertex_descriptor>(num_vertices(kGraph));
kD = std::vector<int>(num_vertices(kGraph));
dijkstra_shortest_paths(
kGraph, kS,
predecessor_map(boost::make_iterator_property_map(kP.begin(), get(boost::vertex_index, kGraph)))
.distance_map(boost::make_iterator_property_map(kD.begin(), get(boost::vertex_index, kGraph)))
.weight_map(kWeightMap));
}
For this sample, I translated A to 0 as the starting vertex. The result path is exactly the same as for the original²
Full Program
Live On Coliru
#include <boost/config.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <boost/graph/graph_traits.hpp>
#include <boost/property_map/property_map.hpp>
#include <boost/range/algorithm.hpp>
#include <boost/range/adaptors.hpp>
#include <fstream>
#include <iostream>
struct VEdge {
int id;
int source, target;
double weight;
// custom variables here
};
class test_dijkstra {
using graph_t = boost::adjacency_list<boost::listS, boost::vecS, boost::directedS, boost::no_property, VEdge>;
using vertex_descriptor = boost::graph_traits<graph_t>::vertex_descriptor;
using edge_descriptor = boost::graph_traits<graph_t>::edge_descriptor;
using weight_map_t = boost::property_map<graph_t, double VEdge::*>::type;
public:
test_dijkstra(std::vector<VEdge>);
~test_dijkstra() {}
void run_dijkstra();
void print_path();
void generate_dot_file();
private:
graph_t kGraph;
std::vector<int> kD;
std::vector<vertex_descriptor> kP;
};
test_dijkstra::test_dijkstra(std::vector<VEdge> edges) {
using namespace boost::adaptors;
size_t max_node;
boost::partial_sort_copy(
edges | transformed([](VEdge const &e) -> size_t { return std::max(e.source, e.target); }),
boost::make_iterator_range(&max_node, &max_node + 1),
std::greater<size_t>());
auto e = edges | transformed([](VEdge const &ve) { return std::make_pair(ve.source, ve.target); });
kGraph = graph_t(e.begin(), e.end(), edges.begin(), max_node + 1);
}
void test_dijkstra::run_dijkstra() {
weight_map_t kWeightMap = boost::get(&VEdge::weight, kGraph);
vertex_descriptor kS = vertex(0, kGraph);
kP = std::vector<vertex_descriptor>(num_vertices(kGraph));
kD = std::vector<int>(num_vertices(kGraph));
dijkstra_shortest_paths(
kGraph, kS,
predecessor_map(boost::make_iterator_property_map(kP.begin(), get(boost::vertex_index, kGraph)))
.distance_map(boost::make_iterator_property_map(kD.begin(), get(boost::vertex_index, kGraph)))
.weight_map(kWeightMap));
}
void test_dijkstra::print_path() {
std::cout << "distances and parents:" << std::endl;
boost::graph_traits<graph_t>::vertex_iterator vi, vend;
for (boost::tie(vi, vend) = vertices(kGraph); vi != vend; ++vi) {
std::cout << "distance(" << *vi << ") = " << kD[*vi] << ", ";
std::cout << "parent(" << *vi << ") = " << kP[*vi] << "\n";
}
}
void test_dijkstra::generate_dot_file() {
weight_map_t kWeightMap = boost::get(&VEdge::weight, kGraph);
std::ofstream dot_file("figs/dijkstra-eg.dot");
dot_file << "digraph D {\n"
<< " rankdir=LR\n"
<< " size=\"4,3\"\n"
<< " ratio=\"fill\"\n"
<< " edge[style=\"bold\"]\n"
<< " node[shape=\"circle\"]\n";
boost::graph_traits<graph_t>::edge_iterator ei, ei_end;
for (boost::tie(ei, ei_end) = edges(kGraph); ei != ei_end; ++ei) {
boost::graph_traits<graph_t>::edge_descriptor e = *ei;
boost::graph_traits<graph_t>::vertex_descriptor u = source(e, kGraph), v = target(e, kGraph);
dot_file << u << " -> " << v << "[label=\"" << get(kWeightMap, e) << "\"";
if (kP[v] == u)
dot_file << ", color=\"black\"";
else
dot_file << ", color=\"grey\"";
dot_file << "]";
}
dot_file << "}";
}
int main() {
std::vector<VEdge> edges {
{ 2100, 0, 2, 1 },
{ 2101, 1, 1, 2 },
{ 2102, 1, 3, 1 },
{ 2103, 1, 4, 2 },
{ 2104, 2, 1, 7 },
{ 2105, 2, 3, 3 },
{ 2106, 3, 4, 1 },
{ 2107, 4, 0, 1 },
{ 2108, 4, 1, 1 },
};
test_dijkstra test(edges);
test.run_dijkstra();
test.print_path();
test.generate_dot_file();
}
2. Using VEdge*
If you insist on using the pointers in the properties a few things become more complicated:
you'll need to manage the lifetime of the elements
you can't use the double VEdge::* weight_map_t. Instead, you'll need to adapt a custom propertymap for this:
auto kWeightMap = boost::make_transform_value_property_map(
[](VEdge* ve) { return ve->weight; },
boost::get(boost::edge_bundle, kGraph)
);
On the bright side, you can use the short-hand indexer notation to evaluate edge properties from an edge_descriptor as shown in generate_dot_file():
dot_file << u << " -> " << v << "[label=\"" << kGraph[e]->weight << "\"";
Of course this approach avoids copying the VEdge objects into the bundle, so it could be more efficient
Without further ado (and without bothering about the memory leaks):
Live On Coliru
#include <boost/config.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <boost/graph/graph_traits.hpp>
#include <boost/property_map/property_map.hpp>
#include <boost/range/algorithm.hpp>
#include <boost/range/adaptors.hpp>
#include <boost/property_map/transform_value_property_map.hpp>
#include <fstream>
#include <iostream>
struct VEdge {
int id;
int source, target;
double weight;
// custom variables here
};
class test_dijkstra {
using graph_t = boost::adjacency_list<boost::listS, boost::vecS, boost::directedS, boost::no_property, VEdge*>;
using vertex_descriptor = boost::graph_traits<graph_t>::vertex_descriptor;
using edge_descriptor = boost::graph_traits<graph_t>::edge_descriptor;
public:
test_dijkstra(std::vector<VEdge*>);
~test_dijkstra() {}
void run_dijkstra();
void print_path();
void generate_dot_file();
private:
graph_t kGraph;
std::vector<int> kD;
std::vector<vertex_descriptor> kP;
};
test_dijkstra::test_dijkstra(std::vector<VEdge*> edges) {
using namespace boost::adaptors;
size_t max_node;
boost::partial_sort_copy(
edges | transformed([](VEdge const* e) -> size_t { return std::max(e->source, e->target); }),
boost::make_iterator_range(&max_node, &max_node + 1),
std::greater<size_t>());
auto e = edges | transformed([](VEdge const *ve) { return std::make_pair(ve->source, ve->target); });
kGraph = graph_t(e.begin(), e.end(), edges.begin(), max_node + 1);
}
void test_dijkstra::run_dijkstra() {
auto kWeightMap = boost::make_transform_value_property_map(
[](VEdge* ve) { return ve->weight; },
boost::get(boost::edge_bundle, kGraph)
);
vertex_descriptor kS = vertex(0, kGraph);
kP = std::vector<vertex_descriptor>(num_vertices(kGraph));
kD = std::vector<int>(num_vertices(kGraph));
dijkstra_shortest_paths(
kGraph, kS,
predecessor_map(boost::make_iterator_property_map(kP.begin(), get(boost::vertex_index, kGraph)))
.distance_map(boost::make_iterator_property_map(kD.begin(), get(boost::vertex_index, kGraph)))
.weight_map(kWeightMap));
}
void test_dijkstra::print_path() {
std::cout << "distances and parents:" << std::endl;
boost::graph_traits<graph_t>::vertex_iterator vi, vend;
for (boost::tie(vi, vend) = vertices(kGraph); vi != vend; ++vi) {
std::cout << "distance(" << *vi << ") = " << kD[*vi] << ", ";
std::cout << "parent(" << *vi << ") = " << kP[*vi] << "\n";
}
}
void test_dijkstra::generate_dot_file() {
std::ofstream dot_file("figs/dijkstra-eg.dot");
dot_file << "digraph D {\n"
<< " rankdir=LR\n"
<< " size=\"4,3\"\n"
<< " ratio=\"fill\"\n"
<< " edge[style=\"bold\"]\n"
<< " node[shape=\"circle\"]\n";
boost::graph_traits<graph_t>::edge_iterator ei, ei_end;
for (boost::tie(ei, ei_end) = edges(kGraph); ei != ei_end; ++ei) {
boost::graph_traits<graph_t>::edge_descriptor e = *ei;
boost::graph_traits<graph_t>::vertex_descriptor u = source(e, kGraph), v = target(e, kGraph);
dot_file << u << " -> " << v << "[label=\"" << kGraph[e]->weight << "\"";
if (kP[v] == u)
dot_file << ", color=\"black\"";
else
dot_file << ", color=\"grey\"";
dot_file << "]";
}
dot_file << "}";
}
int main() {
std::vector<VEdge*> edges {
new VEdge { 2100, 0, 2, 1 },
new VEdge { 2101, 1, 1, 2 },
new VEdge { 2102, 1, 3, 1 },
new VEdge { 2103, 1, 4, 2 },
new VEdge { 2104, 2, 1, 7 },
new VEdge { 2105, 2, 3, 3 },
new VEdge { 2106, 3, 4, 1 },
new VEdge { 2107, 4, 0, 1 },
new VEdge { 2108, 4, 1, 1 },
};
test_dijkstra test(edges);
test.run_dijkstra();
test.print_path();
test.generate_dot_file();
}
¹ after swatting silly typos
² self-contained Live On Coliru

boost graph that doesn't allow circular references

I'm new to boost graphs and are researching the graph that best fits my need. I need to create a dependency graph and given a vertex, I need access to in and out edges. An adjacency_list with Directed=bidirectionalS is what I'm thinking.
But I need to make sure when I call add_edge and that causes a circular reference then it has to error out. I can't seem to find how to do this.
In general, there's only one way to discover whether a graph is a-cyclic: traverse all nodes.
So you'd just need to check whether the graph is still a-cyclic after adding each edge.
However, depending on how you are adding the nodes, you can optimize. If, e.g. you add edges by traversing nodes from a source in DFS order, you can just keep track of nodes "seen" in the current path and refuse to add an out edge to those.
Simplistic example based on topological_sort Live On Coliru:
#include <iostream> // for std::cout
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graphviz.hpp>
#include <boost/graph/topological_sort.hpp>
#include <boost/function_output_iterator.hpp>
using namespace boost;
int main()
{
srand(time(0));
typedef adjacency_list<vecS, vecS, bidirectionalS> Graph;
const int num_vertices = 10;
Graph g(num_vertices);
// add random edges to the graph object
for (size_t i = 0; i < 10; ++i)
{
auto f = rand()%num_vertices,
s = rand()%num_vertices;
add_edge(f, s, g);
try {
topological_sort(g, boost::make_function_output_iterator([](int){}));
} catch(not_a_dag const& e)
{
remove_edge(f, s, g);
std::cerr << "dropped edge: " << e.what() << "\n";
}
}
write_graphviz(std::cout, g);
}
Creates random DAGs like
In boost graph BidirectinalS indicates that the edge will be having soruce and target vertices both.
Here is the example for it:
#include <QtCore/QCoreApplication>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/subgraph.hpp>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
using namespace std;
using namespace boost;
typedef boost::subgraph<boost::adjacency_list< boost::listS,
boost::vecS,
boost::bidirectionalS,
boost::property<boost::vertex_index_t, int , property<boost::vertex_color_t, boost::default_color_type > > ,
boost::property<boost::edge_index_t,int, property<boost::edge_color_t , default_color_type> > > >
Graph;
const int num_vertices = 5;
Graph g(num_vertices);
add_edge(0, 1, g);
add_edge(1, 2, g);
add_edge(1, 3, g);
add_edge(2, 4, g);
add_edge(3, 4, g);
boost::graph_traits<Graph>::vertex_iterator VertexItr, VertexItr_end;
boost::graph_traits<Graph>::in_edge_iterator in, in_end;
boost::graph_traits<Graph>::out_edge_iterator out,out_end;
typedef boost::graph_traits < Graph >::adjacency_iterator adjacency_iterator;
// This loop is for getting in edges at vertex
cout<<"In Edge :- "<<endl;
for(boost::tie(VertexItr,VertexItr_end) = vertices(g); VertexItr != VertexItr_end; ++VertexItr) {
cout << *VertexItr << " <-- ";
for (boost::tie(in,in_end) = in_edges(*VertexItr, g); in != in_end; ++in)
cout << source(*in, g) << " ";
cout << endl;
}
// This loop is for getting out edges from vertex
cout<<endl<<"Out Edge :- "<<endl;
for(boost::tie(VertexItr,VertexItr_end) = vertices(g); VertexItr != VertexItr_end; ++VertexItr) {
cout<<*VertexItr<<"--->";
for (boost::tie(out,out_end) = out_edges(*VertexItr, g); out != out_end; ++out)
cout << target(*out, g) << " ";
cout << endl;
}
// This loop is for getting the neighbour vertices of vertex
typedef boost::property_map<Graph, boost::vertex_index_t>::type IndexMap;
IndexMap index = get(boost::vertex_index, g);
cout<<"Adjacent vertices"<<endl;
for(boost::tie(VertexItr,VertexItr_end) = vertices(g); VertexItr != VertexItr_end; ++VertexItr) {
cout<<*VertexItr<<"--->";
std::pair<adjacency_iterator, adjacency_iterator> neighbors =
boost::adjacent_vertices(vertex(*VertexItr,g), g);
for(; neighbors.first != neighbors.second; ++neighbors.first)
{
std::cout << index[*neighbors.first] << " ";
}
cout<<endl;
}
return a.exec();
}
I found this section on the boost documentation discussing how to detect dependencies:
http://www.boost.org/doc/libs/1_55_0/libs/graph/doc/file_dependency_example.html#sec:cycles
But for the adjacency_list the VertexList and EdgeList have to be of type vecS. There's discussion about this here:
How to print a boost graph in graphviz with one of the properties displayed?

Using boost connected components with cartesian points

I found http://www.boost.org/doc/libs/1_49_0/libs/graph/example/incremental_components.cpp and want to check if it will work for me. How to convert this example to cope with cartesian points with (x,y) or (x,y,z). I can't find such example in documentation of boost.
I see that i must redefine vertice in some way, so change in adjacency_list is needed. Tried to change vecS with Point definifion, but i think also some changes in add_edge functions are needed.
I made a couple minor changes to the example you pointed too. Specifically setting the 4th & fifth template parameters on the adjacency_list to be the a type containing any additional vertex and edge properties. See docs here: http://www.boost.org/doc/libs/1_48_0/libs/graph/doc/adjacency_list.html
struct point
{
int x;
int y;
int z;
};
typedef adjacency_list <vecS, vecS, undirectedS, point > Graph;
After nodes & vertices the additional point data can be set like this:
graph[0].x = 42;
And retrieved at the end after the components have been computed:
std::cout << child_index << " " << "x=" << graph[current_index].x << " ";
Full code:
//=======================================================================
// Copyright 1997, 1998, 1999, 2000 University of Notre Dame.
// Copyright 2009 Trustees of Indiana University.
// Authors: Andrew Lumsdaine, Lie-Quan Lee, Jeremy G. Siek, Michael Hansen
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================
#include <iostream>
#include <vector>
#include <boost/foreach.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graph_utility.hpp>
#include <boost/graph/incremental_components.hpp>
#include <boost/pending/disjoint_sets.hpp>
/*
This example shows how to use the disjoint set data structure
to compute the connected components of an undirected, changing
graph.
Sample output:
An undirected graph:
0 <--> 1 4
1 <--> 0 4
2 <--> 5
3 <-->
4 <--> 1 0
5 <--> 2
representative[0] = 1
representative[1] = 1
representative[2] = 5
representative[3] = 3
representative[4] = 1
representative[5] = 5
component 0 contains: 4 1 0
component 1 contains: 3
component 2 contains: 5 2
*/
using namespace boost;
struct point
{
point() : x(0), y(0), z(0) {}
int x;
int y;
int z;
};
int main(int argc, char* argv[])
{
typedef adjacency_list <vecS, vecS, undirectedS, point > Graph;
typedef graph_traits<Graph>::vertex_descriptor Vertex;
typedef graph_traits<Graph>::vertices_size_type VertexIndex;
const int VERTEX_COUNT = 6;
Graph graph(VERTEX_COUNT);
std::vector<VertexIndex> rank(num_vertices(graph));
std::vector<Vertex> parent(num_vertices(graph));
typedef VertexIndex* Rank;
typedef Vertex* Parent;
disjoint_sets<Rank, Parent> ds(&rank[0], &parent[0]);
initialize_incremental_components(graph, ds);
incremental_components(graph, ds);
graph_traits<Graph>::edge_descriptor edge;
bool flag;
boost::tie(edge, flag) = add_edge(0, 1, graph);
ds.union_set(0,1);
boost::tie(edge, flag) = add_edge(1, 4, graph);
ds.union_set(1,4);
boost::tie(edge, flag) = add_edge(4, 0, graph);
ds.union_set(4,0);
boost::tie(edge, flag) = add_edge(2, 5, graph);
ds.union_set(2,5);
graph[0].x = 42;
std::cout << "An undirected graph:" << std::endl;
print_graph(graph, get(boost::vertex_index, graph));
std::cout << std::endl;
BOOST_FOREACH(Vertex current_vertex, vertices(graph)) {
std::cout << "representative[" << current_vertex << "] = " <<
ds.find_set(current_vertex) << std::endl;
}
std::cout << std::endl;
typedef component_index<VertexIndex> Components;
// NOTE: Because we're using vecS for the graph type, we're
// effectively using identity_property_map for a vertex index map.
// If we were to use listS instead, the index map would need to be
// explicitly passed to the component_index constructor.
Components components(parent.begin(), parent.end());
// Iterate through the component indices
BOOST_FOREACH(VertexIndex current_index, components) {
std::cout << "component " << current_index << " contains: ";
// Iterate through the child vertex indices for [current_index]
BOOST_FOREACH(VertexIndex child_index,
components[current_index])
{
std::cout << child_index
<< " {" << graph[child_index].x
<< "," << graph[child_index].y
<< "," << graph[child_index].z << "} ";
}
std::cout << std::endl;
}
return (0);
}