Please help me with Boost Graph Library - parallel-processing

I want to use the dense_boruvka_minimum_spanning_tree function in Boost Graph Library, C++. What I want to do is to generate a random graph and then input it into that function to run the Boruvka's algorithm in parallel. Could anybody help me with couple of lines of code as for how the function is used?

Does this help?
typedef adjacency_list<listS,
distributedS<mpi_process_group, vecS>,
undirectedS,
no_property,
property<edge_weight_t, int> > Graph;
typedef graph_traits<Graph>::vertex_descriptor vertex_descriptor;
typedef graph_traits<Graph>::vertex_iterator vertex_iterator;
typedef graph_traits<Graph>::edge_descriptor edge_descriptor;
typedef std::pair<int, int> E;
const int num_nodes = 5;
E edge_array[] = { E(0, 2), E(1, 3), E(1, 4), E(2, 1), E(2, 3),
E(3, 4), E(4, 0), E(4, 1)
};
int weights[] = { 1, 1, 2, 7, 3, 1, 1, 1 };
std::size_t num_edges = sizeof(edge_array) / sizeof(E);
Graph g(edge_array, edge_array + num_edges, weights, num_nodes);
typedef property_map<Graph, edge_weight_t>::type WeightMap;
WeightMap weight_map = get(edge_weight, g);
std::vector<edge_descriptor> mst_edges;
dense_boruvka_minimum_spanning_tree(make_vertex_list_adaptor(g),
weight_map,
std::back_inserter(mst_edges));

Related

Boost A* throwing segmentation fault

