I am using Boost C++ Library to build a adjacency list to represent an undirected graph. Each edge on the graph is associated with respective weights and I want to check if the weight is greater than some threshold than I merge the 2 vertices together.
How I merge:
For the vertex to merge, gather all the edges to and from this vertex and direct the edges to the new vertex
Clear the merging vertex
Remove the vertex
My Problem:
I use a simple program to first construct the algorithm before I use it for purpose. In this program I am using simple family tree method to perform the above steps. When I remove the vertex using the function remove_vertex(vertex, Graph) I get a segmentation fault.
I cannot figure out if once the vertex is removed, does the graph automatically updates its structure?
My C++ code is as follows:
#include <boost/graph/adjacency_list.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/config.hpp>
#include <iostream>
#include <vector>
#include <string>
using namespace std;
typedef boost::property<boost::vertex_index_t, int> vertex_property;
typedef boost::property<boost::edge_weight_t, float> edge_property;
typedef typename boost::adjacency_list <boost::vecS,
boost::vecS,
boost::undirectedS,
vertex_property,
edge_property> Graph;
void boostSampleGraph() {
enum family {
Jeanie, Debbie, Rick, John, Amanda, Margaret, Benjamin, N };
const char *name[] = { "Jeanie", "Debbie", "Rick", "John", "Amanda",
"Margaret", "Benjamin", "N"
};
/* actual graph structure */
Graph graph;
/* add vertices to the graph */
add_vertex(Jeanie, graph);
add_vertex(Debbie, graph);
add_vertex(Rick, graph);
add_vertex(John, graph);
add_vertex(Amanda, graph);
add_vertex(Margaret, graph);
add_vertex(Benjamin, graph);
// add_vertex(N, graph);
/* add edges to the vertices in the graph*/
add_edge(Jeanie, Debbie, edge_property(0.5f), graph);
add_edge(Jeanie, Rick, edge_property(0.2f), graph);
add_edge(Jeanie, John, edge_property(0.1f), graph);
add_edge(Debbie, Amanda, edge_property(0.3f), graph);
add_edge(Rick, Margaret, edge_property(0.4f), graph);
add_edge(John, Benjamin, edge_property(0.6f), graph);
// add_edge(Benjamin, Benjamin, edge_property(0.7f), graph);
/* vertex iterator */
boost::graph_traits<Graph>::vertex_iterator i, end;
typedef typename boost::graph_traits<
Graph>::adjacency_iterator AdjacencyIterator;
/* gets the graph vertex index */
typedef typename boost::property_map
<Graph, boost::vertex_index_t >::type IndexMap;
IndexMap index_map = get(boost::vertex_index, graph);
/* container to hold the edge descriptor info */
typedef typename boost::graph_traits<
Graph>::edge_descriptor EdgeDescriptor;
EdgeDescriptor e_descriptor;
typedef typename boost::property_map<Graph, boost::edge_weight_t
>::type EdgePropertyAccess;
EdgePropertyAccess edge_weights = get(boost::edge_weight, graph);
typedef typename boost::property_traits<boost::property_map<
Graph, boost::edge_weight_t>::const_type>::value_type EdgeValue;
float edge_size = num_vertices(graph);
std::cout << "# of Edges: " << edge_size << std::endl;
/* iterator throught the graph */
for (tie(i, end) = vertices(graph); i != end; ++i) {
std::cout << name[get(index_map, *i)];
AdjacencyIterator ai, a_end;
tie(ai, a_end) = adjacent_vertices(*i, graph);
if (ai == a_end) {
std::cout << " has no children";
} else {
std::cout << " is the parent of ";
}
for (; ai != a_end; ++ai) {
AdjacencyIterator tmp;
bool found;
tie(e_descriptor, found) = edge(*i, *ai, graph);
float weights_ = 0.0f;
if (found) {
EdgeValue edge_val = boost::get(
boost::edge_weight, graph, e_descriptor);
weights_ = edge_val;
if (weights_ > 0.3f) {
// - remove and merge
AdjacencyIterator aI, aEnd;
tie(aI, aEnd) = adjacent_vertices(*ai, graph);
for (; aI != aEnd; aI++) {
EdgeDescriptor ed;
bool located;
tie(ed, located) = edge(*aI, *ai, graph);
if (located && *aI != *i) {
add_edge(
get(index_map, *i), get(index_map, *aI), graph);
}
std::cout << "\n DEBUG: " << *i << " "
<< *ai << " "
<< *aI << " ";
}
std::cout << std::endl;
clear_vertex(*ai, graph);
remove_vertex(*ai, graph);
// std::cout << "\nGraph Size: " <<
// num_vertices(graph) << std::endl;
}
}
// ai = tmp;
std::cout << name[get(index_map, *ai)];
if (boost::next(ai) != a_end) {
std::cout << ", ";
}
}
std::cout << std::endl << std::endl;
}
std::cout << "\nGraph Size: " << num_vertices(graph) << std::endl;
}
int main(int argc, const char *argv[]) {
boostSampleGraph();
return 0;
}
Could I get some help and ideas on where did I got this wrong.
I don't know what you're actually trying to achieve with the algorithm shown in the OP.
Here's, however, one that simplifies the code considerably, so that at least it works safely:
uses Vertex bundled property type for vertex (id, name)
uses ranged for loops where possible (see mir, shorthand to create a boost::iterator_range from a std::pair of iterators)
the code is written in container-selection independent way (so it works just the same when you replace vecS by listS in the Graph type declaration)
it uses out_edges instead of adjacent_vertices to benefit more from the AdjacencyGraph concept, and avoid reverse-lookup of edge-descriptors by (source, target) vertices
most importantly, it uses a std::set<vertex_descriptor> of vertices that have been "removed". The actual removal happens later so we don't get undefined behaviour while iterating a changing container
runs cleanly under valgrind
Live On Coliru
#include <boost/graph/adjacency_list.hpp>
#include <iostream>
struct Vertex {
int id;
const char* name;
Vertex(int i = -1, const char* name = "default") : id(i), name(name) {}
};
template <typename It> boost::iterator_range<It> mir(std::pair<It, It> const& p) {
return boost::make_iterator_range(p.first, p.second);
}
template <typename It> boost::iterator_range<It> mir(It b, It e) {
return boost::make_iterator_range(b, e);
}
typedef typename boost::adjacency_list<
boost::vecS, boost::vecS,
boost::undirectedS,
Vertex, // bundled properties (id, name)
boost::property<boost::edge_weight_t, float> // interior property
> Graph;
Graph make() {
Graph graph;
auto Jeanie = add_vertex(Vertex { 0, "Jeanie" }, graph);
auto Debbie = add_vertex(Vertex { 1, "Debbie" }, graph);
auto Rick = add_vertex(Vertex { 2, "Rick" }, graph);
auto John = add_vertex(Vertex { 3, "John" }, graph);
auto Amanda = add_vertex(Vertex { 4, "Amanda" }, graph);
auto Margaret = add_vertex(Vertex { 5, "Margaret" }, graph);
auto Benjamin = add_vertex(Vertex { 6, "Benjamin" }, graph);
add_edge(Jeanie, Debbie, 0.5f, graph);
add_edge(Jeanie, Rick, 0.2f, graph);
add_edge(Jeanie, John, 0.1f, graph);
add_edge(Debbie, Amanda, 0.3f, graph);
add_edge(Rick, Margaret, 0.4f, graph);
add_edge(John, Benjamin, 0.6f, graph);
return graph;
}
Graph reduce(Graph graph) {
/* vertex iterator */
using vertex_descriptor = boost::graph_traits<Graph>::vertex_descriptor;
std::cout << "# of vertices: " << num_vertices(graph) << "\n";
std::cout << "# of edges: " << num_edges(graph) << "\n";
std::set<vertex_descriptor> to_remove;
/* iterator throught the graph */
for (auto self : mir(vertices(graph)))
{
std::cout << graph[self].name << (boost::empty(mir(out_edges(self, graph)))? " has no children " : " is the parent of ");
for(auto edge : mir(out_edges(self, graph))) {
auto weight = boost::get(boost::edge_weight, graph, edge);
auto mid_point = target(edge, graph);
if (to_remove.count(mid_point)) // already elided
break;
if (weight > 0.3f) {
std::set<vertex_descriptor> traversed;
for (auto hop : mir(out_edges(mid_point, graph))) {
auto hop_target = target(hop, graph);
if (hop_target != self)
add_edge(self, hop_target, graph);
std::cout << "\n DEBUG: " << graph[self].name << " " << graph[mid_point].name << " " << graph[hop_target].name << " ";
}
std::cout << "\n";
clear_vertex(mid_point, graph);
to_remove.insert(mid_point);
}
std::cout << graph[mid_point].name;
}
std::cout << "\n\n";
}
for(auto vd : to_remove)
{
clear_vertex(vd, graph);
remove_vertex(vd, graph);
}
std::cout << "# of vertices: " << num_vertices(graph) << "\n";
std::cout << "# of edges: " << num_edges(graph) << "\n";
return graph;
}
void save(Graph const& g, const char* fname);
int main() {
auto const g = make();
auto const h = reduce(g);
save(g, "before.dot");
save(h, "after.dot");
}
#include <boost/graph/graphviz.hpp>
#include <boost/property_map/property_map.hpp>
#include <boost/property_map/function_property_map.hpp>
#include <boost/property_map/transform_value_property_map.hpp>
#include <boost/format.hpp>
#include <fstream>
void save(Graph const& g, const char* fname) {
std::ofstream ofs(fname);
using namespace boost;
write_graphviz(
ofs,
g,
make_label_writer(make_function_property_map<Graph::vertex_descriptor, std::string>([&] (Graph::vertex_descriptor v){ return g[v].name; })),
make_label_writer(make_transform_value_property_map([](float v){return boost::format("%1.1f") % v;}, boost::get(edge_weight, g)))
);
}
Prints
# of vertices: 7
# of edges: 6
Jeanie is the parent of
DEBUG: Jeanie Debbie Jeanie
DEBUG: Jeanie Debbie Amanda
DebbieJohnAmanda
Debbie has no children
Rick is the parent of Jeanie
DEBUG: Rick Margaret Rick
Margaret
John is the parent of Jeanie
DEBUG: John Benjamin John
Benjamin
Amanda is the parent of Jeanie
Margaret has no children
Benjamin has no children
# of vertices: 4
# of edges: 3
Graph before:
Graph after:
Related
I have a question regarding the edge weights when using Dijkstra's algorithm in Boost. The problem I am facing is that when the edge weights are large, I do not get a solution from Dijkstra's algorithm.
Let's say I have an adjacency list with bundled properties. My vertices are of type VertexType and edges are of type EdgeType. Here, VertexType resembles a person for a simple example. VertexType::pred is the predecessor when used with Dijkstra's algorithm.
struct VertexType
{
std::string name;
int age;
int pred;
};
struct EdgeType
{
double weight;
};
Below is a simple example.
void edgeWeightProblem()
{
// Graph and vertex descriptor typedefs.
typedef boost::adjacency_list<boost::vecS, boost::vecS,
boost::directedS,
VertexType,
EdgeType
> GraphType;
typedef boost::graph_traits<GraphType>::vertex_descriptor VertexDescriptor;
// Create graph and vertices vector.
GraphType G;
std::vector<VertexDescriptor> graphVertices;
// Add vertices.
graphVertices.push_back(add_vertex({"Tom", 23}, G));
graphVertices.push_back(add_vertex({"Frank", 25}, G));
graphVertices.push_back(add_vertex({"John", 42}, G));
graphVertices.push_back(add_vertex({"Emily", 22}, G));
// Add edges.
add_edge(graphVertices[0], graphVertices[1], {.2564}, G);
add_edge(graphVertices[0], graphVertices[2], {.3572}, G);
add_edge(graphVertices[1], graphVertices[3], {.1246}, G);
add_edge(graphVertices[2], graphVertices[3], {.5361}, G);
// Dijkstra shortest path.
dijkstra_shortest_paths(G, graphVertices.front(),
predecessor_map(boost::get(&VertexType::pred, G))
.distance_map(boost::get(&VertexType::pred, G))
.weight_map(boost::get(&EdgeType::weight, G)));
// Debug pred.
std::vector<VertexType> vec;
for (const VertexDescriptor& vd : graphVertices)
{
vec.push_back(G[vd]);
}
// Extract the shortest path.
std::vector<VertexDescriptor> shortestPath;
VertexDescriptor currentVertex = graphVertices.back();
while (currentVertex != graphVertices.front())
{
shortestPath.push_back(currentVertex);
currentVertex = G[currentVertex].pred;
}
shortestPath.push_back(currentVertex);
std::reverse(shortestPath.begin(), shortestPath.end());
// Display graph.
std::cout << "\nGraph Display: \n";
boost::print_graph(G);
std::cout << "\n";
// Print the shortest path.
std::cout << "Shortest Path: \n";
for (const auto& node : shortestPath)
{
std::cout << node << " -> ";
}
std::cout << "\n";
}
As expected, I get the following output:
Graph Display:
0 --> 1 2
1 --> 3
2 --> 3
3 -->
Shortest Path:
0 -> 2 -> 3 ->
But, if I remove the decimal of the edge weights, (ex: .2564 -> 2564), I will not be able to find the shortest path since the predecessor of each vertex will be itself. This also result in an infinite loop as a side affect for this example.
You can see this by placing a breakpoint after the "Debug pred" section and inspecting "vec". What is going on here? I assume this is a misunderstanding of Boost as I am quite new to it.
I have tried to play around with the weights, but it seems once the weights are larger, this issue happens. These weights are not so large that overflow should be a consideration, so I am quite confused as to what is happening.
You're passing pred as both the predecessor map and the distance map. One will overwrite the other at unspecified points during algorithm execution. The result is unspecified at best, undefined if you're lucky.
Fixed that and simplified some of the code, now the result is the same for both weight inputs:
Live On Coliru
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/dag_shortest_paths.hpp>
#include <boost/graph/graph_utility.hpp>
#include <iomanip>
#include <iostream>
struct VertexType {
std::string name;
int age;
size_t pred;
friend std::ostream& operator<<(std::ostream& os, VertexType const& vt) {
return os << "{" << std::quoted(vt.name) << ", " << vt.age << ", pred:" << vt.pred << "}";
}
};
struct EdgeType {
double weight;
};
int main() {
// Graph and vertex descriptor typedefs.
using Graph = boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS, VertexType, EdgeType>;
using V = Graph::vertex_descriptor;
// Create graph and vertices vector.
Graph g;
// Add vertices.
V v0 = add_vertex({"Tom", 23, 0}, g);
V v1 = add_vertex({"Frank", 25, 0}, g);
V v2 = add_vertex({"John", 42, 0}, g);
V v3 = add_vertex({"Emily", 22, 0}, g);
// Add edges.
add_edge(v0, v1, {2564}, g);
add_edge(v0, v2, {3572}, g);
add_edge(v1, v3, {1246}, g);
add_edge(v2, v3, {5361}, g);
auto bundle = get(boost::vertex_bundle, g);
auto pred = get(&VertexType::pred, g);
auto weight = get(&EdgeType::weight, g);
auto distances = std::vector<double>(num_vertices(g));
// Dijkstra shortest path.
auto src = v0;
dijkstra_shortest_paths(g, src,
predecessor_map(pred)
.distance_map(distances.data())
.weight_map(weight));
#if 0
// Debug pred.
std::vector<VertexType> vertex_properties;
for (auto vd : boost::make_iterator_range(vertices(g))) {
vertex_properties.push_back(g[vd]);
}
#endif
// Extract the shortest path.
std::deque<V> path;
for (V cur = num_vertices(g) - 1;; cur = pred[cur]) {
path.push_front(cur);
if (cur == src || cur == pred[cur])
break;
}
// Display graph.
print_graph(g, bundle, std::cout << "Graph Display: \n");
// Print the shortest path.
std::cout << "\nShortest Path: ";
for (const auto& vd : path) std::cout << (vd == src ? "" : " -> ") << vd;
std::cout << "\nWhich is: ";
for (const auto& vd : path) std::cout << (vd == src ? "" : " -> ") << g[vd];
std::cout << "\n";
}
Prints
Graph Display:
{"Tom", 23, pred:0} --> {"Frank", 25, pred:0} {"John", 42, pred:0}
{"Frank", 25, pred:0} --> {"Emily", 22, pred:1}
{"John", 42, pred:0} --> {"Emily", 22, pred:1}
{"Emily", 22, pred:1} -->
Shortest Path: 0 -> 1 -> 3
Which is: {"Tom", 23, pred:0} -> {"Frank", 25, pred:0} -> {"Emily", 22, pred:1}
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)
I'm trying to update the value of an edge in my Graph, however, when i update the value using the following property map:
property_map<Graph, edge_weight_t>::type weight = get(edge_weight, g);
weight[*edgeIndex] = new_value;
Its update the value, however when i try to get the shortest path using dijkstra algorithm, the algorithm get the old_value of the weight. But when i list all edges and weight in the Graph, the updated weight is with the new_value.
i tried to use the boost::put(); however occurs the same problem. It only works when the new_value is smaller than the old_value
Could somebody help me?
in other words, i just want to set name and weight to the edges and update this weights on the fly.
this is my code:
#include <iostream> // for std::cout
#include <utility> // for std::pair
#include <algorithm> // for std::for_each
#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/config.hpp>
#include <string>
#include <string.h>
#include <list>
#include <fstream>
using namespace boost;
//define de Edges and Vertex property
typedef property<edge_weight_t, float, property<edge_name_t, std::string> > EdgeWeightProperty;
typedef property<vertex_name_t, std::string> VertexProperty;
//define the type of the Graph
typedef adjacency_list<vecS, vecS, undirectedS, VertexProperty, EdgeWeightProperty> Graph;
int main(){
typedef float Weight;
//Graph instance
Graph g;
//property accessors
property_map<Graph, vertex_name_t>::type node = get(vertex_name, g);
property_map<Graph, edge_weight_t>::type weight = get(edge_weight, g);
property_map<Graph, edge_name_t>::type edge = get(edge_name, g);
// Create the vertices
typedef graph_traits<Graph>::vertex_descriptor Vertex;
std::vector<Vertex> vertex_list;
typedef graph_traits<Graph>::edge_descriptor Edge;
std::vector<Edge> edges_list;
typedef boost::property_map <Graph, vertex_index_t >::type IndexMap;
typedef boost::property_map <Graph, vertex_name_t >::type NameMap;
typedef boost::iterator_property_map <Vertex*, IndexMap, Vertex, Vertex& > PredecessorMap;
typedef boost::iterator_property_map < Weight*, IndexMap, Weight, Weight& > DistanceMap;
std::string vertex_name;
std::ifstream vertex_file;
vertex_file.open("vertex_file.txt");
if (vertex_file.is_open()){
for(int index = 0; vertex_file.peek() != EOF; index++){
vertex_file >> vertex_name;
vertex_list.push_back(add_vertex(g));
node[vertex_list.at(index)] = vertex_name;
}
vertex_file.close();
}
std::string edge_name, from, to;
std::ifstream edges_file;
edges_file.open("edge.txt");
int index_from, index_to;
if(edges_file.is_open()){
for(int index=0; edges_file.peek() != EOF; index++){
edges_file >> edge_name;
edges_file >> from;
edges_file >> to;
for(index_from=0; index_from < vertex_list.size(); index_from++){
if(strcmp(from.c_str(), node[vertex_list.at(index_from)].c_str()) == 0){
break;
}
}
for(index_to=0; index_to < vertex_list.size(); index_to++){
if(strcmp(to.c_str(), node[vertex_list.at(index_to)].c_str()) == 0){
break;
}
}
edges_list.push_back((add_edge(vertex_list.at(index_from), vertex_list.at(index_to), g)).first);
edge[edges_list.at(index)] = edge_name;
//std::cout << edges_list.at(index) << std::endl;
weight[edges_list.at(index)] = 10;
}
}
typedef graph_traits<Graph>::edge_iterator edge_iter;
std::pair<edge_iter, edge_iter> ep;
edge_iter ei, ei_end;
std::string teste = "0/0to0/1";
for (tie(ei, ei_end) = edges(g); ei != ei_end; ++ei){
//std::cout << edge[*ei] << std::endl;
if(strcmp(edge[*ei].c_str(), teste.c_str()) == 0){
weight[*ei] = 700;
}
}
std::vector<Vertex> predecessors(boost::num_vertices(g)); // To store parents
std::vector<Weight> distances(boost::num_vertices(g)); // To store distances
IndexMap indexMap = boost::get(boost::vertex_index, g);
PredecessorMap predecessorMap(&predecessors[0], indexMap);
DistanceMap distanceMap(&distances[0], indexMap);
boost::dijkstra_shortest_paths(g, vertex_list.at(0), boost::distance_map(distanceMap).predecessor_map(predecessorMap));
std::cout << "distances and parents:" << std::endl;
NameMap nameMap = boost::get(boost::vertex_name, g);
std::cout << std::endl;
typedef std::vector<Graph::edge_descriptor> PathType;
PathType path;
Vertex v = vertex_list.at(1); // We want to start at the destination and work our way back to the source
for(Vertex u = predecessorMap[v]; // Start by setting 'u' to the destintaion node's predecessor
u != v; // Keep tracking the path until we get to the source
v = u, u = predecessorMap[v]) // Set the current vertex to the current predecessor, and the predecessor to one level up
{
std::pair<Graph::edge_descriptor, bool> edgePair = boost::edge(u, v, g);
Graph::edge_descriptor edge = edgePair.first;
path.push_back( edge );
}
// Write shortest path
//std::cout << "Shortest path from v0 to v3:" << std::endl;
float totalDistance = 0;
for(PathType::reverse_iterator pathIterator = path.rbegin(); pathIterator != path.rend(); ++pathIterator)
{
std::cout << nameMap[boost::source(*pathIterator, g)] << " to " << nameMap[boost::target(*pathIterator, g)]
<< " = " << boost::get( boost::edge_weight, g, *pathIterator ) << std::endl;
}
std::cout << std::endl;
std::cout << "Distance: " << distanceMap[vertex_list.at(1)] << std::endl;
return 0;
}
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
I have problems getting to compile the BFS of a very simple graph. Whatever I do I get various compiler messages about unmatched method calls (I've tried boost::visitor and extending boost::default_bfs_visitor etc.)
#include <stdint.h>
#include <iostream>
#include <vector>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/breadth_first_search.hpp>
int main() {
typedef boost::adjacency_list<boost::vecS, boost::hash_setS, boost::undirectedS, uint32_t, uint32_t, boost::no_property> graph_t;
graph_t graph(4);
graph_t::vertex_descriptor a = boost::vertex(0, graph);
graph_t::vertex_descriptor b = boost::vertex(1, graph);
graph_t::vertex_descriptor c = boost::vertex(2, graph);
graph_t::vertex_descriptor d = boost::vertex(3, graph);
graph[a] = 0;
graph[b] = 1;
graph[c] = 2;
graph[d] = 3;
std::pair<graph_t::edge_descriptor, bool> result = boost::add_edge(a, b, 0, graph);
result = boost::add_edge(a, c, 1, graph);
result = boost::add_edge(c, b, 2, graph);
class {
public:
void initialize_vertex(const graph_t::vertex_descriptor &s, graph_t &g) {
std::cout << "Initialize: " << g[s] << std::endl;
}
void discover_vertex(const graph_t::vertex_descriptor &s, graph_t &g) {
std::cout << "Discover: " << g[s] << std::endl;
}
void examine_vertex(const graph_t::vertex_descriptor &s, graph_t &g) {
std::cout << "Examine vertex: " << g[s] << std::endl;
}
void examine_edge(const graph_t::edge_descriptor &e, graph_t &g) {
std::cout << "Examine edge: " << g[e] << std::endl;
}
void tree_edge(const graph_t::edge_descriptor &e, graph_t &g) {
std::cout << "Tree edge: " << g[e] << std::endl;
}
void non_tree_edge(const graph_t::edge_descriptor &e, graph_t &g) {
std::cout << "Non-Tree edge: " << g[e] << std::endl;
}
void gray_target(const graph_t::edge_descriptor &e, graph_t &g) {
std::cout << "Gray target: " << g[e] << std::endl;
}
void black_target(const graph_t::edge_descriptor &e, graph_t &g) {
std::cout << "Black target: " << g[e] << std::endl;
}
void finish_vertex(const graph_t::vertex_descriptor &s, graph_t &g) {
std::cout << "Finish vertex: " << g[s] << std::endl;
}
} bfs_visitor;
boost::breadth_first_search(graph, a, bfs_visitor);
return 0;
}
How to visit the graph using bfs_visitor?
PS. I've seen and compiled "How to create a C++ Boost undirected graph and traverse it in depth first search (DFS) order?" but it didn't help.
You can see here a list of the overloads of breadth_first_search. If you don't want to specify every one of the parameters you need to use the named-parameter version. It would look like this:
breadth_first_search(graph, a, boost::visitor(bfs_visitor));
This would work as is if you had used vecS as your VertexList storage in your graph definition or if you had constructed and initialized an internal vertex_index property map. Since you are using hash_setS you need to change the invocation to:
breath_first_search(graph, a, boost::visitor(bfs_visitor).vertex_index_map(my_index_map));
You are already using an index map in your uint32_t bundled property. You can use get(boost::vertex_bundle, graph) to access it.
There was also a problem with your visitor. You should derive it from boost::default_bfs_visitor and the graph_t parameter of your member functions needs to be const qualified.
Full code:
#include <stdint.h>
#include <iostream>
#include <vector>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/breadth_first_search.hpp>
typedef boost::adjacency_list<boost::vecS, boost::hash_setS, boost::undirectedS, uint32_t, uint32_t, boost::no_property> graph_t;
struct my_visitor : boost::default_bfs_visitor{
void initialize_vertex(const graph_t::vertex_descriptor &s, const graph_t &g) const {
std::cout << "Initialize: " << g[s] << std::endl;
}
void discover_vertex(const graph_t::vertex_descriptor &s, const graph_t &g) const {
std::cout << "Discover: " << g[s] << std::endl;
}
void examine_vertex(const graph_t::vertex_descriptor &s, const graph_t &g) const {
std::cout << "Examine vertex: " << g[s] << std::endl;
}
void examine_edge(const graph_t::edge_descriptor &e, const graph_t &g) const {
std::cout << "Examine edge: " << g[e] << std::endl;
}
void tree_edge(const graph_t::edge_descriptor &e, const graph_t &g) const {
std::cout << "Tree edge: " << g[e] << std::endl;
}
void non_tree_edge(const graph_t::edge_descriptor &e, const graph_t &g) const {
std::cout << "Non-Tree edge: " << g[e] << std::endl;
}
void gray_target(const graph_t::edge_descriptor &e, const graph_t &g) const {
std::cout << "Gray target: " << g[e] << std::endl;
}
void black_target(const graph_t::edge_descriptor &e, const graph_t &g) const {
std::cout << "Black target: " << g[e] << std::endl;
}
void finish_vertex(const graph_t::vertex_descriptor &s, const graph_t &g) const {
std::cout << "Finish vertex: " << g[s] << std::endl;
}
};
int main() {
graph_t graph(4);
graph_t::vertex_descriptor a = boost::vertex(0, graph);
graph_t::vertex_descriptor b = boost::vertex(1, graph);
graph_t::vertex_descriptor c = boost::vertex(2, graph);
graph_t::vertex_descriptor d = boost::vertex(3, graph);
graph[a] = 0;
graph[b] = 1;
graph[c] = 2;
graph[d] = 3;
std::pair<graph_t::edge_descriptor, bool> result = boost::add_edge(a, b, 0, graph);
result = boost::add_edge(a, c, 1, graph);
result = boost::add_edge(c, b, 2, graph);
my_visitor vis;
breadth_first_search(graph, a, boost::visitor(vis).vertex_index_map(get(boost::vertex_bundle,graph)));
return 0;
}
I faced the same problem, but compared to the answer provided by user1252091 my vertex type is a struct that doesn't include an integer that can be used to create a vertex_index_map, therefore the line
breadth_first_search(graph, a, boost::visitor(vis).vertex_index_map(get(boost::vertex_bundle,graph)));
wouldn't work in my case. Eventually I figured out how to create an external vertex_index_map (thanks also to this answer) and pass it to the breadth_first_search function. Here is a working example in case it helps others:
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/visitors.hpp>
#include <boost/graph/breadth_first_search.hpp>
#include <iostream>
struct Person
{
std::string Name;
unsigned int YearBorn;
};
typedef boost::adjacency_list <boost::vecS, boost::hash_setS, boost::bidirectionalS, Person, boost::no_property > FamilyTree;
typedef boost::graph_traits<FamilyTree>::vertex_descriptor Vertex;
typedef boost::graph_traits<FamilyTree>::edge_descriptor Edge;
template <class Graph>
class BfsVisitor : public boost::default_bfs_visitor
{
public:
typedef typename boost::graph_traits<Graph>::vertex_descriptor VertexDescriptor;
typedef typename boost::graph_traits<Graph>::edge_descriptor EdgeDescriptor;
BfsVisitor(std::vector<VertexDescriptor>& nodesVisited)
: m_nodesVisited(nodesVisited){}
void tree_edge(EdgeDescriptor e, const Graph& g) const
{
VertexDescriptor u = source(e, g);
VertexDescriptor v = target(e, g);
m_nodesVisited.push_back(v);
}
private:
std::vector<VertexDescriptor>& m_nodesVisited;
};
const Person Abe_Simpson {"Abe_Simpson", 0};
const Person Mona_Simpson { "Mona_Simpson", 0};
const Person Herb_Simpson { "Herb_Simpson", 0};
const Person Homer_Simpson { "Homer_Simpson", 0};
const Person Clancy_Bouvier { "Clancy_Bouvier", 0};
const Person Jacqueline_Bouvier { "Jacqueline_Bouvier", 0};
const Person Marge_Bouvier { "Marge_Bouvier", 0};
const Person Patty_Bouvier { "Patty_Bouvier", 0};
const Person Selma_Bouvier { "Selma_Bouvier", 0};
const Person Bart_Simpson { "Bart_Simpson", 0};
const Person Lisa_Simpson { "Lisa_Simpson", 0};
const Person Maggie_Simpson { "Maggie_Simpson", 0};
const Person Ling_Bouvier { "Ling_Bouvier", 0};
int main(void)
{
std::cout << __FUNCTION__ << "\n";
FamilyTree g;
// nodes
auto v_Abe_Simpson = boost::add_vertex(Abe_Simpson,g);
auto v_Mona_Simpson = boost::add_vertex(Mona_Simpson,g);
auto v_Herb_Simpson = boost::add_vertex(Herb_Simpson,g);
auto v_Homer_Simpson = boost::add_vertex(Homer_Simpson,g);
auto v_Clancy_Bouvier = boost::add_vertex(Clancy_Bouvier,g);
auto v_Jacqueline_Bouvier = boost::add_vertex(Jacqueline_Bouvier,g);
auto v_Marge_Bouvier = boost::add_vertex(Marge_Bouvier,g);
auto v_Patty_Bouvier = boost::add_vertex(Patty_Bouvier,g);
auto v_Selma_Bouvier = boost::add_vertex(Selma_Bouvier,g);
auto v_Bart_Simpson = boost::add_vertex(Bart_Simpson,g);
auto v_Lisa_Simpson = boost::add_vertex(Lisa_Simpson,g);
auto v_Maggie_Simpson = boost::add_vertex(Maggie_Simpson,g);
auto v_Ling_Bouvier = boost::add_vertex(Ling_Bouvier,g);
// connections
boost::add_edge(v_Abe_Simpson, v_Herb_Simpson, g);
boost::add_edge(v_Abe_Simpson, v_Homer_Simpson, g);
boost::add_edge(v_Mona_Simpson, v_Herb_Simpson, g);
boost::add_edge(v_Mona_Simpson, v_Homer_Simpson, g);
boost::add_edge(v_Clancy_Bouvier, v_Marge_Bouvier, g);
boost::add_edge(v_Clancy_Bouvier, v_Patty_Bouvier, g);
boost::add_edge(v_Clancy_Bouvier, v_Selma_Bouvier, g);
boost::add_edge(v_Jacqueline_Bouvier, v_Marge_Bouvier, g);
boost::add_edge(v_Jacqueline_Bouvier, v_Patty_Bouvier, g);
boost::add_edge(v_Jacqueline_Bouvier, v_Selma_Bouvier, g);
boost::add_edge(v_Homer_Simpson, v_Bart_Simpson, g);
boost::add_edge(v_Homer_Simpson, v_Lisa_Simpson, g);
boost::add_edge(v_Homer_Simpson, v_Maggie_Simpson, g);
boost::add_edge(v_Marge_Bouvier, v_Bart_Simpson, g);
boost::add_edge(v_Marge_Bouvier, v_Lisa_Simpson, g);
boost::add_edge(v_Marge_Bouvier, v_Maggie_Simpson, g);
boost::add_edge(v_Selma_Bouvier, v_Ling_Bouvier, g);
typedef std::map<Vertex, size_t>IndexMap;
IndexMap mapIndex;
boost::associative_property_map<IndexMap> propmapIndex(mapIndex);
size_t i=0;
FamilyTree::vertex_iterator vi, vi_end;
for (boost::tie(vi, vi_end) = boost::vertices(g); vi != vi_end; ++vi)
{
boost::put(propmapIndex, *vi, i++);
}
for (boost::tie(vi, vi_end) = boost::vertices(g); vi != vi_end; ++vi)
{
Vertex vParent = *vi;
std::vector<Vertex> vertexDescriptors;
BfsVisitor<FamilyTree> bfsVisitor(vertexDescriptors);
breadth_first_search(g, vParent, visitor(bfsVisitor).vertex_index_map(propmapIndex));
std::cout << "\nDecendants of " << g[vParent].Name << ":\n";
for (auto v : vertexDescriptors)
{
Person p = g[v];
std::cout << p.Name << "\n";
}
}
getchar();
return 0;
}