BGL bundled edge properties containing vector [duplicate] - boost

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

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

Conversion of data type using auto in C++

I have 2 vector container which contains 2 different kind of value with data type uint32_t. I want to print both of them together.
Like this is what I have
vector<uint32_t> data1;
vector<uint32_t> data2;
Now I know a method for single data like below
for(auto const& d1: data1)
cout<< d1 << endl;
But I want to print both data together like this,
cout<< d1 << "\t" << d2 << endl;
How can I do this using auto? (where d2 is auto converted value from data2)
You could use a normal for loop over the index:
for (auto i = 0u; i != n; ++i)
std::cout << data1[i] << "\t" << data2[i] << "\n";
Edit: if you want to convert the uint32_t to an int, for example, you could do:
auto d1 = static_cast<int>(data1[i]);
but it is up to you to ensure the conversion is safe. i.e the value fits in the target type.
Use the Boost Zip Iterator, which will let you have a range of pairs rather than two ranges of the vectors' data types. Something along the lines of:
#include <boost/iterator/zip_iterator.hpp>
#include <boost/range.hpp>
#include <stdint.h>
#include <vector>
#include <iostream>
template <typename... TContainer>
auto zip(TContainer&... containers) -> boost::iterator_range<boost::zip_iterator<decltype(boost::make_tuple(std::begin(containers)...))>> {
auto zip_begin = boost::make_zip_iterator(boost::make_tuple(std::begin(containers)...));
auto zip_end = boost::make_zip_iterator(boost::make_tuple(std::end(containers)...));
return boost::make_iterator_range(zip_begin, zip_end);
}
int main()
{
std::vector<uint32_t> data1( { 11, 22, 33 } );
std::vector<uint32_t> data2( { 44, 55, 66 } );
for (auto t : zip(data1, data2)) {
std::cout << boost::get<0>(t) << "\t" << boost::get<1>(t) << "\n";
}
}
The zip() function is due to this question and you can put it in a separate header file since it's not specific to your case.
If possible (and plausible for your use case): work with a container of pairs
If your application is not in a bind w.r.t. computer resources, and you know that you will be working with the values of your two containers as pairs (assuming same-length containers, as in your example), it might be useful to actually work with a container of pairs, which also ease the use of the neat range-based for loops ( >= C++11).
#include <iostream>
#include <vector>
#include <algorithm>
int main()
{
std::vector<uint32_t> data1 = {1, 2, 3};
std::vector<uint32_t> data2 = {4, 5, 6};
// construct container of (int, int) pairs
std::vector<std::pair<int, int>> data;
data.reserve(data1.size());
std::transform(data1.begin(), data1.end(), data2.begin(), std::back_inserter(data),
[](uint32_t first, uint32_t second) {
return std::make_pair(static_cast<int>(first), static_cast<int>(second));
}); /* as noted in accepted answer: you're responsible for
ensuring that the conversion here is safe */
// easily use range-based for loops to traverse of the
// pairs of your container
for(const auto& pair: data) {
std::cout << pair.first << " " << pair.second << "\n";
} /* 1 4
2 5
3 6 */
return 0;
}

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.

Is it possible to have several edge weight property maps for one graph?

