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
Related
I'm learning about BGL and pieced together a program (from various related StackOverflow answers) to build a graph that should be able to deal with polymorphic vertices and edges. The graph should be able to handle parallel edges (of the same type or not). I'd like to get dijkstra_shortest_paths from a starting vertex_descriptor to : 1. all Park's; 2. all Park's and City's; 3. all City's via Railway only. Is that even possible ? Thank you.
#include <iostream>
#include <deque>
#include "boost/graph/adjacency_list.hpp"
#include "boost/graph/topological_sort.hpp"
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <boost/property_map/property_map.hpp>
#include <boost/variant.hpp>
#include <boost/graph/adj_list_serialize.hpp>
#include <boost/property_map/function_property_map.hpp>
#include <boost/property_map/transform_value_property_map.hpp>
using namespace boost;
struct Highway {
std::string name;
int miles;
int speed_limit;
int lanes;
bool divided;
};
struct Railway {
std::string name;
int km;
};
struct City {
std::string name;
int population;
std::vector<int> zipcodes;
};
struct Park {
std::string name;
int population;
std::vector<int> zipcodes;
};
struct Country {
std::string name;
bool use_right; // Drive on the left or right
bool use_metric; // mph or km/h
};
int main(int argc, char *argv[]) {
typedef boost::adjacency_list<
boost::listS, boost::vecS, boost::bidirectionalS,
boost::variant<City, Park>, boost::variant<Highway, Railway>, Country>
Map;
City c1 = {"San Jose", 1200000, {95126, 95128}};
City c2 = {"San Francisco", 500000, {95008, 95009}};
City c3 = {"Santa Cruz", 300000, {94001, 94002}};
City c4 = {"Oakland", 800000, {93001, 93002}};
City c5 = {"San Diego", 2800000, {92001, 92002}};
Map map; // load the map
Map::vertex_descriptor c1d = boost::add_vertex(c1, map),
c2d = boost::add_vertex(c2, map),
c3d = boost::add_vertex(c3, map),
c4d = boost::add_vertex(c4, map),
c5d = boost::add_vertex(c5, map);
add_edge(c1d, c2d, Highway{"101", 42, 65, 3, true}, map);
add_edge(c2d, c3d, Highway{"280", 52, 60, 1, true}, map);
add_edge(c2d, c3d, Highway{"92", 13, 60, 1, true}, map);
//add_edge(c2, c3, map);
//add_edge(c1, c3, map);
map[graph_bundle].name = "United States";
map[graph_bundle].use_right = true;
map[graph_bundle].use_metric = false;
using vertex_descriptor = boost::graph_traits<Map>::vertex_descriptor;
Map::vertex_descriptor from = *vertices(map).first;
std::vector<int> distances(num_vertices(map));
auto v_index = get(boost::vertex_index, map);
auto v_bundle = get(boost::vertex_bundle, map);
dijkstra_shortest_paths(map, from,
weight_map(get(get(&Highway::miles, map))
.distance_map(make_iterator_property_map(distances.begin(),
get(vertex_index, map))));
std::cout << "distances and parents:" << std::endl;
graph_traits <Map>::vertex_iterator vi, vend;
for (boost::tie(vi, vend) = vertices(map); vi != vend; ++vi) {
std::cout << "distance(" << get<City>(&City::name, map[*vi]) << ") = " << distances[*vi] << ", " << "\n";
}
std::cout << std::endl;
return 0;
}
So, in short, you want to access a property from a variant element type. You could make your own property map, facilitate your own set of get/put accessors or use a property map adaptor.
The latter being least work:
auto highway_miles = boost::make_transform_value_property_map(
[](Interconnect const& conn) {
if (auto* c = boost::get<Highway>(&conn)) {
return c->miles;
} else {
return std::numeric_limits<Distance>::max();
}
},
get(boost::edge_bundle, map));
dijkstra_shortest_paths(
map, from,
weight_map(highway_miles)
.distance_map(make_iterator_property_map(
distances.begin(), v_index)));
In fact with vecS chosen vertex container selector, you could write it more succinctly:
dijkstra_shortest_paths(
map, from, weight_map(highway_miles).distance_map(distances.data()));
Live Demo
Live on Compiler Explorer
#include <deque>
#include <iostream>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <boost/graph/topological_sort.hpp>
#include <boost/property_map/function_property_map.hpp>
#include <boost/property_map/transform_value_property_map.hpp>
#include <boost/variant.hpp>
using Distance = int;
struct Highway {
std::string name;
Distance miles;
unsigned speed_limit, lanes;
bool divided;
};
struct Railway {
std::string name;
Distance km;
};
struct City {
std::string name;
unsigned population;
std::vector<unsigned> zipcodes;
};
struct Park {
std::string name;
unsigned population;
std::vector<unsigned> zipcodes;
};
using Interconnect = boost::variant<Highway, Railway>;
using Area = boost::variant<City, Park>;
enum class RoadOrientation { right, left };
enum class UnitSystem { metric, imperial };
struct Country {
std::string name;
RoadOrientation driving;
UnitSystem measure;
};
using Map = boost::adjacency_list< //
boost::listS, boost::vecS, boost::bidirectionalS, //
Area, Interconnect, Country>;
int main()
{
Map map;
map[boost::graph_bundle] = {
"United States",
RoadOrientation::right,
UnitSystem::imperial,
};
auto c1d = add_vertex(City{"San Jose", 1200000, {95126, 95128}}, map);
auto c2d = add_vertex(City{"San Francisco", 500000, {95008, 95009}}, map);
auto c3d = add_vertex(City{"Santa Cruz", 300000, {94001, 94002}}, map);
add_vertex(City{"Oakland", 800000, {93001, 93002}}, map);
add_vertex(City{"San Diego", 2800000, {92001, 92002}}, map);
add_edge(c1d, c2d, Highway{"101", 42, 65, 3, true}, map);
add_edge(c2d, c3d, Highway{"280", 52, 60, 1, true}, map);
add_edge(c2d, c3d, Highway{"92", 13, 60, 1, true}, map);
using V = Map::vertex_descriptor;
V from = vertex(0, map);
std::vector<int> distances(num_vertices(map));
auto v_index = get(boost::vertex_index, map);
auto highway_miles = boost::make_transform_value_property_map(
[](Interconnect const& conn) {
if (auto* c = boost::get<Highway>(&conn)) {
return c->miles;
} else {
return std::numeric_limits<Distance>::max();
}
},
get(boost::edge_bundle, map));
dijkstra_shortest_paths(
map, from,
weight_map(highway_miles)
.distance_map(make_iterator_property_map(
distances.begin(), v_index)));
// dijkstra_shortest_paths(
// map, from, weight_map(highway_miles).distance_map(distances.data()));
std::cout << "distances and parents:" << std::endl;
auto name_of = [](auto const& area) {
return boost::apply_visitor(
[](auto const& c_or_p) { return c_or_p.name; }, area);
};
for (auto v : boost::make_iterator_range(vertices(map)))
std::cout << "distance(" << name_of(map[v]) << ") = " << distances[v] << "\n";
}
Prints
distances and parents:
distance(San Jose) = 0
distance(San Francisco) = 42
distance(Santa Cruz) = 55
distance(Oakland) = 2147483647
distance(San Diego) = 2147483647
More Ideas
I've provided recommendations/alternatives in the past:
Graph with two types of nodes
C++ and generic graph distance algorithm
UPDATE: Filtering Graph
In response to
I'd like to get dijkstra_shortest_paths from a starting vertex_descriptor to : 1. all Park's; 2. all Park's and City's; 3. all City's via Railway only. Is that even possible ? Thank you.
Here's how you might write that using filtered graphs:
using V = Map::vertex_descriptor;
using E = Map::edge_descriptor;
using VertexFilter = std::function<bool(V)>;
using EdgeFilter = std::function<bool(E)>;
using FMap = boost::filtered_graph<Map, EdgeFilter, VertexFilter>;
EdgeFilter any_interconnect = boost::keep_all{};
EdgeFilter railway_only = [&map](E e) -> bool { return get<Railway>(&map[e]); };
VertexFilter parks = [&map](V v) -> bool { return get<Park>(&map[v]); };
VertexFilter cities = [&map](V v) -> bool { return get<City>(&map[v]); };
VertexFilter parks_cities = boost::keep_all{};
struct {
FMap fmap;
std::string_view caption;
} cases[] = {
{FMap(map, any_interconnect, parks), "All parks"},
{FMap(map, any_interconnect, parks_cities), "All parks and cities"},
{FMap(map, railway_only, cities), "Cities by railway"},
};
for (auto const& [fmap, caption] : cases) {
std::cout << " ---- " << caption << "\n";
if (auto [vb, ve] = vertices(fmap); vb != ve) {
std::vector<Distance> distances(num_vertices(
fmap)); // or map, since descriptors are same domain
auto weight = make_transform_value_property_map(
distance_km, get(boost::edge_bundle, fmap));
dijkstra_shortest_paths(
fmap, *vb,
weight_map(weight).distance_map(distances.data()));
for (auto v : boost::make_iterator_range(vertices(fmap)))
std::cout << "distance(" << name_of(fmap[v])
<< ") = " << distances[v] << "km\n";
}
}
Here's a live demo:
Live On Compiler Explorer
#include <iostream>
#include <iomanip>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/adjacency_list_io.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <boost/graph/topological_sort.hpp>
#include <boost/graph/filtered_graph.hpp>
#include <boost/property_map/transform_value_property_map.hpp>
#include <boost/variant.hpp>
using Distance = double;
struct Highway {
std::string name;
Distance miles;
unsigned speed_limit, lanes;
bool divided;
};
struct Railway {
std::string name;
Distance km;
};
struct City {
std::string name;
unsigned population;
std::vector<unsigned> zipcodes;
};
struct Park {
std::string name;
unsigned population;
std::vector<unsigned> zipcodes;
};
using Interconnect = boost::variant<Highway, Railway>;
using Area = boost::variant<City, Park>;
enum class RoadOrientation { right, left };
enum class UnitSystem { metric, imperial };
struct {
Distance operator()(Highway const& hw) const { return 1.60934 * hw.miles; }
Distance operator()(Railway const& rw) const { return rw.km; }
Distance operator()(auto const& variant) const {
return boost::apply_visitor(
[this](auto const& obj) { return (*this)(obj); }, variant);
}
} static const inline distance_km;
struct {
std::string_view operator()(auto const& variant) const {
return boost::apply_visitor(
[](auto const& obj) -> std::string_view { return obj.name; },
variant);
}
} static const inline name_of;
struct Country {
std::string name;
RoadOrientation driving;
UnitSystem measure;
};
// for debug output
static inline std::ostream& operator<<(std::ostream& os, City const& c) { return os << "City{" << std::quoted(c.name) << ", pop:" << c.population << "}"; }
static inline std::ostream& operator<<(std::ostream& os, Park const& p) { return os << "Park{" << std::quoted(p.name) << ", pop:" << p.population << "}"; }
static inline std::ostream& operator<<(std::ostream& os, Highway const& hw) { return os << "Highway{" << std::quoted(hw.name) << ", " << distance_km(hw) << "km}"; }
static inline std::ostream& operator<<(std::ostream& os, Railway const& rw) { return os << "Railway{" << std::quoted(rw.name) << ", " << distance_km(rw) << "km}"; }
using Map = boost::adjacency_list< //
boost::listS, boost::vecS, boost::bidirectionalS, //
Area, Interconnect, Country>;
int main()
{
Map map;
map[boost::graph_bundle] = {
"United States",
RoadOrientation::right,
UnitSystem::imperial,
};
auto c1d = add_vertex(City{"San Jose", 1200000, {95126, 95128}}, map);
auto c2d = add_vertex(City{"San Francisco", 500000, {95008, 95009}}, map);
auto c3d = add_vertex(City{"Santa Cruz", 300000, {94001, 94002}}, map);
auto c4d = add_vertex(City{"Oakland", 800000, {93001, 93002}}, map);
add_vertex(City{"San Diego", 2800000, {92001, 92002}}, map);
auto p1d = add_vertex(Park{"Alum Rock Park", 0, {}}, map);
auto p2d = add_vertex(Park{"Calero County Park", 0, {}}, map);
auto p3d = add_vertex(Park{"Reinhardt Redwood", 0, {}}, map);
auto p4d = add_vertex(Park{"Knowland Park", 0, {}}, map);
add_edge(c1d, c2d, Highway{"101", 42, 65, 3, true}, map);
add_edge(c2d, c3d, Highway{"280", 52, 60, 1, true}, map);
add_edge(c2d, c3d, Highway{"92", 13, 60, 1, true}, map);
add_edge(c1d, p1d, Highway{"102", 27, 65, 2, false}, map);
add_edge(c1d, p2d, Railway{"N1", 36}, map);
add_edge(c4d, p3d, Highway{"703", 53, 65, 2, false}, map);
add_edge(c4d, p4d, Railway{"N2", 70}, map);
std::cout << " ---- map\n" << boost::write(map) << "\n";
using V = Map::vertex_descriptor;
using E = Map::edge_descriptor;
using VertexFilter = std::function<bool(V)>;
using EdgeFilter = std::function<bool(E)>;
using FMap = boost::filtered_graph<Map, EdgeFilter, VertexFilter>;
EdgeFilter any_interconnect = boost::keep_all{};
EdgeFilter railway_only = [&map](E e) -> bool { return get<Railway>(&map[e]); };
VertexFilter parks = [&map](V v) -> bool { return get<Park>(&map[v]); };
VertexFilter cities = [&map](V v) -> bool { return get<City>(&map[v]); };
VertexFilter parks_cities = boost::keep_all{};
struct {
FMap fmap;
std::string_view caption;
} cases[] = {
{FMap(map, any_interconnect, parks), "All parks"},
{FMap(map, any_interconnect, parks_cities), "All parks and cities"},
{FMap(map, railway_only, cities), "Cities by railway"},
};
for (auto const& [fmap, caption] : cases) {
std::cout << " ---- " << caption << "\n";
if (auto [vb, ve] = vertices(fmap); vb != ve) {
std::vector<Distance> distances(num_vertices(
fmap)); // or map, since descriptors are same domain
auto weight = make_transform_value_property_map(
distance_km, get(boost::edge_bundle, fmap));
dijkstra_shortest_paths(
fmap, *vb,
weight_map(weight).distance_map(distances.data()));
for (auto v : boost::make_iterator_range(vertices(fmap)))
std::cout << "distance(" << name_of(fmap[v])
<< ") = " << distances[v] << "km\n";
}
}
}
Prints
---- map
v
City{"San Jose", pop:1200000}
City{"San Francisco", pop:500000}
City{"Santa Cruz", pop:300000}
City{"Oakland", pop:800000}
City{"San Diego", pop:2800000}
Park{"Alum Rock Park", pop:0}
Park{"Calero County Park", pop:0}
Park{"Reinhardt Redwood", pop:0}
Park{"Knowland Park", pop:0}
e
0 1 Highway{"101", 67.5923km}
1 2 Highway{"280", 83.6857km}
1 2 Highway{"92", 20.9214km}
0 5 Highway{"102", 43.4522km}
0 6 Railway{"N1", 36km}
3 7 Highway{"703", 85.295km}
3 8 Railway{"N2", 70km}
---- All parks
distance(Alum Rock Park) = 0km
distance(Calero County Park) = 1.79769e+308km
distance(Reinhardt Redwood) = 1.79769e+308km
distance(Knowland Park) = 1.79769e+308km
---- All parks and cities
distance(San Jose) = 0km
distance(San Francisco) = 67.5923km
distance(Santa Cruz) = 88.5137km
distance(Oakland) = 1.79769e+308km
distance(San Diego) = 1.79769e+308km
distance(Alum Rock Park) = 43.4522km
distance(Calero County Park) = 36km
distance(Reinhardt Redwood) = 1.79769e+308km
distance(Knowland Park) = 1.79769e+308km
---- Cities by railway
distance(San Jose) = 0km
distance(San Francisco) = 1.79769e+308km
distance(Santa Cruz) = 1.79769e+308km
distance(Oakland) = 1.79769e+308km
distance(San Diego) = 1.79769e+308km
I made up some extra data to even have parks or railways.
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)
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
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:
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;
}