BGL: Get initial direction of edge in undirected graph - boost
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);
}
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
Boost labeled graph doesn't delete vertices
I am using boost::labeled_graph in order to get vertices by their name. But labeled_graph doesn't have a remove_vertex_by_label function, so I remove vertices like in a code below. But vertex_by_label return nonexistent vertex after deletion. #include <string.h> #include <iostream> #include <boost/graph/adjacency_list.hpp> #include <boost/graph/labeled_graph.hpp> using namespace std; using namespace boost; typedef adjacency_list< listS, vecS, undirectedS > Graph; typedef labeled_graph< Graph, string > LabeledGraph; int main(int argc, char* argv[]) { LabeledGraph lg; vector<string> names{"a", "b", "c", "d", "e"}; for(auto& name : names) add_vertex(name, lg); remove_vertex(vertex_by_label("c", lg), lg.graph()); cout << "num_vertices = " << num_vertices(lg) << endl; for(auto& name : names) cout << name + " = " << vertex_by_label(name, lg) << endl; for(auto v : make_iterator_range(vertices(lg.graph()))) cout << v << endl; } And the output is: num_vertices = 4 a = 0 b = 1 c = 2 d = 3 e = 4 0 1 2 3 The questions are: 1) Why does vertex_by_label return nonexistent vertex, while there are 4 vertices (vertex was successfully deleted)? UPD: It happened that labeled_graph has a bug - https://svn.boost.org/trac10/ticket/9493 Unfortunately, the current implementation has a serious bug that might lead to a crash. The problem appears when removing a vertex from labeled_graph by its label. My investigation shown that despite of vertex being actually removed, the label is not and it still refers to the removed vertex. 2) Is there any way to delete a vertex from a labeled_graph? 3) If no, then what is the most convenient method to keep track of vertices by their names? Saving maps from strings to vertex descriptors is not an option, since the documentation says that old vertex descriptors become invalid after vertex deletion (this is happening when vecS is a vertex list container).
The problem is clear from what you write: remove_vertex(vertex_by_label("c", lg), lg.graph()); You wrote lg.graph(), not lg. So you shouldn't expect anything to get removed from lg. You expressly asked to remove something from the underlying graph model only. Here's the correct version: lg.remove_vertex("c"); For maximum convenience, you can use the overloaded free function: remove_vertex("c", lg); NOTE On Iterator Stability I haven't looked at the implementation, but it looks like the labeled_graph<> adaptor relies on iterator stability for the underlying graph. Using adjacency_list<> with vecS for the vertex container selector documents all subsequent iterators and references to be invalidated on removal of a vertex. Here's a fixed demo using listS as the vertex container selector, and it does exactly what you expect. Note: I used a vertex property bundle as the "friendly" vertex id so you don't have to print out raw vertex-descriptors. Live On Coliru #include <iostream> #include <string.h> #include <boost/graph/adjacency_list.hpp> #include <boost/graph/labeled_graph.hpp> using namespace boost; typedef adjacency_list<listS, listS, undirectedS, int> Graph; typedef labeled_graph<Graph, std::string> LabeledGraph; int main() { LabeledGraph lg; auto vid = get(vertex_bundle, lg.graph()); int id = 0; for (std::string name : { "a", "b", "c", "d", "e" }) add_vertex(name, id++, lg); std::cout << "===================\nnum_vertices = " << num_vertices(lg) << "\n"; for (std::string name : { "a", "b", "c", "d", "e" }) std::cout << name + " = " << vid[vertex_by_label(name, lg)] << "\n"; for (auto v : make_iterator_range(vertices(lg))) std::cout << vid[v] << " "; std::cout << "\n"; // lg.remove_vertex("c"); remove_vertex("c", lg); std::cout << "===================\nnum_vertices = " << num_vertices(lg) << "\n"; for (std::string name : { "a", "b", /* "c",*/ "d", "e" }) std::cout << name + " = " << vid[vertex_by_label(name, lg)] << "\n"; for (auto v : make_iterator_range(vertices(lg))) std::cout << vid[v] << " "; std::cout << "\n"; } Prints =================== num_vertices = 5 a = 0 b = 1 c = 2 d = 3 e = 4 0 1 2 3 4 =================== num_vertices = 4 a = 0 b = 1 d = 3 e = 4 0 1 3 4
Boost Mem_fn and accessing member function of derived class
I made a simple example to test boost bind's interaction with derived classes. I created two subclasses with different getarea functions. I expected g1 = boost::bind(boost::mem_fn(&Shape::getarea), Rec) to print the area of Rectangle(10,20) but instead it printed '1'. I get the same when I instead write Rectangle::getarea. It prints the same even when I input other functions eg. member of Rectangle double sum(double h,double w){return h+w; } and use g1 = boost::bind(boost::mem_fn(&Rectangle::sum), Rec,2,3) Question 1: Why does it return '1'?Is that a default response for error? My second problem is to do the same of printing g2 but now Rec is replaced by **iter, i.e. an object of some derived class type from a list of objects. Since getarea is a virtual fcn, once I get the above working it should be fine to just write: g2= boost::bind(boost::mem_fn(& Shape::getarea , &(**iter)); Question 2: However, I was wondering if there is a way to return the classtype of **iter eg. classof(**iter) and then put it in g2 i.e. g2= boost::bind(boost::mem_fn(& classof(**iter)::getarea , &(**iter)); When I ran g2 by writing Shape::getarea, I got '1' again for all iter. #include <memory> #include <vector> #include <string> #include <iostream> #include <sstream> #include <boost/bind.hpp> using namespace std; class Shape { public: Shape(double h, double w) :height(h), width(w) {}; virtual double getarea() = 0; double height; double width; }; class Rectangle: public Shape { public: Rectangle(double h, double w): Shape(h,w) {}; double getarea() override { return height*width; } }; class Triangle : public Shape { public: Triangle(double h, double w) :Shape(h,w) {}; double getarea() { return height*width*0.5; }}; int main() { //create objects Rectangle Rec(10, 20); Triangle Tri(2, 3); //create boost bind function boost::function<double(double, double)> g1; g1 = boost::bind(boost::mem_fn(&Shape::getarea), Rec); //print area and g cout << Rec.getarea()<<" should be equal to " << g1<< '\n'; //create list vector<shared_ptr<Shape>> Plist; Plist.push_back(make_shared<Rectangle>(Rec)); Plist.push_back(make_shared<Triangle>(Tri)); //print each element from the vector list for (auto iter = Plist.begin(); iter != Plist.end(); iter ++ ) { boost::function<double(double, double)> g2; g2= boost::bind(boost::mem_fn(& .... , &(**iter)); //where in dots we need Classtype_of_**iter::getarea cout << (**iter).getarea()<<"should be equal to " << g2<< '\n'; } }
You... forget to invoke the functions... for (auto iter = Plist.begin(); iter != Plist.end(); iter++) { boost::function<double()> g2; g2 = boost::bind(&Shape::getarea, iter->get()); cout << (*iter)->getarea() << " should be equal to " << g2() << '\n'; } What you saw what the implicit conversion to bool (http://www.boost.org/doc/libs/1_60_0/doc/html/boost/function.html#idm45507164686720-bb) Note also I fixed the signature of g1 and g2: Live On Coliru. Some further improvements (remove the need for the g2 in the loop?): auto getarea = boost::mem_fn(&Shape::getarea); for (auto iter = Plist.begin(); iter != Plist.end(); iter++) { cout << (*iter)->getarea() << " should be equal to " << getarea(**iter) << '\n'; } Or, indeed in c++11: for (auto& s : Plist) cout << s->getarea() << " should be equal to " << getarea(*s) << '\n'; By this time, you'd wonder why you have this accessor when you can just use the member.
How to use get() boost graph
I am trying to implement my own A star algorithm. However, I've only come up with a way to get my edge weight values through iterators. Since Iterators access the property map in order, I cannot prioritize edges and need to do a full loop for each vertex to find the neighbors, this would make the algorithm really slow. Is there any way to use get(), put()... without iterators, selecting the vertices of which I want to check the edges? This is my program at the moment: struct Point {//struct point with vertex properties int x, y; int parentx, parenty; double g; double h; friend std::ostream& operator<<(std::ostream& os, Point p) { return os << "[" << p.x << "," << p.y << "]"; } }; int main() { //declarations typedef property < edge_weight_t, double >Weight; using std::vector;//? using Graph = adjacency_list<setS, vecS, undirectedS, Point, Weight>;//graph includes our created point struct property<edge_weight_t using vertex_descriptor = Graph::vertex_descriptor; Graph lattuce; //lattuce graph is created with weighted edges value 1 or 1,41 if diagonal. The functions used on a loop are: //add_edge(nodes[p.x][p.y],nodes[neighbour.x][neighbour.y], Weight(1.0), lattuce); //add_edge(nodes[p.x][p.y],nodes[neighbour.x][neighbour.y], Weight(1.4), lattuce); typedef Graph::edge_iterator EdgeIterator; std::pair<EdgeIterator, EdgeIterator> edges = boost::edges(lattuce); typedef boost::property_map<Graph, boost::edge_weight_t>::type WeightMap; WeightMap weights = boost::get(boost::edge_weight_t(), lattuce); //cout<<get(weights,(1,2)); EdgeIterator edge; for (edge = edges.first; edge != edges.second; ++edge) { std::cout << boost::get(weights, *edge) <<" "<< *edge<< " "; if (source(*edge, lattuce) == origin || target(*edge, lattuce)==origin ){ get(weights, *edge); cout<<"Edge related to the origin "; } if (source(*edge, lattuce) == end_vertex || target(*edge, lattuce)==end_vertex ){ cout<<"Edge related to the end_vertex "; } if (get(weights, *edge)==1.41){ cout<<" Diagonal"<<endl; } else if (get(weights, *edge)==1){ cout<<" Unitary"<<endl; } else if (get(weights, *edge)==99999999.0){ cout<<"Infinite"<<endl; } } } Also, would like to be able to modify the values of f, h and parent within a Point struct. Any help or guidance will be greatly appreciated.
Answering your questions You can use vertex descriptors instead of the iterators. In your case, because you are using bundled properties, you can use the operator[descriptor] shorthand on the graph. You can use this to alter the bundled properties as long as your graph is not const. Code It's not actually clear to me what the code in your question was supposed to illustrate. I'll simply reply with a similarly dissociated piece of code that built from there and shows you a few things that might be enlightening :) Live On Coliru #include <boost/graph/adjacency_list.hpp> #include <boost/graph/astar_search.hpp> #include <boost/graph/random.hpp> #include <iostream> #include <random> static std::mt19937 s_rng { std::random_device{}() }; static std::uniform_int_distribution<> s_coord(-10,10); static std::uniform_real_distribution<double> s_double(-1.,1.); static int gen_coord() { return s_coord(s_rng); } static int gen_gh () { return s_double(s_rng); } struct Point { // struct point with vertex properties int x = gen_coord(), y = gen_coord(); int parentx = gen_coord(), parenty = gen_coord(); double g = gen_gh(); double h = gen_gh(); friend std::ostream &operator<<(std::ostream &os, Point p) { return os << "[" << p.x << "," << p.y << "]"; } }; // declarations typedef boost::property<boost::edge_weight_t, double> Weight; using Graph = boost::adjacency_list<boost::setS, boost::vecS, boost::undirectedS, Point, Weight>; using vertex_descriptor = Graph::vertex_descriptor; int main() { Graph lattuce; using namespace boost; generate_random_graph(lattuce, 10, 20, s_rng); vertex_descriptor origin = vertex(0, lattuce); vertex_descriptor end_vertex = vertex(9, lattuce); typedef boost::property_map<Graph, boost::edge_weight_t>::type WeightMap; WeightMap weights = boost::get(boost::edge_weight_t(), lattuce); for (Graph::edge_descriptor edge : make_iterator_range(edges(lattuce))) { std::cout << boost::get(weights, edge) << " " << edge << " "; vertex_descriptor src = source(edge, lattuce); vertex_descriptor trg = target(edge, lattuce); Point& src_bundle = lattuce[src]; Point& trg_bundle = lattuce[trg]; std::cout << "Edge: " << src_bundle << " -- " << trg_bundle << "\n"; src_bundle.g *= 1.1; src_bundle.h *= -1.1; //double weight = get(weights, edge); } (void) origin; // unused (void) end_vertex; // unused } Printing, for example: 0 (7,5) Edge: [-5,-5] -- [-9,-6] 0 (9,2) Edge: [3,2] -- [-3,3] 0 (3,4) Edge: [-4,-10] -- [-10,-5] 0 (1,6) Edge: [3,4] -- [-7,7] 0 (9,4) Edge: [3,2] -- [-10,-5] 0 (3,8) Edge: [-4,-10] -- [-6,0] 0 (1,9) Edge: [3,4] -- [3,2] 0 (5,9) Edge: [-9,-6] -- [3,2] 0 (6,9) Edge: [-7,7] -- [3,2] 0 (0,1) Edge: [-9,8] -- [3,4] 0 (1,4) Edge: [3,4] -- [-10,-5] 0 (0,2) Edge: [-9,8] -- [-3,3] 0 (9,3) Edge: [3,2] -- [-4,-10] 0 (2,4) Edge: [-3,3] -- [-10,-5] 0 (7,6) Edge: [-5,-5] -- [-7,7] 0 (1,2) Edge: [3,4] -- [-3,3] 0 (0,9) Edge: [-9,8] -- [3,2] 0 (4,7) Edge: [-10,-5] -- [-5,-5] 0 (1,5) Edge: [3,4] -- [-9,-6] 0 (0,7) Edge: [-9,8] -- [-5,-5]
Longest Path in Boost Graph
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.