Longest Path in Boost Graph - boost

Sorry if this is a very basic questions for some of you but I'm new to C++ (let alone Boost Graph Library) and couldn't figure out this problem. So far I've been able to formulate/gather code to create a graph using the code below.
Now I'm trying to figure out the code to find the longest path in this graph.
Can someone please help with what would the code be? I was having trouble trying to figure out if/how to traverse through each node and/or edge when trying to find the path?
I have to try to return all the nodes and edges in the longest path.
Any help will be greatly appreciated.
P.S. does anyone know if C++ has organized documentation like Javadoc??
#include <boost/graph/dag_shortest_paths.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <windows.h>
#include <iostream>
int main()
{
using namespace boost;
typedef adjacency_list<vecS, vecS, directedS, property<vertex_distance_t, double>, property<edge_weight_t, double> > graph_t;
graph_t g(6);
enum verts { stationA, stationB, stationC, stationD, stationE, stationF };
char name[] = "rstuvx";
add_edge(stationA, stationB, 5000.23, g);
add_edge(stationA, stationC, 3001, g);
add_edge(stationA, stationD, 2098.67, g);
add_edge(stationA, stationE, 3298.84, g);
add_edge(stationB, stationF, 2145, g);
add_edge(stationC, stationF, 4290, g);
add_edge(stationD, stationF, 2672.78, g);
add_edge(stationE, stationF, 11143.876, g);
add_edge(stationA, stationF, 1, g);
//Display all the vertices
typedef property_map<graph_t, vertex_index_t>::type IndexMap;
IndexMap index = get(vertex_index, g);
std::cout << "vertices(g) = ";
typedef graph_traits<graph_t>::vertex_iterator vertex_iter;
std::pair<vertex_iter, vertex_iter> vp;
for (vp = vertices(g); vp.first != vp.second; ++vp.first)
std::cout << index[*vp.first] << " ";
std::cout << std::endl;
// ...
// Display all the edges
// ...
std::cout << "edges(g) = " << std::endl;
graph_traits<graph_t>::edge_iterator ei, ei_end;
for (tie(ei, ei_end) = edges(g); ei != ei_end; ++ei)
std::cout << "(" << index[source(*ei, g)] << "," << index[target(*ei, g)] << ") \n";
std::cout << std::endl;
// ...

I think you should check the example in your boost distribution.
Online : http://www.boost.org/doc/libs/1_38_0/libs/graph/example/dijkstra-example.cpp
To make it find the longest path you need to simply inverse the weight (W), either use a Constant - W, or 1/W. If the constant is 0, then it means it's a negation (-W).

I agree that one has to be careful about sign reversal. Firstly, most shortest path algorithms are only for positive edge graphs. You do have some options (e.g Bellman-Ford algorithm) that generalize to graphs with negative weight, they are not guaranteed to return the optimal answer if there are negative cycles in the graph.

Related

Create Edges in Boost Graph using Multi Threading

I am trying to create a boost graph with more than 50K nodes (It will map the configuration space of a robot) and I want to create edges between the node using multi threading as it has become a bottleneck for my program.
I store all the vertices' index in a hash map so that they are easy for lookup while adding edges. For each vertex I find 5 nearest neighbors that are to be connected.
Also I have disabled parallel edges in the graph and the graph definition is
using Graph = boost::adjacency_list<boost::setS, boost::vecS, boost::undirectedS, VertexProperties, EdgeProperties>;
For finding the nearest neighbours, I use Local Senstivity Hashing (github_repo).
model* myModel;
myModel = new lshEuclidean();
myModel->fit(datapoints, status); /// training on all leaf nodes that are not in collision
Also before connecting the edges, I insert all the vertices in the graph and also make a hash map so that it is easy to recover the vertex index for adding an edge. (For quick testing, I convert the vector to a string to store in the hashmap, I know this is inefficient and need to make my own hash function)
BoostGraph::VertexProperties vp1;
BoostGraph graph(5);
std::unordered_map<std::string, int> map;
for(int center = 0; center < finalLeafNodes.size(); center++){
Vec origin = finalLeafNodes[center]->getOrigin();
std::vector<double> joint_angle = {origin.at(0)*toRadians, origin.at(1)*toRadians, origin.at(2)*toRadians,
origin.at(3)*toRadians, origin.at(4)*toRadians};
Eigen::VectorXd joint_angle_center;
joint_angle_center.resize(5);
joint_angle_center << joint_angle[0], joint_angle[1], joint_angle[2], joint_angle[3], joint_angle[4];
vp1.joint_angles = joint_angle;
BoostGraph::Vertex v_center = graph.AddVertex(vp1);
int vertex_index_center = graph.getVertexIndex(v_center);
Vec joint_angle_in_vector_degrees = origin;
std::stringstream output;
std::copy(joint_angle_in_vector_degrees.begin(), joint_angle_in_vector_degrees.end(), std::ostream_iterator<double>(output, " "));
map[output.str()] = vertex_index_center;
}
Then for each vertex node, I find neighbours in a given radius, sort them to nearest neighbour and take top 3/5 and add an edge by finding those neighbours vertex index through the hashmap mentioned above. I also have a local planner that checks if the path between two points will also be collision free or not. If its collision free, edge is added.
neighbors.sort([&query](Item &a, Item &b) -> bool {compare(a, b, query);});
auto edge = graph.AddEdge(center_iterator->second, neighbour_iterator->second, BoostGraph::EdgeProperties{(double)recursion_index + 1.});
Also I am now trying on a five degree of freedom robot, so the dimension has also increased.
I have tried multi threading with mutex_lock() but its not giving much of a speedup.
Is there a way to create a shared memory object where I can store the all the edges in multi threading and just loop over it to add the edges in the graph so that I don't have parallel edges.
I want to create edges between the node using multi threading as it has become a bottleneck for my program
Frequently the solution will be to change the choice of datastructure or algorithm. And it is quite likely that the time is actually spent doing other things than actually inserting edges.
In some cases you will even want to have a graph model that is just an edge list.
Here's a implementation of your description (using parts of the code from previous questions). In some sense it is straight-forward. In some sense it might show you some advanced algorithm/datastructure ideas. I think it doesn't have the performance bottleneck you are talking about?
Generating Input Data
Let's read vertices from CSV data. Generating 50k input lines:
Live On Coliru: gen.cpp
./a.out > input.txt; wc -l input.txt; tail input.txt
50000 input.txt
-0.54953,0.309816,1.49314
-1.38758,1.10754,1.12841
0.468204,-1.38628,1.29798
1.44672,-0.600287,-1.1688
1.28432,-1.40215,0.701882
1.4669,-0.215648,-0.404705
-0.701017,-0.130071,-0.62072
1.3742,-0.639261,1.44033
-1.17127,-1.48499,-1.03837
-1.16458,-1.19539,-0.946286
Parsing Vertices From Input Data
Note I included the optimization I suggested in an earlier question:
using JointAngles = std::array<double, 3>;
This also makes it easier later on to use geometry algorithms.
The parsing is not really related to the question, so posted as-is:
template <typename F> size_t read_vertices(std::string_view input, F callback) {
using namespace boost::spirit::x3;
using boost::fusion::at_c;
Vertex n = 0;
auto action = [&](auto& ctx) {
auto& vv = _attr(ctx);
callback(JointAngles{at_c<0>(vv), at_c<1>(vv), at_c<2>(vv)});
n += 1;
};
static auto const line = (double_ >> ',' >> double_ >> ',' >> double_)[action];
parse(begin(input), end(input), skip(blank)[line % (eol | eoi) > (*eol >> eoi)]);
return n;
}
Note how it is a whitespace tolerant where possible and supports ±inf/nan.
A Spatial Index
Instead of brute-forcing our way, let's use a Spatial Index from Boost Geometry. What this will allow us to do is find the nearest-k points much cheaper than bruteforce.
Firstly, include the relevant headers:
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/adapted/std_array.hpp>
#include <boost/geometry/index/adaptors/query.hpp>
#include <boost/geometry/index/rtree.hpp>
Now, let's tell Boost Geometry about our point type, and define a Tree type:
BOOST_GEOMETRY_REGISTER_STD_ARRAY_CS(bg::cs::cartesian)
namespace bg = boost::geometry;
namespace bgi = bg::index;
using Tree = bgi::rtree<std::pair<JointAngles, Vertex>, bgi::rstar<16>>;
We choose R* packing algorithm, which should usually give us best nearest() performance at the cost of higher insertion cost:
Actually Read The Graph
Using the parsing function above, let's build the graph and the spatial index tree at once:
int main() {
// read and index vertices
Tree tree;
Graph graph;
std::ifstream ifs("input.txt", std::ios::binary);
std::string const input(std::istreambuf_iterator<char>(ifs), {});
graph.m_vertices.reserve(50'000);
auto const n = read_vertices(input, [&](JointAngles ja) {
tree.insert({ja, add_vertex(VertexProperties{ja}, graph)});
});
std::cout << "Parsed " << n << " vertices, indexed: " << tree.size()
<< " graph: " << num_vertices(graph) << "\n";
That's all. Note how each inserted point in the tree carries the vertex descriptor as meta data, so we can correlate vertices with tree nodes.
This code will print, as expected, for our generated input.txt:
Parsed 50000 vertices, indexed: 50000 graph: 50000
Adding 5-nearest edges
Using a bgi query this is pretty simple. Likely this can be optimized, but let's do the naive thing first, just to see whether the performance is reasonable:
// connect 5-degree nearest vertices
size_t added = 0, dups =0;
for (auto& [vja, v] : tree) {
for (auto& [uja, u] : tree | queried(bgi::nearest(vja, 6))) {
if (v == u)
continue;
auto w = bg::distance(vja, uja);
auto [e, ok] = add_edge(v, u, EdgeProperties{w}, graph);
//std::cout << (ok ? "Added " : "Duplicate ") << e << " weight " << w << "\n";
(ok? added:dups)++;
}
}
std::cout << "Total edges added:" << added << " dups:" << dups << "\n";
Note that we omit self-edges, and rely on setS and undirectedS to detect duplicates - which are obviously expected. This prints, for our test data:
Total edges added:150778 dups:99222
BONUS: A* search
Like in your previous question, let's perform an A* search between arbitrary vertices:
// do A* search
std::vector<Vertex> predecessors(n);
std::vector<double> distances(n);
auto vidx = get(boost::vertex_index, graph); // redundant with vecS
auto pmap = make_iterator_property_map(predecessors.data(), vidx);
auto dmap = make_iterator_property_map(distances.data(), vidx);
auto weightmap = get(&EdgeProperties::weight, graph);
std::mt19937 gen(std::random_device{}());
Vertex start = random_vertex(graph, gen);
Vertex goal = random_vertex(graph, gen);
try {
// call astar named parameter interface
auto heuristic = [&, gja = graph[goal].joint_angles](Vertex u) {
return bg::distance(graph[u].joint_angles, gja);
};
astar_search( //
graph, start, heuristic,
boost::predecessor_map(pmap) //
.distance_map(dmap)
.weight_map(weightmap)
.visitor(goal_visitor{goal}));
fmt::print("{} -> {}: No path\n", start, goal);
} catch (goal_visitor::found) {
std::list<Vertex> path;
for (auto cursor = goal;;) {
path.push_front(cursor);
auto previous = std::exchange(cursor, predecessors.at(cursor));
if (cursor == previous)
break;
}
fmt::print("{} -> {}: {}\n", start, goal, path);
}
As you can see everything is basically unchanged, except the distance_heuristic class has been replaced by the much simpler lambda:
auto heuristic = [&, gja = graph[goal].joint_angles](Vertex u) {
return bg::distance(graph[u].joint_angles, gja);
};
This effectively does the same as your manual heuristic, except potentially smarter - who knows :).
Possible outputs. Doing 1000 random searches took ~1.8s:
Parsed 50000 vertices, indexed: 50000 graph: 50000 0.161082s
Total edges added:150778 dups:99222 0.190395s
7489 -> 8408: [7489, 23635, 34645, 41337, 1725, 46184, 25161, 33297, 30471, 37500, 4073, 30763, 4488, 30949, 9505, 48543, 33639, 35640, 19525, 34765, 18439, 21830, 4170, 27552, 22621, 6327, 8277, 8082, 15932, 23390, 8408]
6968 -> 49906: [6968, 43210, 9331, 36641, 15088, 45635, 47530, 9136, 18177, 30781, 46243, 21125, 12868, 42416, 46187, 24824, 39841, 39095, 13494, 27104, 34973, 49906]
39242 -> 46236: [39242, 34365, 14041, 30310, 8757, 35459, 41035, 32883, 1552, 24120, 43646, 38812, 17835, 14082, 46568, 37492, 17564, 4934, 28288, 20393, 924, 14615, 15993, 39413, 10407, 46236]
--
31949 -> 38708: [31949, 16473, 18328, 20099, 22828, 42868, 46176, 22766, 49370, 17479, 636, 6173, 36367, 32040, 16961, 48438, 18883, 44611, 19468, 4095, 18156, 33083, 12925, 41017, 17514, 17765, 19710, 25790, 46668, 28202, 12010, 39520, 17796, 45443, 9474, 17370, 5071, 27279, 17083, 3503, 11401, 11209, 32403, 23265, 38708]
9895 -> 41286: [9895, 7793, 34802, 28190, 24889, 578, 49750, 20217, 41057, 2637, 24109, 4262, 38363, 11680, 7513, 39893, 21158, 15747, 33531, 11051, 7893, 31583, 45825, 18988, 38405, 13631, 31016, 45820, 9078, 37368, 28401, 14573, 9294, 6214, 28330, 22949, 10575, 41286]
42176 -> 37875: [42176, 12091, 19799, 41080, 47399, 30041, 41714, 10766, 8904, 41305, 4973, 21270, 18139, 29246, 34739, 35599, 11807, 36557, 48764, 9641, 3619, 11747, 34201, 33629, 20414, 24646, 43402, 36831, 7384, 29363, 24768, 33415, 41325, 17709, 32108, 42284, 28683, 5310, 1506, 14339, 27331, 14861, 7152, 37211, 22754, 7602, 48398, 27378, 39577, 37875]
Total search time: 1.79371s
real 0m2,209s
user 0m2,160s
sys 0m0,044s
Complete Benchmark
Live On Coliru
#include <boost/fusion/adapted/std_array.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/astar_search.hpp>
#include <boost/graph/random.hpp>
#include <chrono>
#include <fmt/ranges.h>
#include <fstream>
#include <random>
static auto now = &std::chrono::steady_clock::now;
using namespace std::chrono_literals;
using JointAngles = std::array<double, 3>;
struct VertexProperties {
JointAngles joint_angles{0, 0, 0};
};
struct EdgeProperties {
double weight = 0;
};
using Graph = boost::adjacency_list<boost::setS, boost::vecS, boost::undirectedS,
VertexProperties, EdgeProperties>;
using Vertex = Graph::vertex_descriptor;
template <typename F> size_t read_vertices(std::string_view input, F callback) {
using namespace boost::spirit::x3;
using boost::fusion::at_c;
Vertex n = 0;
auto action = [&](auto& ctx) {
auto& vv = _attr(ctx);
callback(JointAngles{at_c<0>(vv), at_c<1>(vv), at_c<2>(vv)});
n += 1;
};
static auto const line = (double_ >> ',' >> double_ >> ',' >> double_)[action];
parse(begin(input), end(input), skip(blank)[line % (eol | eoi) > (*eol >> eoi)]);
return n;
}
// visitor that terminates when we find the goal
struct goal_visitor : boost::default_astar_visitor {
struct found {}; // exception for termination
Vertex m_goal;
goal_visitor(Vertex g) : m_goal(g) {}
template <class Graph> void examine_vertex(Vertex u, Graph&) {
if (u == m_goal)
throw found{};
}
};
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/adapted/std_array.hpp>
#include <boost/geometry/index/adaptors/query.hpp>
#include <boost/geometry/index/rtree.hpp>
namespace bg = boost::geometry;
namespace bgi = bg::index;
using bgi::adaptors::queried;
BOOST_GEOMETRY_REGISTER_STD_ARRAY_CS(bg::cs::cartesian)
using Tree = bgi::rtree<std::pair<JointAngles, Vertex>, bgi::rstar<16>>;
int main() {
auto elapsed = [start = now()]() mutable {
auto n = now();
return (n - std::exchange(start, n)) / 1.0s;
};
// read and index vertices
Tree tree;
Graph graph;
std::ifstream ifs("input.txt", std::ios::binary);
std::string const input(std::istreambuf_iterator<char>(ifs), {});
graph.m_vertices.reserve(50'000);
auto const n = read_vertices(input, [&](JointAngles ja) {
tree.insert({ja, add_vertex(VertexProperties{ja}, graph)});
});
std::cout << "Parsed " << n << " vertices, indexed: " << tree.size()
<< " graph: " << num_vertices(graph) << " " << elapsed() << "s\n";
assert(n == tree.size());
assert(n == num_vertices(graph));
// connect 5-degree nearest vertices
size_t added = 0, dups =0;
for (auto& [vja, v] : tree) {
for (auto& [uja, u] : tree | queried(bgi::nearest(vja, 6))) {
if (v == u)
continue;
auto w = bg::distance(vja, uja);
auto [e, ok] = add_edge(v, u, EdgeProperties{w}, graph);
//std::cout << (ok ? "Added " : "Duplicate ") << e << " weight " << w << "\n";
(ok? added:dups)++;
}
}
std::cout << "Total edges added:" << added << " dups:" << dups << " " << elapsed() << "s\n";
// do A* search
std::vector<Vertex> predecessors(n);
std::vector<double> distances(n);
for (auto i = 0; i < 1'000; ++i) {
auto vidx = get(boost::vertex_index, graph); // redundant with vecS
auto pmap = make_iterator_property_map(predecessors.data(), vidx);
auto dmap = make_iterator_property_map(distances.data(), vidx);
auto weightmap = get(&EdgeProperties::weight, graph);
std::mt19937 gen(std::random_device{}());
Vertex start = random_vertex(graph, gen);
Vertex goal = random_vertex(graph, gen);
try {
// call astar named parameter interface
auto heuristic = [&, gja = graph[goal].joint_angles](Vertex u) {
return bg::distance(graph[u].joint_angles, gja);
};
astar_search( //
graph, start, heuristic,
boost::predecessor_map(pmap) //
.distance_map(dmap)
.weight_map(weightmap)
.visitor(goal_visitor{goal}));
fmt::print("{} -> {}: No path\n", start, goal);
} catch (goal_visitor::found) {
std::list<Vertex> path;
for (auto cursor = goal;;) {
path.push_front(cursor);
auto previous = std::exchange(cursor, predecessors.at(cursor));
if (cursor == previous)
break;
}
fmt::print("{} -> {}: {}\n", start, goal, path);
}
}
std::cout << "Total search time: " << elapsed() << "s\n";
}
On Coliru, takes a little longer:
Parsed 50000 vertices, indexed: 50000 graph: 50000 0.252916s
Total edges added:150778 dups:99222 0.38979s
43176 -> 2998: [43176, 8919, 27234, 38221, 8714, 2907, 45819, 32924, 33376, 14539, 9174, 19001, 30909, 3923, 36332, 4521, 43005, 31867, 7326, 46231, 20699, 24026, 44641, 21918, 43012, 37366, 2800, 14239, 21197, 26989, 38269, 16522, 25964, 18224, 47148, 21553, 19350, 37546, 41390, 1247, 2998]
19955 -> 30654: [19955, 18833, 24521, 9310, 29015, 5746, 46264, 7706, 4929, 11078, 41910, 30676, 26759, 16638, 3075, 23001, 9322, 38446, 20634, 1120, 30761, 47535, 15750, 10039, 34123, 42874, 22325, 24136, 30285, 34230, 23926, 9978, 4427, 23805, 10436, 41678, 46936, 37189, 30654]
45710 -> 21757: [45710, 45416, 1375, 16480, 21730, 22843, 15897, 33652, 12561, 46834, 23178, 44302, 21027, 15457, 38383, 14716, 26787, 20697, 41752, 42153, 44194, 21757]
--
16543 -> 43355: [16543, 44982, 27516, 6578, 27706, 39013, 35842, 33455, 30460, 22955, 579, 46537, 43224, 6811, 1651, 41054, 21637, 9496, 36577, 21896, 49329, 43355]
2856 -> 24431: [2856, 21766, 1449, 2525, 15156, 6325, 23773, 25733, 48449, 24269, 49865, 34213, 47119, 48167, 12609, 46284, 33395, 10107, 26726, 14078, 28431, 33884, 468, 39873, 42529, 32395, 49457, 44554, 2207, 47678, 4783, 14247, 39638, 8510, 9439, 20570, 18018, 34614, 37184, 17579, 49921, 8755, 44316, 24431]
17195 -> 21888: [17195, 38851, 28287, 18829, 14051, 28305, 32206, 11044, 6989, 30201, 49002, 19410, 6456, 47912, 35145, 9286, 17782, 10294, 14344, 49966, 49634, 5262, 12496, 45270, 20093, 11298, 7202, 15409, 41313, 35934, 14510, 17221, 23121, 49522, 38138, 45948, 43564, 7840, 4456, 32016, 16660, 5832, 7578, 380, 9925, 18908, 38131, 36929, 28073, 21888]
Total search time: 3.41871s

BGL bundled edge properties containing vector [duplicate]

I have a boost graph with multiples weights for each edges (imagine one set of weights per hour of the day). Every one of those weights values is stored in a propretyEdge class :
class propretyEdge {
std::map<std::string,double> weights; // Date indexed
}
I created a graph with those properties, and then filled it with the right values.
The problem is now that I want to launch the Dijkstra algorithm over a particular set of weight on the graph : for example a function that could be :
void Dijkstra (string date, parameters ... )
That would use the
weights[date]
value for each Edge of the graph.
I read over and over the documentation, and I couldn't have a clear picture of what I have to do. I surely need to write something like this, but I have no idea were to start :
boost::dijkstra_shortest_paths (
(*graph_m),
vertex_origin_num_l,
// weight_map (get (edge_weight, (*graph_m)))
// predecessor_map(boost::make_iterator_property_map(predecessors.begin(), get(boost::vertex_index, (*graph_m)))).
// distance_map(boost::make_iterator_property_map(distances.begin (), get(vertex_index,(*graph_m) )))
predecessor_map(predecessorMap).
distance_map(distanceMap)
);
Thank you for your help.
Edit
Thanks to the wonderful Answer of Sehe, I was able to do exactly what I wanted on MacOS and on Ubuntu.
But when we tried to compile this piece of code on Visual Studio 2012, it appeared that VS wasn't very good at understanding pointer function of boost. So we modified the part of Sehe :
auto dated_weight_f = [&](Graph::edge_descriptor ed) {
return g[ed].weights.at(date);
};
auto dated_weight_map = make_function_property_map<Graph::edge_descriptor, double>(dated_weight_f);
by :
class dated_weight_f {
public:
dated_weight_f(Graph* graph_p,std::string date_p){
graph_m=graph_p;
date_m=date_p;
}
typedef double result_type;
result_type operator()(Edge edge_p) const{
return (*graph_m)[edge_p].weights.at(date_m);
}
private:
Graph* graph_m;
std::string date_m;
};
const auto dated_weight_map = make_function_property_map<Edge>(dated_weight_f(graph_m,date_l));
Which had the advantage of not using a pointer function.
Since it's apparently not immediately clear that this question is answered in the other answer, I'll explain.
All you really need is a custom weight_map parameter that is "stateful" and can select a certain value for a given date.
You can make this as complicated as you wish ¹, so you could even interpolate/extrapolate a weight given an unknown date ², but let's for the purpose of this demonstration keep it simple.
Let's define the graph type (roughly) as above:
struct propretyEdge {
std::map<std::string, double> weights; // Date indexed
};
using Graph = adjacency_list<vecS, vecS, directedS, no_property, propretyEdge>;
Now, let's generate a random graph, with random weights for 3 different dates:
int main() {
Graph g;
std::mt19937 prng { std::random_device{}() };
generate_random_graph(g, 8, 12, prng);
uniform_real<double> weight_dist(10,42);
for (auto e : make_iterator_range(edges(g)))
for (auto&& date : { "2014-01-01", "2014-02-01", "2014-03-01" })
g[e].weights[date] = weight_dist(prng);
And, jumping to the goal:
for (std::string const& date : { "2014-01-01", "2014-02-01", "2014-03-01" }) {
Dijkstra(date, g, 0);
}
}
Now how do you implement Dijkstra(...)? Gleaning from the documentation sample, you'd do something like
void Dijkstra(std::string const& date, Graph const& g, int vertex_origin_num_l = 0) {
// magic postponed ...
std::vector<Graph::vertex_descriptor> p(num_vertices(g));
std::vector<double> d(num_vertices(g));
std::vector<default_color_type> color_map(num_vertices(g));
boost::typed_identity_property_map<Graph::vertex_descriptor> vid; // T* property maps were deprecated
dijkstra_shortest_paths(g, vertex_origin_num_l,
weight_map(dated_weight_map).
predecessor_map(make_iterator_property_map(p.data(), vid)).
distance_map(make_iterator_property_map(d.data(), vid)).
color_map(make_iterator_property_map(color_map.data(), vid))
);
Now the only unclear bit here should be dated_weight_map.
Enter Boost Property Maps
As I showed in the linked Is it possible to have several edge weight property maps for one graph BOOST?, you can have all kinds of property maps ³, including invocation of user-defined functions. This is the missing piece:
auto dated_weight_f = [&](Graph::edge_descriptor ed) {
return g[ed].weights.at(date);
};
auto dated_weight_map = make_function_property_map<Graph::edge_descriptor, double>(dated_weight_f);
Voilà: done
I hope that by now, the correspondence in the question as well as the answer of the linked question is clear. All that's left to do is post the full live sample and the outcome in a pretty picture:
Live On Coliru
#include <boost/property_map/property_map.hpp>
#include <boost/property_map/function_property_map.hpp>
#include <boost/property_map/property_map_iterator.hpp>
#include <random>
#include <boost/graph/random.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <fstream>
using namespace boost;
struct propretyEdge {
std::map<std::string, double> weights; // Date indexed
};
using Graph = adjacency_list<vecS, vecS, directedS, no_property, propretyEdge>;
void Dijkstra(std::string const& date, Graph const& g, int vertex_origin_num_l = 0) {
auto dated_weight_f = [&](Graph::edge_descriptor ed) {
return g[ed].weights.at(date);
};
auto dated_weight_map = make_function_property_map<Graph::edge_descriptor, double>(dated_weight_f);
std::vector<Graph::vertex_descriptor> p(num_vertices(g));
std::vector<double> d(num_vertices(g));
std::vector<default_color_type> color_map(num_vertices(g));
boost::typed_identity_property_map<Graph::vertex_descriptor> vid; // T* property maps were deprecated
dijkstra_shortest_paths(g, vertex_origin_num_l,
weight_map(dated_weight_map).
predecessor_map(make_iterator_property_map(p.data(), vid)).
distance_map(make_iterator_property_map(d.data(), vid)).
color_map(make_iterator_property_map(color_map.data(), vid))
);
std::cout << "distances and parents for '" + date + "':" << std::endl;
for (auto vd : make_iterator_range(vertices(g)))
{
std::cout << "distance(" << vd << ") = " << d[vd] << ", ";
std::cout << "parent(" << vd << ") = " << p[vd] << std::endl;
}
std::cout << std::endl;
std::ofstream dot_file("dijkstra-eg-" + date + ".dot");
dot_file << "digraph D {\n"
" rankdir=LR\n"
" size=\"6,4\"\n"
" ratio=\"fill\"\n"
" graph[label=\"shortest path on " + date + "\"];\n"
" edge[style=\"bold\"]\n"
" node[shape=\"circle\"]\n";
for (auto ed : make_iterator_range(edges(g))) {
auto u = source(ed, g),
v = target(ed, g);
dot_file
<< u << " -> " << v << "[label=\"" << get(dated_weight_map, ed) << "\""
<< (p[v] == u?", color=\"black\"" : ", color=\"grey\"")
<< "]";
}
dot_file << "}";
}
int main() {
Graph g;
std::mt19937 prng { std::random_device{}() };
generate_random_graph(g, 8, 12, prng);
uniform_real<double> weight_dist(10,42);
for (auto e : make_iterator_range(edges(g)))
for (auto&& date : { "2014-01-01", "2014-02-01", "2014-03-01" })
g[e].weights[date] = weight_dist(prng);
for (std::string const& date : { "2014-01-01", "2014-02-01", "2014-03-01" }) {
Dijkstra(date, g, 0);
}
}
Output, e.g.
¹ As long as you keep the invariants required by the algorithm you're invoking. In particular, you must return the same weight consistently during the execution, given the same edge. Also, some algorithms don't support negative weight etc.
² I'd highly suggest using a Boost ICL interval_map in such a case but I digress
³ see also map set/get requests into C++ class/structure changes

BGL: Get initial direction of edge in undirected graph

In an undirected BGL graph: Can I get the information whether an edge (u,v) == (v,u) was initially added as (u,v) or as (v,u)?
Background:
I created a graph using Pythons graph-tool library which internally uses Boost Graph Library (BGL).
Each edge has a "directed attribute" referring to source and target of the edge: (source_range, target_range).
I want to perform an undirected Depth-First search to find all paths between two nodes. I'm using graph-tools get_all_paths() as a basis. I altered the underlying BGL implementation in a way that traversal of the graph depends on the "directed attribute". I got it working for the directed case. However when I switch the graph to undirected I have the problem that I don't know the initial direction of an edge. Thus I don't know the ordering of the edge attribute:
(source_range, target_range) vs (target_range, source_range)
Here is my DFS code with the mentioned stop criterion (// Check overlap part):
template <class Graph, class Yield, class VMap, class EMap>
void get_all_paths(size_t s, size_t t, size_t cutoff, VMap visited,
EMap startend, Yield& yield, Graph& g)
{
typedef typename graph_traits<Graph>::out_edge_iterator eiter_t;
typedef std::pair<eiter_t, eiter_t> item_t;
visited[s] = true; // set visited true for source node
// could also use refrences to startend property map here. meh...
uint8_t t_start_e1, t_end_e1, q_start_e1, q_end_e1;
uint8_t q_start_e2, q_end_e2, t_start_e2, t_end_e2;
int32_t startend_e1;
int32_t startend_e2;
typedef typename property_map<Graph, vertex_index_t>::type IndexMap;
IndexMap index = get(vertex_index, g);
vector<size_t> vs = {s}; // vector of indexes
vector<item_t> stack = {out_edges(s, g)}; // vector of edge_iterator pairs
while (!stack.empty())
{
std::cout << "Stack before check overlap: ";
for (uint8_t i=0; i<stack.size(); i++) {
std::cout << " (" << source(*stack[i].first, g) << "," << target(*stack[i].first, g) << ") ";
}
std::cout << "\n";
auto& pos = stack.back(); // last element in eiter vector
// End of path because of self loop or cutoff is reached
if (pos.first == pos.second || stack.size() > cutoff)
{
visited[vs.back()] = false; // revoke visited flag for last node
vs.pop_back();
stack.pop_back();
if (!stack.empty())
++stack.back().first; // increment first iterator
continue;
}
// Check overlap
if (stack.size() > 1)
{
auto& pos_prev = *(stack.rbegin() + 1); // second last eiter
startend_e1 = startend[*pos_prev.first];
startend_e2 = startend[*pos.first];
std::cout << "Checking Edges: (" << source(*pos_prev.first, g) << "," << target(*pos_prev.first, g) << ")";
std::cout << " (" << source(*pos.first, g) << "," << target(*pos.first, g) << "):";
// take apart 2x int32_t to 8x int8_t (memory optimization)
// Undirected case:If the edge was added
// as (u,v) and (v,u) was detected
// I need to swap q(uery) and t(arget) values here.
// --> How can I detect if (u,v) was initially added as (u,v)
// or (v, u)
q_start_e1 = startend_e1 & 0xFF;
q_end_e1 = (startend_e1 >> 8) & 0xFF;
t_start_e1 = (startend_e1 >> 16) & 0xFF;
t_end_e1 = (startend_e1 >> 24) & 0xFF;
q_start_e2 = startend_e2 & 0xFF;
q_end_e2 = (startend_e2 >> 8) & 0xFF;
t_start_e2 = (startend_e2 >> 16) & 0xFF;
t_end_e2 = (startend_e2 >> 24) & 0xFF;
if ((min(t_end_e1, q_end_e2) - max(t_start_e1, q_start_e2)) < 1)
{
std::cout << "Failed\n";
++pos.first;
std::cout << "Stack after check overlap: ";
for (uint8_t i=0; i<stack.size(); i++) {
std::cout << "(" << source(*stack[i].first, g) << "," << target(*stack[i].first, g) << ") ";
}
std::cout << "\n";
continue;
}
std::cout << "Passed\n";
}
auto v = target(*pos.first, g); // get target vertex
// reached target node
if (v == t)
{
vector<size_t> path = {s}; // path vector
for (auto& ei : stack)
path.push_back(target(*ei.first, g));
yield(wrap_vector_owned<size_t>(path)); // yield path
++pos.first; // increment eiter
}
else
{
//check if node was visited
if (!visited[v]) //not visited
{
visited[v] = true;
vs.push_back(v);
stack.push_back(out_edges(v, g));
}
else // visited
{
++pos.first;
}
}
}
};
Thank you for your help!
Update:
I came up with the following workaround for my problem. I have an edge property (some_val_ref_u, some_val_ref_v) of the edge (u,v). In an undirected graph the edge (v,u) will still have the edge property (some_val_ref_u, some_val_ref_v). Thus I would assign some_val_ref_u to v and some_val_ref_v to u, which is not correct. I have to take the order into account when dealing with a "reverse edge".
The solution I came up with is to set the order dynamically when creating the graph depending on the edge index of v and u.
if edge_index[v] < edge_index[u]:
g.ep.myattr[g.edge(v,u)] = (some_val_ref_v, some_val_ref_u)
else:
g.ep.myattr[g.edge(v,u)] = (some_val_ref_u, some_val_ref_v)
So the order of the edge property tuple depends on which edge index is smaller. Consequently, when traversing the graph, I can decide the order of the edge attribute by comparing the vertex indices.
This does not directly answer my question but hopefully will be a workaround for my problem.
You can simply iterate:
for (auto ed : boost::make_iterator_range(edges(g))) {
std::cout << "Added as " << ed << " (so " << source(ed, g) << "->" << target(ed, g) << ")\n";
}
This is due to the adjacency lists storing lists of adjacencies per node.
For a directed graph this loop would effectively be equivalent to
doing:
for (auto from : boost::make_iterator_range(vertices(g))) {
for (auto to : boost::make_iterator_range(adjacent_vertices(from, g)))
std::cout << "Added as " << from << "->" << to << "\n";
}
However, for undirected graphs this would list all non-self edges duplicate.
Live On Coliru
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graph_utility.hpp>
#include <iostream>
void testcase(int from, int to) {
using namespace boost;
adjacency_list<vecS, vecS, undirectedS> g(2);
add_edge(from, to, g);
for (auto ed : boost::make_iterator_range(edges(g))) {
std::cout << "Added as " << ed << " (so " << source(ed, g) << "->" << target(ed, g) << ")\n";
}
}
int main() {
testcase(0, 1);
testcase(1, 0);
}
Prints
Added as (0,1) (so 0->1)
Added as (1,0) (so 1->0)
sehe's answer is incorrect - there is no way of obtaining original source and target vertices from any edge descriptor, as shown below:
#include <iostream>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graph_utility.hpp>
int main() {
using namespace boost;
typedef adjacency_list<vecS, vecS, undirectedS> Graph;
Graph g(2);
add_edge(0, 1, g);
for (auto ed : make_iterator_range(edges(g))) {
std::cout << "Added as " << ed << " (so " << source(ed, g) << "->" << target(ed, g) << ")\n";
}
//edge (1,0) exists since the graph is undirected,
//yet source and target vertices are not the way they were originally specified
Graph::edge_descriptor ed = edge(1, 0, g).first;
std::cout << ed << " (so " << source(ed, g) << "->" << target(ed, g) << ')';
}
One solution, as already mentioned, is to iterate through all the edges.
The other one, more efficient, is to seek for vertex v in out edges of vertex u (or the other way around):
#include <iostream>
#include <cassert>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graph_utility.hpp>
int main() {
using namespace boost;
typedef adjacency_list<vecS, vecS, undirectedS> Graph;
Graph g(2);
add_edge(0, 1, g);
Graph::EdgeContainer::const_iterator edge1 = std::find(g.out_edge_list(0).begin(), g.out_edge_list(0).end(), Graph::StoredEdge(1))->get_iter();
Graph::EdgeContainer::const_iterator edge2 = std::find(g.out_edge_list(1).begin(), g.out_edge_list(1).end(), Graph::StoredEdge(0))->get_iter();
assert(edge1 == edge2);
std::cout << source(*edge1, g) << "->" << target(*edge1, g);
}

Boost kruskals algorithm find sum of edges between vertex 0 and the one farthest from it?

I have to use kruskals algorithm in the boost library to find the weight of the minimum spanning tree. I think I managed that one
#include <iostream>
#include <boost/config.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <boost/graph/kruskal_min_spanning_tree.hpp>
#include<vector>
using namespace std;
using namespace boost;
int main(){
typedef adjacency_list <vecS,vecS,undirectedS,no_property,property <edge_weight_t,int> > Graph;
typedef graph_traits <Graph>::edge_descriptor Edge;
typedef graph_traits <Graph>::vertex_descriptor Vertex;
int a,b,c,no_vertices,no_edges;
cin>>no_vertices>>no_edges;
Graph g(no_vertices);
property_map <Graph,edge_weight_t>::type weightmap=get(edge_weight,g);
vector <Edge> spanning_tree;
for(int i=0;i<no_edges;i++)
{
bool success;
Edge e;
cin>>a>>b>>c;
tie(e,success)=add_edge(a,b,g);
weightmap[e]=c;
}
kruskal_minimum_spanning_tree(g,back_inserter(spanning_tree));
//weight of spanning tree
int ww=0;
graph_traits<Graph>::edge_iterator ei, ei_end;
for (tie(ei, ei_end) = edges(g); ei != ei_end; ++ei)
{
ww=ww+weightmap[*ei];
}
cout<<"\n"<<ww;
return 0;
}
Now I need to find the distance(sum of weights) between vertex 0 and the one farthest from it? Any hints as to how I could do it?
I was thinking of using vertex iterator, but then I store the weight in weightMap so how do I access it if I iterate through the vertices of my graph?
EDIT: I have modified my program,decided to use kruskal and prim
1.kruskal for the spanning tree weight
2.prim algorithm for the distance of each vertex from the vertex 0(in spanning tree stored in map distance)
Unfortunately something goes wrong, distance[*vertex] which is the third vertex doesnt give the answer 2,but gives one
Also the weight of spanning tree is 14 instead of 7
my dummy input is:
5 6
0 1 1
0 2 2
1 2 5
1 3 1
3 2 2
2 4 3
here my programs:
#include <boost/config.hpp>
#include <iostream>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/prim_minimum_spanning_tree.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <boost/graph/kruskal_min_spanning_tree.hpp>
using namespace std;
int
main()
{
using namespace boost;
typedef adjacency_list < vecS, vecS, undirectedS,
property<vertex_distance_t, int>, property < edge_weight_t, int > > Graph;
int num_nodes,num_edges,a,b,c;
cin>>num_nodes>>num_edges;
Graph g(num_nodes);
property_map<Graph, edge_weight_t>::type weightmap = get(edge_weight, g);
for (int j = 0; j < num_edges ; ++j) {
cin>>a>>b>>c;
graph_traits<Graph>::edge_descriptor e;
bool inserted;
tie(e, inserted) = add_edge(a, b, g);
weightmap[e] = c;
}
vector < graph_traits < Graph >::vertex_descriptor > p(num_vertices(g));
cout<<num_vertices(g);
property_map<Graph, vertex_distance_t>::type distance = get(vertex_distance, g);
property_map<Graph, vertex_index_t>::type indexmap = get(vertex_index, g);
prim_minimum_spanning_tree
(g, *vertices(g).first, &p[0], distance, weightmap, indexmap,
default_dijkstra_visitor());
vector <graph_traits<Graph>::edge_descriptor> spanning_tree;
kruskal_minimum_spanning_tree(g,back_inserter(spanning_tree));
int ww=0;
typedef graph_traits < Graph >::edge_descriptor Edge;
for (vector<Edge>::iterator et= spanning_tree.begin(); et != spanning_tree.end(); ++et)
{
ww=ww+weightmap[*et];
}
typedef graph_traits<Graph>::vertex_iterator vertex_iter;
std::pair<vertex_iter, vertex_iter> vp;
for (vp = vertices(g); vp.first != vp.second; ++vp.first)
{
cout<<distance[*vp.first];
}
prim_minimum_spanning_tree
(g, *vertices(g).first, &p[0], distance, weightmap, indexmap,
default_dijkstra_visitor());
return EXIT_SUCCESS;
}
THank you :)
I'm not really sure how to interpret the result of the Kruskal MST algorithm (in particular the edge list). Could this be what you were looking for:
int ww = 0;
for (auto const& e : spanning_tree) {
std::cout << "Traversing: " << source(e,g) << " -> " << target(e,g) << ", cost " << weightmap[e] << "\n";
ww += weightmap[e];
}
cout << "\n" << ww;
Otherwise, you'd probably want to pass a predecessor map to Kruskal and read that for your desired path.
Meanwhile see my above sketch Live On Coliru

Output BGL Edge Weights

I am trying to iterate through the edges of a graph and output their edge weights. I am confused though. I know how to output the "edges", but this is actually just a (vertex, vertex) which defines the edge. So do I index *edgePair.first into the EdgeWeightMap to get the weight of the edge starting from vertex *edgePair.first? This doesn't compile : "no match for operator<<".
#include <iostream>
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/adjacency_list.hpp>
typedef boost::property<boost::edge_weight_t, double> EdgeWeightProperty;
typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS, EdgeWeightProperty> Graph;
int main(int,char*[])
{
// Create a graph object
Graph g(2);
EdgeWeightProperty e = 5;
add_edge(0, 1, e, g);
boost::property_map<Graph, boost::edge_weight_t>::type EdgeWeightMap = get(boost::edge_weight_t(), g);
typedef boost::graph_traits<Graph>::edge_iterator edge_iter;
std::pair<edge_iter, edge_iter> edgePair;
for(edgePair = edges(g); edgePair.first != edgePair.second; ++edgePair.first)
{
std::cout << EdgeWeightMap[*edgePair.first] << " ";
}
return 0;
}
Any thoughts?
Thanks,
David
In this code, EdgeWeightProperty is declared as a vertex property rather than an edge property, and so it doesn't make sense to insert edges with that property. Try adding boost::no_property before EdgeWeightProperty in your adjacency_list typedef. Also, you might want to use get(EdgeWeightMap, *edgePair.first) rather than operator[] because that will work with more property map types.

Resources