this is the continuation of another question I asked regarding boost graphs. (GraphML in Boost).
After successfully reading the graph, I am trying to apply boost A* on some random start and goal nodes but its throwing segmentation fault.
Following are the details of my graph.
using Graph = boost::adjacency_list<boost::setS, boost::vecS, boost::undirectedS, VertexProperties, EdgeProperties>;
struct VertexProperties {
std::vector<double> joint_angles;
VertexProperties() : joint_angles(3){}
};
struct EdgeProperties {
double weight;
};
I used A* cities file from Boost as reference (A* Cities) to code my distance heuristic and astar_goal_visitor.
struct found_goal {}; // exception for termination
// visitor that terminates when we find the goal
template <typename Vertex>
class astar_goal_visitor : public boost::default_astar_visitor
{
public:
astar_goal_visitor(Vertex goal) : m_goal(goal) {}
template <class Graph>
void examine_vertex(Vertex u, Graph& g) {
if(u == m_goal)
throw found_goal();
}
private:
Vertex m_goal;
};
// euclidean distance heuristic
template <class Graph>
class distance_heuristic : public boost::astar_heuristic<typename Graph::Graph, double>
{
public:
typedef typename boost::graph_traits<typename Graph::Graph>::vertex_descriptor Vertex;
distance_heuristic(Vertex goal, Graph &graph)
: m_goal(goal), m_graph(graph) {}
double operator()(Vertex u)
{
double dx = m_graph.getGraph()[m_goal].joint_angles[0] - m_graph.getGraph()[u].joint_angles[0];
double dy = m_graph.getGraph()[m_goal].joint_angles[1] - m_graph.getGraph()[u].joint_angles[1];
double dz = m_graph.getGraph()[m_goal].joint_angles[2] - m_graph.getGraph()[u].joint_angles[2];
return ::sqrt(dx * dx + dy * dy + dz * dz);
}
private:
Graph m_graph;
Vertex m_goal;
};
As for astar_search parameters, the predecessor map is defined as below.
typedef boost::property_map < Graph, boost::vertex_index_t >::type IndexMap;
typedef boost::iterator_property_map < Vertex*, IndexMap, Vertex, Vertex& > PredecessorMap;
BoostGraph::PredecessorMap BoostGraph::getPredecessorMap(){
IndexMap indexMap = boost::get(boost::vertex_index, graph);
std::vector<Vertex> p(boost::num_vertices(graph));
PredecessorMap predecessorMap(&p[0], indexMap);
return predecessorMap;
}
The final code for search is
std::vector<double> d(boost::num_vertices(graph.getGraph()));
std::mt19937 gen(time(0));
BoostGraph::Vertex start = boost::random_vertex(graph.getGraph(), gen);
BoostGraph::Vertex goal = boost::random_vertex(graph.getGraph(), gen);
auto weightmap = boost::get(&EdgeProperties::weight, graph.getGraph());
try {
// call astar named parameter interface
boost::astar_search
(graph.getGraph(), start,
distance_heuristic<BoostGraph>
(goal, graph),
boost::predecessor_map(graph.getPredecessorMap()).distance_map(boost::make_iterator_property_map(d.begin(), boost::get(boost::vertex_index, graph.getGraph()))).
weight_map(weightmap).
visitor(astar_goal_visitor<BoostGraph::Vertex>(goal)));
} catch(found_goal fg) { // found a path to the goal
std::list<BoostGraph::Vertex> shortest_path;
for(BoostGraph::Vertex v = goal;; v = p[v]) {
shortest_path.push_front(v);
if(p[v] == v)
break;
}
}
The getGraph function of Class BoostGraph is defined below where graph is the protected member of class BoostGraph.
protected:
Graph graph;
const BoostGraph::Graph& BoostGraph::getGraph() const{
return graph;
}
The segmentation fault is coming in stl_tree.h and I have no idea what has gone wrong. Any help would be appreciated. Thanks
Your heuristic holds a copy of the graph. You're indexing using vertex descriptors belonging to different graphs.
Your predecessor map is a local variable (the vector), and the map is a dangling reference to it after getPredecessorMap returns. Just make the vector a member, and then getPredecessorMap can be eliminated, because it doesn't really add much.
Also, you're indexing into joint_angles without bounds checking. Prefer .at(n) over [n] if you want to be safer. In fact, consider using std::array<double, 3> instead of std::vector<double>.
All in all I get the impression that you've been trying to hide complexity in a class and member functions, however it leads to the code becoming fragmented and inviting lots of unnecessary lifetime issues.
There are also parts of the code you do not show. They likely hold more problems (e.g. getGraph() is crucial).
Here's my simplified take:
Coliru
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/astar_search.hpp>
#include <boost/graph/random.hpp>
using JointAngles = std::vector<double>;
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;
// 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{};
}
};
// euclidean distance heuristic
struct distance_heuristic : boost::astar_heuristic<Graph, double> {
distance_heuristic(Vertex goal, Graph& graph)
: m_graph(graph)
, m_goal(graph[goal].joint_angles) {}
double operator()(Vertex u) const {
auto& c = m_graph[u].joint_angles;
auto //
dx = m_goal.at(0) - c.at(0), //
dy = m_goal.at(1) - c.at(1), //
dz = m_goal.at(2) - c.at(2);
using std::sqrt; // allow ADL, good practice
return sqrt(dx * dx + dy * dy + dz * dz);
}
private:
Graph& m_graph; // reference!
JointAngles m_goal;
};
#include <random>
#include <fmt/ranges.h>
int main() {
Graph graph(4);
graph[0] = VertexProperties{{0, 0, 0}};
graph[1] = VertexProperties{{1, 1, 1}};
graph[2] = VertexProperties{{2, 2, 2}};
add_edge(0, 1, graph);
add_edge(1, 2, graph);
std::vector<Vertex> predecessors(num_vertices(graph));
std::vector<double> distances(num_vertices(graph));
auto index = get(boost::vertex_index, graph);
auto pmap = make_safe_iterator_property_map(predecessors.begin(), predecessors.size(), index);
auto dmap = make_safe_iterator_property_map(distances.begin(), distances.size(), index);
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
astar_search( //
graph, start, distance_heuristic{goal, graph},
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);
}
}
Which prints e.g.
2 -> 1: [2, 1]
Encapsulation?
If you want to encapsulate, do it along the functional boundaries, instead of artificial boundaries that gave you the lifetime headaches you didn't need. If performance is no concern, you can reduce code with a facility like vector_property_map. For example:
Live On Coliru
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/astar_search.hpp>
#include <boost/graph/graph_utility.hpp>
#include <boost/graph/random.hpp>
#include <boost/property_map/transform_value_property_map.hpp>
#include <boost/property_map/vector_property_map.hpp>
#include <fmt/ranges.h>
#include <random>
class BoostGraph {
private:
using JointAngles = std::vector<double>;
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;
public:
BoostGraph() : m_graph(4) {
// TODO read graph
m_graph[0] = VertexProperties{{0, 0, 0}};
m_graph[1] = VertexProperties{{1, 1, 1}};
m_graph[2] = VertexProperties{{2, 2, 2}};
add_edge(0, 1, m_graph);
add_edge(1, 2, m_graph);
}
friend std::ostream& operator<<(std::ostream& os, BoostGraph const& bg) {
auto name = boost::make_transform_value_property_map(
[ja = get(&VertexProperties::joint_angles, bg.m_graph)](Vertex v) {
return fmt::format("Vertex #{} ({})", v, ja[v]);
},
get(boost::vertex_index, bg.m_graph));
print_graph(bg.m_graph, name, os);
return os;
}
Vertex random_vertex() { return boost::random_vertex(m_graph, m_prng); }
std::list<Vertex> find_path(Vertex start, Vertex goal) const {
std::list<Vertex> path;
auto pmap = make_vector_property_map<Vertex>(get(boost::vertex_index, m_graph));
try {
astar_search( //
m_graph, start, distance_heuristic{goal, m_graph},
boost::predecessor_map(pmap) //
.weight_map(get(&EdgeProperties::weight, m_graph))
.visitor(finder{goal}));
} catch (finder::found) {
for (auto cursor = goal;;) {
path.push_front(cursor);
auto previous = std::exchange(cursor, pmap[cursor]);
if (cursor == previous)
break;
}
}
return path;
}
private:
// visitor that terminates when we find the goal
struct finder : boost::default_astar_visitor {
struct found {}; // exception for termination
Vertex m_goal;
finder(Vertex g) : m_goal(g) {}
void examine_vertex(Vertex u, Graph const&) const {
if (u == m_goal)
throw found{};
}
};
// euclidean distance heuristic
struct distance_heuristic : boost::astar_heuristic<Graph, double> {
distance_heuristic(Vertex goal, Graph const& graph)
: m_graph(graph)
, m_goal(graph[goal].joint_angles) {}
double operator()(Vertex u) const {
auto& c = m_graph[u].joint_angles;
auto //
dx = m_goal.at(0) - c.at(0), //
dy = m_goal.at(1) - c.at(1), //
dz = m_goal.at(2) - c.at(2);
using std::sqrt; // allow ADL, good practice
return sqrt(dx * dx + dy * dy + dz * dz);
}
private:
Graph const& m_graph; // reference!
JointAngles m_goal;
};
Graph m_graph;
std::mt19937 m_prng{std::random_device{}()};
};
int main() {
BoostGraph bg;
std::cout << "Graph: " << bg << "\n";
for (auto i = 0; i < 10; ++i) {
auto start = bg.random_vertex(), goal = bg.random_vertex();
auto path = bg.find_path(start, goal);
fmt::print("{} -> {}: {}\n", start, goal, path);
}
}
Printing e.g.
Graph: Vertex #0 ([0, 0, 0]) <--> Vertex #1 ([1, 1, 1])
Vertex #1 ([1, 1, 1]) <--> Vertex #0 ([0, 0, 0]) Vertex #2 ([2, 2, 2])
Vertex #2 ([2, 2, 2]) <--> Vertex #1 ([1, 1, 1])
Vertex #3 ([0, 0, 0]) <-->
2 -> 2: [2]
1 -> 0: [1, 0]
0 -> 1: [0, 1]
2 -> 0: [2, 1, 0]
3 -> 0: []
0 -> 3: []
0 -> 3: []
1 -> 0: [1, 0]
1 -> 0: [1, 0]
3 -> 1: []