How would I create a graph, such that the property map (weight of edges) is different in each property map? Is it possible to create such a property map?
Like an array of property maps?
I have not seen anyone on the Internet using it, could I have an example?
Graph g(10); // graph with 10 nodes
cin>>a>>b>>weight1>>weight2>>weight3>>weight4;
and put each weight in a property map.
You can compose a property map in various ways. The simplest approach would seem something like:
Using C++11 lambdas with function_property_map
Live On Coliru
#include <boost/property_map/function_property_map.hpp>
#include <iostream>
struct weights_t {
float weight1, weight2, weight3, weight4;
};
using namespace boost;
int main() {
std::vector<weights_t> weight_data { // index is vertex id
{ 1,2,3,4 },
{ 5,6,7,8 },
{ 9,10,11,12 },
{ 13,14,15,16 },
};
auto wmap1 = make_function_property_map<unsigned, float>([&weight_data](unsigned vertex_id) { return weight_data.at(vertex_id).weight1; });
auto wmap2 = make_function_property_map<unsigned, float>([&weight_data](unsigned vertex_id) { return weight_data.at(vertex_id).weight2; });
auto wmap3 = make_function_property_map<unsigned, float>([&weight_data](unsigned vertex_id) { return weight_data.at(vertex_id).weight3; });
auto wmap4 = make_function_property_map<unsigned, float>([&weight_data](unsigned vertex_id) { return weight_data.at(vertex_id).weight4; });
for (unsigned vertex = 0; vertex < weight_data.size(); ++vertex)
std::cout << wmap1[vertex] << "\t" << wmap2[vertex] << "\t" << wmap3[vertex] << "\t"<< wmap4[vertex] << "\n";
}
Using C++03 with transform_value_property_map
This is mainly much more verbose:
Live On Coliru
#include <boost/property_map/transform_value_property_map.hpp>
#include <iostream>
struct weights_t {
float weight1, weight2, weight3, weight4;
weights_t(float w1, float w2, float w3, float w4)
: weight1(w1), weight2(w2), weight3(w3), weight4(w4)
{ }
template <int which> struct access {
typedef float result_type;
float operator()(weights_t const& w) const {
BOOST_STATIC_ASSERT(which >= 1 && which <= 4);
switch (which) {
case 1: return w.weight1;
case 2: return w.weight2;
case 3: return w.weight3;
case 4: return w.weight4;
}
}
};
};
using namespace boost;
int main() {
std::vector<weights_t> weight_data; // index is vertex id
weight_data.push_back(weights_t(1,2,3,4));
weight_data.push_back(weights_t(5,6,7,8));
weight_data.push_back(weights_t(9,10,11,12));
weight_data.push_back(weights_t(13,14,15,16));
boost::transform_value_property_map<weights_t::access<1>, weights_t*, float> wmap1 = make_transform_value_property_map(weights_t::access<1>(), &weight_data[0]);
boost::transform_value_property_map<weights_t::access<2>, weights_t*, float> wmap2 = make_transform_value_property_map(weights_t::access<2>(), &weight_data[0]);
boost::transform_value_property_map<weights_t::access<3>, weights_t*, float> wmap3 = make_transform_value_property_map(weights_t::access<3>(), &weight_data[0]);
boost::transform_value_property_map<weights_t::access<4>, weights_t*, float> wmap4 = make_transform_value_property_map(weights_t::access<4>(), &weight_data[0]);
for (unsigned vertex = 0; vertex < weight_data.size(); ++vertex)
std::cout << wmap1[vertex] << "\t" << wmap2[vertex] << "\t" << wmap3[vertex] << "\t"<< wmap4[vertex] << "\n";
}
Output
Both samples output
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16

Implementing equivalence relations in C++ (using boost::disjoint_sets)