Boost Graph max-flow algorithm to find out the arcs on the minimal S/T cut

I have an application where for a given fixed number of vertices, there is a need to solve large number of different max-flow algorithms from a given fixed source (S) to a given fixed sink (T). Each max-flow problem differs in that the directed arcs themselves change along with their capacities. As an example, see below.
The number of vertices remains fixed, but the actual arcs and their capacities differ from one problem to the next.
I have the following code that solves the max-flow problem iteratively for Graph 1 and Graph 2 in the figure above using boost thus (apologies for the wall of text, I have tried to make it as minimal as possible. The code below fully compiles on g++ on my linux box, but I am unable to have this correcly compile on online compilers such as wandbox, etc.):
#include <boost/config.hpp>
#include <iostream>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/boykov_kolmogorov_max_flow.hpp>
using namespace boost;
typedef adjacency_list_traits<vecS, vecS, directedS> Traits;
typedef adjacency_list<
vecS, vecS, directedS,
property<
vertex_name_t, std::string,
property<vertex_index_t, int,
property<vertex_color_t, boost::default_color_type,
property<vertex_distance_t, double,
property<vertex_predecessor_t, Traits::edge_descriptor>
> > > >,
property<
edge_index_t, int,
property<edge_capacity_t, double,
property<edge_weight_t, double,
property<edge_residual_capacity_t, double,
property<edge_reverse_t, Traits::edge_descriptor>
> > > > >
Graph;
Graph g;
property_map<Graph, edge_index_t>::type e;
property_map<Graph, edge_capacity_t>::type cap;
property_map<Graph, edge_weight_t>::type cost;
property_map<Graph, edge_residual_capacity_t>::type rescap;
property_map<Graph, edge_reverse_t>::type rev;
property_map<Graph, vertex_color_t>::type colors;
void initialize(int nnodes) {
e = get(edge_index, g);
cap = get(edge_capacity, g);
cost = get(edge_weight, g);
rescap = get(edge_residual_capacity, g);
rev = get(edge_reverse, g);
colors = get(vertex_color, g);
for(int i = 0; i < nnodes; i++)
add_vertex(g);
}
void clearedges() {
Graph::vertex_iterator v, vend;
for (boost::tie(v, vend) = vertices(g); v != vend; ++v)
boost::clear_out_edges(*v, g);
}
void createedges(std::vector<std::pair<int, int>>& arcs, std::vector<double>& capacity) {
Traits::edge_descriptor edf, edr;//forward and reverse
for (int eindex = 0, sz = static_cast<int>(arcs.size()); eindex < sz; eindex++) {
int fr, to;
fr = arcs[eindex].first;
to = arcs[eindex].second;
edf = add_edge(fr, to, g).first;
edr = add_edge(to, fr, g).first;
e[edf] = 2 * eindex;
e[edr] = e[edf] + 1;
cap[edf] = capacity[eindex];
cap[edr] = capacity[eindex];
rev[edf] = edr;
rev[edr] = edf;
}
}
double solve_max_flow(int s, int t) {
double retval = boykov_kolmogorov_max_flow(g, s, t);
return retval;
}
bool is_part_of_source(int i) {
if (colors[i] == boost::black_color)
return true;
return false;
}
int main() {
initialize(6);
std::vector<std::pair<int, int>> arcs1 = { std::make_pair<int,int>(0,1),
std::make_pair<int,int>(0,2),
std::make_pair<int,int>(1,2),
std::make_pair<int,int>(1,3),
std::make_pair<int,int>(1,4),
std::make_pair<int,int>(2,4),
std::make_pair<int,int>(3,4),
std::make_pair<int,int>(3,5),
std::make_pair<int,int>(4,5)
};
std::vector<double> capacities1 = { 10, 10, 10, 10, 1, 4, 3, 2, 10 };
clearedges();
createedges(arcs1, capacities1);
double maxflow = solve_max_flow(0, 5);
printf("max flow is %f\n", maxflow);
for (int i = 0; i < 6; i++)
if (is_part_of_source(i))
printf("Node %d belongs to subset source is in\n", i);
Graph::edge_iterator e_, eend_;
int Eindex = 0;
for (boost::tie(e_, eend_) = edges(g); e_ != eend_; ++e_) {
int fr = source(*e_, g);
int to = target(*e_, g);
printf("(%d) Edge %d: (%d -> %d), capacity %f\n", Eindex, e[*e_], fr, to, cap[*e_]);
Eindex++;
if (is_part_of_source(fr) && is_part_of_source(to) == false)
printf("----is part of ST Cut-----\n");
else
printf("x\n");
}
std::vector<std::pair<int, int>> arcs2 = { std::make_pair<int,int>(0,1),
std::make_pair<int,int>(0,2),
std::make_pair<int,int>(1,3),
std::make_pair<int,int>(2,4),
std::make_pair<int,int>(3,5),
std::make_pair<int,int>(4,5)
};
std::vector<double> capacities2 = { 10, 10, 10, 4, 2, 0 };
clearedges();
createedges(arcs2, capacities2);
maxflow = solve_max_flow(0, 5);
printf("max flow is %f\n", maxflow);
for (int i = 0; i < 6; i++)
if (is_part_of_source(i))
printf("Node %d belongs to subset source is in\n", i);
Eindex = 0;
for (boost::tie(e_, eend_) = edges(g); e_ != eend_; ++e_) {
int fr = source(*e_, g);
int to = target(*e_, g);
printf("(%d) Edge %d: (%d -> %d), capacity %f\n", Eindex, e[*e_], fr, to, cap[*e_]);
Eindex++;
if (is_part_of_source(fr) && is_part_of_source(to) == false)
printf("----is part of ST Cut-----\n");
else
printf("x\n");
}
getchar();
}
I have the following questions.
(a) If the underlying vertices remain fixed, but only the arcs and their capacities change from iteration to iteration, is there anything faster than using clear_out_edges to clear the arcs and then using add_edge to add the new arcs with their new capacities? Also, does clear_out_edges correctly also clear the property map entries that may have the edge descriptor just deleted as key?
(b) Boost max-flow algorithms seem to want the explicit addition of reverse arcs. As of now, in function createedges I explicitly do this via a forward edge descriptor (edf) and a reverse edge descriptor (edr). Is there any performance penalty for this especially when the number of max flow problems that need to be solved is in the 1000s? Is there anything that is more efficient than this?
(c) I am able to correctly enumerate the arcs of the minimal S/T cut via the following portion of the code:
int Eindex = 0;
for (boost::tie(e_, eend_) = edges(g); e_ != eend_; ++e_) {
int fr = source(*e_, g);
int to = target(*e_, g);
printf("(%d) Edge %d: (%d -> %d), capacity %f\n", Eindex, e[*e_], fr, to, cap[*e_]);
Eindex++;
if (is_part_of_source(fr) && is_part_of_source(to) == false)
printf("----is part of ST Cut-----\n");
else
printf("x\n");
}
Is there any more efficient way or enumerating the arcs of the S/T cut than the above?
There's many issues. If you use modern C++ and compiler warnings, you can reduce the code and spot the bugs in printing vertex descriptors (printf is just not safe; use the diagnostics!).
Here's my take after review.
Notable changes:
bundled properties instead of separate interior properties
this implies passing named arguments (but see https://stackoverflow.com/a/64744086/85371)
no more global variables, no more loopy initialization if the simple constructor suffices
no more duplicated code (nothing invites error quite like having capacities1 and capacities2 lying around)
using clear_vertex instead of just clear_out_edges - this may not make a difference (?) but seems to express intent a bit better
no more printf (I'll use libfmt, which is also in c++23), so e.g.
fmt::print("Max flow {}\nNodes {} are in source subset\n", maxflow,
vertices(_g) | filtered(is_source));
prints
Max flow 10
Nodes {0, 1, 2, 3} are in source subset
all in one go
print what you think you are printing. In particular, use the library support for printing edges if you can
Live On Compiler Explorer
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/boykov_kolmogorov_max_flow.hpp>
#include <boost/range/adaptors.hpp>
#include <fmt/ostream.h>
#include <fmt/ranges.h>
using boost::adaptors::filtered;
using Traits = boost::adjacency_list_traits<boost::vecS, boost::vecS, boost::directedS>;
using V = Traits::vertex_descriptor;
using E = Traits::edge_descriptor;
using Capacity = double;
using Color = boost::default_color_type;
struct VertexProps {
// std::string name;
Color color;
Capacity distance;
E predecessor;
};
struct EdgeProps {
int id;
Capacity weight, residual;
E reverse;
};
using Graph = boost::adjacency_list<
boost::vecS, boost::vecS, boost::directedS,
VertexProps,
// see https://stackoverflow.com/a/64744086/85371 :(
boost::property<boost::edge_capacity_t, Capacity, EdgeProps>>;
struct MyGraph {
MyGraph(size_t nnodes) : _g(nnodes) {}
void runSimulation(auto const& arcs, auto const& capacities)
{
reconfigure(arcs, capacities);
Capacity maxflow = solve_max_flow(0, 5);
auto cap = get(boost::edge_capacity, _g);
auto is_source = [this](V v) { return _g[v].color == Color::black_color; };
fmt::print("Max flow {}\nNodes {} are in source subset\n", maxflow,
vertices(_g) | filtered(is_source));
for (E e : boost::make_iterator_range(edges(_g))) {
bool st_cut =
is_source(source(e, _g)) and
not is_source(target(e, _g));
fmt::print("Edge {} (id #{:2}), capacity {:3} {}\n", e, _g[e].id,
cap[e], st_cut ? "(ST Cut)" : "");
}
}
private:
Graph _g;
void reconfigure(auto const& arcs, auto const& capacities)
{
assert(arcs.size() == capacities.size());
for (auto v : boost::make_iterator_range(vertices(_g))) {
// boost::clear_out_edges(v, g);
boost::clear_vertex(v, _g);
}
auto cap = get(boost::edge_capacity, _g);
auto eidx = get(&EdgeProps::id, _g);
auto rev = get(&EdgeProps::reverse, _g);
auto eindex = 0;
for (auto [fr, to] : arcs) {
auto edf = add_edge(fr, to, _g).first;
auto edr = add_edge(to, fr, _g).first;
eidx[edf] = 2 * eindex;
eidx[edr] = eidx[edf] + 1;
cap[edf] = cap[edr] = capacities[eindex];
rev[edf] = edr;
rev[edr] = edf;
++eindex;
}
}
Capacity solve_max_flow(V src, V sink)
{
return boykov_kolmogorov_max_flow(
_g, src, sink,
// named arguments
boost::reverse_edge_map(get(&EdgeProps::reverse, _g))
.residual_capacity_map(get(&EdgeProps::residual, _g))
.vertex_color_map(get(&VertexProps::color, _g))
.predecessor_map(get(&VertexProps::predecessor, _g))
.distance_map(get(&VertexProps::distance, _g))
// end named arguments
);
}
};
int main() {
MyGraph g{6};
using namespace std;
for (auto&& [arcs, capacities] : { tuple
// 1
{vector{pair{0, 1}, {0, 2}, {1, 2}, {1, 3}, {1, 4},
{2, 4}, {3, 4}, {3, 5}, {4, 5}},
vector{10, 10, 10, 10, 1, 4, 3, 2, 10}},
// 2
{vector{pair{0, 1}, {0, 2}, {1, 3}, {2, 4}, {3, 5}, {4, 5}},
vector{10, 10, 10, 4, 2, 0}},
})
{
g.runSimulation(arcs, capacities);
}
}
Prints
Max flow 10
Nodes {0, 1, 2, 3} are in source subset
Edge (0,1) (id # 0), capacity 10
Edge (0,2) (id # 2), capacity 10
Edge (1,0) (id # 1), capacity 10
Edge (1,2) (id # 4), capacity 10
Edge (1,3) (id # 6), capacity 10
Edge (1,4) (id # 8), capacity 1 (ST Cut)
Edge (2,0) (id # 3), capacity 10
Edge (2,1) (id # 5), capacity 10
Edge (2,4) (id #10), capacity 4 (ST Cut)
Edge (3,1) (id # 7), capacity 10
Edge (3,4) (id #12), capacity 3 (ST Cut)
Edge (3,5) (id #14), capacity 2 (ST Cut)
Edge (4,1) (id # 9), capacity 1
Edge (4,2) (id #11), capacity 4
Edge (4,3) (id #13), capacity 3
Edge (4,5) (id #16), capacity 10
Edge (5,3) (id #15), capacity 2
Edge (5,4) (id #17), capacity 10
Max flow 2
Nodes {0, 1, 2, 3, 4} are in source subset
Edge (0,1) (id # 0), capacity 10
Edge (0,2) (id # 2), capacity 10
Edge (1,0) (id # 1), capacity 10
Edge (1,3) (id # 4), capacity 10
Edge (2,0) (id # 3), capacity 10
Edge (2,4) (id # 6), capacity 4
Edge (3,1) (id # 5), capacity 10
Edge (3,5) (id # 8), capacity 2 (ST Cut)
Edge (4,2) (id # 7), capacity 4
Edge (4,5) (id #10), capacity 0 (ST Cut)
Edge (5,3) (id # 9), capacity 2
Edge (5,4) (id #11), capacity 0
Side Note
If you think main is overcomplicated, here's another way to write it for just the two invocations:
Live On Compiler Explorer
g.runSimulation({{0, 1}, {0, 2}, {1, 2}, {1, 3}, {1, 4}, {2, 4}, {3, 4}, {3, 5}, {4, 5}},
{10, 10, 10, 10, 1, 4, 3, 2, 10});
g.runSimulation({{0, 1}, {0, 2}, {1, 3}, {2, 4}, {3, 5}, {4, 5}},
{10, 10, 10, 4, 2, 0});

How to assign variadic template arguments to std::array

I have an std::array and I have a variadic template function with the number of parameters that matches the size of the array. I need to assign the arguments to the elements of the array. In other words, in the code below I wish a to get values {1, 2, 3} and b to get values {1, 2, 3, 4, 5}
std::array<int, 3> a;
std::array<int, 5> b;
assign_values(a, 1, 2, 3);
assign_values(b, 1, 2, 3, 4, 5);
The question is how to implement the assign_values variadic template function.
I'm limited with the C++14 version.
Update:
The arguments can be of different types: assign_values(b, 1, 2u, 3., '4', 5l);
Sth like this:
template<class T, size_t N, class ... Values>
void assign_values(std::array<T,N>& arr, Values... vals) {
static_assert(N == sizeof...(vals));
int j = 0;
for (auto i : std::initializer_list< std::common_type_t<Values...> >{vals...})
arr[j++] = i;
}
Demo
I'm limited with the C++14 version
The good old trick of the unused array initialization (pre C++17 surrogate of template folding) should works (also C++11)
template <typename T, std::size_t N, typename ... Values>
void assign_values (std::array<T,N> & arr, Values... vals)
{
static_assert(N == sizeof...(vals));
using unused = int[];
int j = 0;
(void)unused { 0, (arr[j++] = vals, 0)... };
}

How make a stride chunk iterator thrust cuda

I need a class iterator like this
https://github.com/thrust/thrust/blob/master/examples/strided_range.cu
but that this new iterator do the next sequence
[k * size_stride, k * size_stride+1, ...,k * size_stride+size_chunk-1,...]
with
k = 0,1,...,N
Example:
size_stride = 8
size_chunk = 3
N = 3
then the sequence is
[0,1,2,8,9,10,16,17,18,24,25,26]
I don't know how do this efficiently...
The strided range interator is basically a carefully crafted permutation iterator with a functor that gives the appropriate indices for permutation.
Here is a modification to the strided range iterator example. The main changes were:
include the chunk size as an iterator parameter
modify the functor that provides the indices for the permutation iterator to spit out the desired sequence
adjust the definitions of .end() iterator to provide the appropriate length of sequence.
Worked example:
$ cat t1280.cu
#include <thrust/iterator/counting_iterator.h>
#include <thrust/iterator/transform_iterator.h>
#include <thrust/iterator/permutation_iterator.h>
#include <thrust/functional.h>
#include <thrust/fill.h>
#include <thrust/device_vector.h>
#include <thrust/copy.h>
#include <thrust/sequence.h>
#include <iostream>
#include <assert.h>
// this example illustrates how to make strided-chunk access to a range of values
// examples:
// strided_chunk_range([0, 1, 2, 3, 4, 5, 6], 1,1) -> [0, 1, 2, 3, 4, 5, 6]
// strided_chunk_range([0, 1, 2, 3, 4, 5, 6], 2,1) -> [0, 2, 4, 6]
// strided_chunk_range([0, 1, 2, 3, 4, 5, 6], 3,2) -> [0 ,1, 3, 4, 6]
// ...
template <typename Iterator>
class strided_chunk_range
{
public:
typedef typename thrust::iterator_difference<Iterator>::type difference_type;
struct stride_functor : public thrust::unary_function<difference_type,difference_type>
{
difference_type stride;
int chunk;
stride_functor(difference_type stride, int chunk)
: stride(stride), chunk(chunk) {}
__host__ __device__
difference_type operator()(const difference_type& i) const
{
int pos = i/chunk;
return ((pos * stride) + (i-(pos*chunk)));
}
};
typedef typename thrust::counting_iterator<difference_type> CountingIterator;
typedef typename thrust::transform_iterator<stride_functor, CountingIterator> TransformIterator;
typedef typename thrust::permutation_iterator<Iterator,TransformIterator> PermutationIterator;
// type of the strided_range iterator
typedef PermutationIterator iterator;
// construct strided_range for the range [first,last)
strided_chunk_range(Iterator first, Iterator last, difference_type stride, int chunk)
: first(first), last(last), stride(stride), chunk(chunk) {assert(chunk<=stride);}
iterator begin(void) const
{
return PermutationIterator(first, TransformIterator(CountingIterator(0), stride_functor(stride, chunk)));
}
iterator end(void) const
{
int lmf = last-first;
int nfs = lmf/stride;
int rem = lmf-(nfs*stride);
return begin() + (nfs*chunk) + ((rem<chunk)?rem:chunk);
}
protected:
Iterator first;
Iterator last;
difference_type stride;
int chunk;
};
int main(void)
{
thrust::device_vector<int> data(50);
thrust::sequence(data.begin(), data.end());
typedef thrust::device_vector<int>::iterator Iterator;
// create strided_chunk_range
std::cout << "stride 3, chunk 2, length 7" << std::endl;
strided_chunk_range<Iterator> scr1(data.begin(), data.begin()+7, 3, 2);
thrust::copy(scr1.begin(), scr1.end(), std::ostream_iterator<int>(std::cout, " ")); std::cout << std::endl;
std::cout << "stride 8, chunk 3, length 50" << std::endl;
strided_chunk_range<Iterator> scr(data.begin(), data.end(), 8, 3);
thrust::copy(scr.begin(), scr.end(), std::ostream_iterator<int>(std::cout, " ")); std::cout << std::endl;
return 0;
}
$ nvcc -arch=sm_35 -o t1280 t1280.cu
$ ./t1280
stride 3, chunk 2, length 7
0 1 3 4 6
stride 8, chunk 3, length 50
0 1 2 8 9 10 16 17 18 24 25 26 32 33 34 40 41 42 48 49
$
This is probably not the most optimal implementation, in particular because we are doing division in the permutation functor, but it should get you started.
I assume (and test for) chunk<=stride, because this seemed reasonable to me, and simplified my thought process. I'm sure it could be modified, with an appropriate example of what sequence you would like to see, for the case where chunk>stride.

Replace pointer to pointer by initializer_list

#include <initializer_list>
#include <iostream>
#include <vector>
//this api is anti intuition
void original(int const **data)
{
for(size_t i = 0; i != 3; ++i){
int const *ptr = *data;
//std::cout<<*ptr++<<", "<<*ptr<<std::endl; //this line may cause undefined behavior
std::cout<<ptr[0]<<", "<<ptr[1]<<std::endl;
++data;
}
}
//my eyes prefer this api than original like api
void replace_original(std::initializer_list<std::initializer_list<int>> list)
{
std::vector<int const*> results(list.size());
for(auto data : list){
results.push_back(std::begin(data)); //#1
}
original(&results[0]);
}
int main()
{
int first[] = {0, 1};
int second[] = {2, 3};
int third[] = {4, 5};
int const *array[] = {first, second, third};
original(array);
replace_original({ {0, 1}, {2, 3}, {4, 5} });
return 0;
}
The results are
0, 1
2, 3
4, 5
expected results are
0, 1
2, 3
4, 5
0, 1
2, 3
4, 5
I want to encapsulate the api of original(old, c style api) by the api like replace_original
But can't figure out why #1 can't work.
Ah, stupid mistake, I should change the loop to
size_t const size = list.size();
std::vector<int const*> results(size);
for(size_t i = 0; i != size; ++i){
results[i] = std::begin( *(std::begin(list) + i) );
}
Do you have a better solution to encapsulate this kind of api?
After google, I find out that in c++14, size() of initializer_list will
become constexpr so we should be able to use std::array to replace std::vector

Resources