Assume you have many elements, and you need to keep track of the equivalence relations between them. If element A is equivalent to element B, it is equivalent to all the other elements B is equivalent to.
I am looking for an efficient data structure to encode this information. It should be possible to dynamically add new elements through an equivalence with an existing element, and from that information it should be possible to efficiently compute all the elements the new element is equivalent to.
For example, consider the following equivalence sets of the elements [0,1,2,3,4]:
0 = 1 = 2
3 = 4
where the equality sign denotes equivalence. Now we add a new element 5
0 = 1 = 2
3 = 4
5
and enforcing the equivalence 5=3, the data structure becomes
0 = 1 = 2
3 = 4 = 5
From this, one should be able to iterate efficiently through the equivalence set for any element. For 5, this set would be [3,4,5].
Boost already provides a convenient data structure called disjoint_sets that seems to meet most of my requirements. Consider this simple program that illustates how to implement the above example:
#include <cstdio>
#include <vector>
#include <boost/pending/disjoint_sets.hpp>
#include <boost/unordered/unordered_set.hpp>
/*
Equivalence relations
0 = 1 = 2
3 = 4
*/
int main(int , char* [])
{
typedef std::vector<int> VecInt;
typedef boost::unordered_set<int> SetInt;
VecInt rank (100);
VecInt parent (100);
boost::disjoint_sets<int*,int*> ds(&rank[0], &parent[0]);
SetInt elements;
for (int i=0; i<5; ++i) {
ds.make_set(i);
elements.insert(i);
}
ds.union_set(0,1);
ds.union_set(1,2);
ds.union_set(3,4);
printf("Number of sets:\n\t%d\n", (int)ds.count_sets(elements.begin(), elements.end()));
// normalize set so that parent is always the smallest number
ds.normalize_sets(elements.begin(), elements.end());
for (SetInt::const_iterator i = elements.begin(); i != elements.end(); ++i) {
printf("%d %d\n", *i, ds.find_set(*i));
}
return 0;
}
As seen above one can efficiently add elements, and dynamically expand the disjoint sets. How can one efficiently iterate over the elements of a single disjoint set, without having to iterate over all the elements?
Most probably you can't do that, disjoint_sets doesn't support iteration over one set only. The underlying data structure and algorithms wouldn't be able to do it efficiently anyway, i.e. even if there was support built in to disjoint_sets for iteration over one set only, that would be just as slow as iterating over all sets, and filtering out wrong sets.
Either I am missing something, you forgot to mention something, or maybe you were overthinking this ;)
Happily, equivalence is not equality. For A & B to be equivalent; they only need to share an attribute with the same value. this could be a scalar or even a vector. Anyway, I think your posted requirements can be achieved just using std::multiset and it's std::multiset::equal_range() member function.
//////////////////////////////////////
class E
{
//could be a GUID or something instead but the time complexity of
//std::multiset::equal_range with a simple int comparison should be logarithmic
static size_t _faucet;
public:
struct LessThan
{
bool operator()(const E* l, const E* r) const { return (l->eqValue() < r->eqValue()); }
};
using EL=std::vector<const E*>;
using ES=std::multiset<const E*, E::LessThan>;
using ER=std::pair<ES::iterator, ES::iterator>;
static size_t NewValue() { return ++_faucet; }
~E() { eqRemove(); }
E(size_t val) : _eqValue(val) {}
E(std::string name) : Name(name), _eqValue(NewValue()) { E::Elementals.insert(this); }
//not rly a great idea to use operator=() for this. demo only..
const E& operator=(const class E& other) { eqValue(other); return *this; }
//overriddable default equivalence interface
virtual size_t eqValue() const { return _eqValue; };
//clearly it matters how mutable you need your equivalence relationships to be,,
//in this implementation, if an element's equivalence relation changes then
//the element is going to be erased and re-inserted.
virtual void eqValue(const class E& other)
{
if (_eqValue == other._eqValue) return;
eqRemove();
_eqValue=other._eqValue;
E::Elementals.insert(this);
};
ES::iterator eqRemove()
{
auto range=E::Elementals.equal_range(this);
//worst-case complexity should be aprox linear over the range
for (auto it=range.first; it!=range.second; it++)
if (this == (*it))
return E::Elementals.erase(it);
return E::Elementals.end();
}
std::string Name; //some other attribute unique to the instance
static ES Elementals; //canonical set of elements with equivalence relations
protected:
size_t _eqValue=0;
};
size_t E::_faucet=0;
E::ES E::Elementals{};
//////////////////////////////////////
//random specialisation providing
//dynamic class-level equivalence
class StarFish : public E
{
public:
static void EqAssign(const class E& other)
{
if (StarFish::_id == other.eqValue()) return;
E el(StarFish::_id);
auto range=E::Elementals.equal_range(&el);
StarFish::_id=other.eqValue();
E::EL insertList(range.first, range.second);
E::Elementals.erase(range.first, range.second);
E::Elementals.insert(insertList.begin(), insertList.end());
}
StarFish() : E("starfish") {}
//base-class overrides
virtual size_t eqValue() const { return StarFish::_id; };
protected: //equivalence is a the class level
virtual void eqValue(const class E& other) { assert(0); }
private:
static size_t _id;
};
size_t StarFish::_id=E::NewValue();
//////////////////////////////////////
void eqPrint(const E& e)
{
std::cout << std::endl << "elementals equivalent to " << e.Name << ": ";
auto range=E::Elementals.equal_range(&e);
for (auto it=range.first; it!=range.second; it++)
std::cout << (*it)->Name << " ";
std::cout << std::endl << std::endl;
}
//////////////////////////////////////
void eqPrint()
{
for (auto it=E::Elementals.begin(); it!=E::Elementals.end(); it++)
std::cout << (*it)->Name << ": " << (*it)->eqValue() << " ";
std::cout << std::endl << std::endl;
}
//////////////////////////////////////
int main()
{
E e0{"zero"}, e1{"one"}, e2{"two"}, e3{"three"}, e4{"four"}, e5{"five"};
//per the OP
e0=e1=e2;
e3=e4;
e5=e3;
eqPrint(e0);
eqPrint(e3);
eqPrint(e5);
eqPrint();
StarFish::EqAssign(e3);
StarFish starfish1, starfish2;
starfish1.Name="fred";
eqPrint(e3);
//re-assignment
StarFish::EqAssign(e0);
e3=e0;
{ //out of scope removal
E e6{"six"};
e6=e4;
eqPrint(e4);
}
eqPrint(e5);
eqPrint(e0);
eqPrint();
return 0;
}
online demo
NB: C++ class inheritance also provides another kind of immutable equivalence that can be quite useful ;)

Resources