What is a Simple Example for using the Boost Graph Library - c++

I am trying to use the BGL, I find the documentation precise but lacks more examples for simple cases. My goal is described below (after reading the documentation I still couldn't do this):
struct Vertex
{
double m_d;
std::size_t m_id;
};
//or
struct Vertex
{
double m_d;
std::size_t id() const;
};
Goals:
A directed graph G (what is the difference between a bidirectional and directed other than in_edges please?)
G can hold the vertex type Vertex.
get the vertex by id from G and change the value m_d in the Vertex struct when I want.
add, remove verticies and edges between verticies and also supports costs i.e. cost(edge).
Could you please write me an example on how to do this with BGL please? I beleive I need MutableBidirectionalGraph?

A directed graph G
Straight-away:
struct Vertex {
double m_d = 0;
size_t m_id = -1;
// or std::size_t id() const;
};
struct Edge {
double cost = 0;
};
using Graph =
boost::adjacency_list<boost::vecS, boost::vecS, boost::bidirectionalS, Vertex, Edge>;
(what is the difference between a bidirectional and directed other than
in_edges please?)
There is no other difference, except of course the complexity guarantees
for enumerating incoming edges, and a linear overhead upon insertion of edges
G can hold the vertex type Vertex.
See 0.
get the vertex by id from G
auto find_by_id = [&g](size_t id) -> Vertex& {
auto vv = boost::make_iterator_range(vertices(g));
auto vd = find_if(vv, [&, id](auto vd) { return g[vd].m_id == id; });
return g[*vd];
};
and change the value m_d in the Vertex struct when I want.
if (i_want()) {
g[vd].m_id += 1;
}
Or,
auto idmap = boost::get(&Vertex::m_id, g);
if (i_want()) {
idmap[vd] += 1;
}
or even
put(idmap, vd, 42);
or even more unmarked:
get(boost::vertex_bundle, g, vd).m_id = 999;
add, remove vertices
remove_vertex(vd, g);
and edges between vertices
clear_vertex(vd, g);
and also supports costs i.e. cost(edge).
Wow that really has nothing to do with any of the above. But it's really the same as with vertex ids:
if (i_want()) {
g[ed].cost = new_cost;
}
Or,
auto cost = boost::get(&Edge::cost, g);
if (i_want()) {
cost[ed] = new_cost;
}
or even
put(cost, ed, new_cost);
or even more unmarked:
get(boost::edge_bundle, g, ed).cost = new_cost;
Live Demo
Live On Coliru
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graph_utility.hpp>
#include <boost/range/algorithm.hpp>
#include <iostream>
struct Vertex {
double m_d = 0;
size_t m_id = -1;
// or std::size_t id() const;
};
struct Edge {
double cost = 0;
};
using Graph =
boost::adjacency_list<boost::vecS, boost::vecS, boost::bidirectionalS, Vertex, Edge>;
using boost::make_iterator_range;
int main(){
Graph g;
auto v0 = add_vertex({0.1, 100}, g);
auto v1 = add_vertex({0.2, 200}, g);
auto v2 = add_vertex({0.3, 300}, g);
auto v3 = add_vertex({0.4, 400}, g);
auto v4 = add_vertex({0.5, 500}, g);
auto v5 = add_vertex({0.6, 600}, g);
add_edge(v0, v2, Edge{1.5}, g);
add_edge(v1, v3, Edge{2.5}, g);
add_edge(v4, v1, Edge{3.5}, g);
add_edge(v2, v5, Edge{4.5}, g);
auto idmap = boost::get(&Vertex::m_id, g);
auto cost = boost::get(&Edge::cost, g);
auto find_by_id = [&g](size_t id) -> Vertex& {
auto vv = boost::make_iterator_range(vertices(g));
auto vd = find_if(vv, [&, id](auto vd) { return g[vd].m_id == id; });
return g[*vd];
};
print_graph(g, idmap, std::cout << "original: ");
auto i_want = [](auto vd) {
return (vd % 2); // when I want
};
for (auto vd : make_iterator_range(vertices(g))) {
if (i_want(vd))
g[vd].m_id += 1;
if (i_want(vd))
idmap[vd] += 1;
//put(idmap, vd, 42);
//get(boost::vertex_bundle, g, vd).m_id = 999;
}
print_graph(g, idmap, std::cout << "altered: ");
clear_vertex(v3, g);
remove_vertex(v3, g); // undefined behaviour unless edges cleared
print_graph(g, idmap, std::cout << "removed: ");
for (auto ed : make_iterator_range(edges(g))) {
std::cout << ed << " cost " << cost[ed] << "\n";
}
for (auto ed : make_iterator_range(edges(g))) {
cost[ed] *= 111;
}
for (auto ed : make_iterator_range(edges(g))) {
std::cout << ed << " cost " << cost[ed] << "\n";
}
};
Prints
original: 100 --> 300
200 --> 400
300 --> 600
400 -->
500 --> 200
600 -->
altered: 100 --> 300
202 --> 402
300 --> 602
402 -->
500 --> 202
602 -->
removed: 100 --> 300
202 -->
300 --> 602
500 --> 202
602 -->
(0,2) cost 1.5
(3,1) cost 3.5
(2,4) cost 4.5
(0,2) cost 166.5
(3,1) cost 388.5
(2,4) cost 499.5

Related

Boost Graph Library - Dijkstra's shortest path fails when the edge weights are large?

I have a question regarding the edge weights when using Dijkstra's algorithm in Boost. The problem I am facing is that when the edge weights are large, I do not get a solution from Dijkstra's algorithm.
Let's say I have an adjacency list with bundled properties. My vertices are of type VertexType and edges are of type EdgeType. Here, VertexType resembles a person for a simple example. VertexType::pred is the predecessor when used with Dijkstra's algorithm.
struct VertexType
{
std::string name;
int age;
int pred;
};
struct EdgeType
{
double weight;
};
Below is a simple example.
void edgeWeightProblem()
{
// Graph and vertex descriptor typedefs.
typedef boost::adjacency_list<boost::vecS, boost::vecS,
boost::directedS,
VertexType,
EdgeType
> GraphType;
typedef boost::graph_traits<GraphType>::vertex_descriptor VertexDescriptor;
// Create graph and vertices vector.
GraphType G;
std::vector<VertexDescriptor> graphVertices;
// Add vertices.
graphVertices.push_back(add_vertex({"Tom", 23}, G));
graphVertices.push_back(add_vertex({"Frank", 25}, G));
graphVertices.push_back(add_vertex({"John", 42}, G));
graphVertices.push_back(add_vertex({"Emily", 22}, G));
// Add edges.
add_edge(graphVertices[0], graphVertices[1], {.2564}, G);
add_edge(graphVertices[0], graphVertices[2], {.3572}, G);
add_edge(graphVertices[1], graphVertices[3], {.1246}, G);
add_edge(graphVertices[2], graphVertices[3], {.5361}, G);
// Dijkstra shortest path.
dijkstra_shortest_paths(G, graphVertices.front(),
predecessor_map(boost::get(&VertexType::pred, G))
.distance_map(boost::get(&VertexType::pred, G))
.weight_map(boost::get(&EdgeType::weight, G)));
// Debug pred.
std::vector<VertexType> vec;
for (const VertexDescriptor& vd : graphVertices)
{
vec.push_back(G[vd]);
}
// Extract the shortest path.
std::vector<VertexDescriptor> shortestPath;
VertexDescriptor currentVertex = graphVertices.back();
while (currentVertex != graphVertices.front())
{
shortestPath.push_back(currentVertex);
currentVertex = G[currentVertex].pred;
}
shortestPath.push_back(currentVertex);
std::reverse(shortestPath.begin(), shortestPath.end());
// Display graph.
std::cout << "\nGraph Display: \n";
boost::print_graph(G);
std::cout << "\n";
// Print the shortest path.
std::cout << "Shortest Path: \n";
for (const auto& node : shortestPath)
{
std::cout << node << " -> ";
}
std::cout << "\n";
}
As expected, I get the following output:
Graph Display:
0 --> 1 2
1 --> 3
2 --> 3
3 -->
Shortest Path:
0 -> 2 -> 3 ->
But, if I remove the decimal of the edge weights, (ex: .2564 -> 2564), I will not be able to find the shortest path since the predecessor of each vertex will be itself. This also result in an infinite loop as a side affect for this example.
You can see this by placing a breakpoint after the "Debug pred" section and inspecting "vec". What is going on here? I assume this is a misunderstanding of Boost as I am quite new to it.
I have tried to play around with the weights, but it seems once the weights are larger, this issue happens. These weights are not so large that overflow should be a consideration, so I am quite confused as to what is happening.
You're passing pred as both the predecessor map and the distance map. One will overwrite the other at unspecified points during algorithm execution. The result is unspecified at best, undefined if you're lucky.
Fixed that and simplified some of the code, now the result is the same for both weight inputs:
Live On Coliru
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/dag_shortest_paths.hpp>
#include <boost/graph/graph_utility.hpp>
#include <iomanip>
#include <iostream>
struct VertexType {
std::string name;
int age;
size_t pred;
friend std::ostream& operator<<(std::ostream& os, VertexType const& vt) {
return os << "{" << std::quoted(vt.name) << ", " << vt.age << ", pred:" << vt.pred << "}";
}
};
struct EdgeType {
double weight;
};
int main() {
// Graph and vertex descriptor typedefs.
using Graph = boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS, VertexType, EdgeType>;
using V = Graph::vertex_descriptor;
// Create graph and vertices vector.
Graph g;
// Add vertices.
V v0 = add_vertex({"Tom", 23, 0}, g);
V v1 = add_vertex({"Frank", 25, 0}, g);
V v2 = add_vertex({"John", 42, 0}, g);
V v3 = add_vertex({"Emily", 22, 0}, g);
// Add edges.
add_edge(v0, v1, {2564}, g);
add_edge(v0, v2, {3572}, g);
add_edge(v1, v3, {1246}, g);
add_edge(v2, v3, {5361}, g);
auto bundle = get(boost::vertex_bundle, g);
auto pred = get(&VertexType::pred, g);
auto weight = get(&EdgeType::weight, g);
auto distances = std::vector<double>(num_vertices(g));
// Dijkstra shortest path.
auto src = v0;
dijkstra_shortest_paths(g, src,
predecessor_map(pred)
.distance_map(distances.data())
.weight_map(weight));
#if 0
// Debug pred.
std::vector<VertexType> vertex_properties;
for (auto vd : boost::make_iterator_range(vertices(g))) {
vertex_properties.push_back(g[vd]);
}
#endif
// Extract the shortest path.
std::deque<V> path;
for (V cur = num_vertices(g) - 1;; cur = pred[cur]) {
path.push_front(cur);
if (cur == src || cur == pred[cur])
break;
}
// Display graph.
print_graph(g, bundle, std::cout << "Graph Display: \n");
// Print the shortest path.
std::cout << "\nShortest Path: ";
for (const auto& vd : path) std::cout << (vd == src ? "" : " -> ") << vd;
std::cout << "\nWhich is: ";
for (const auto& vd : path) std::cout << (vd == src ? "" : " -> ") << g[vd];
std::cout << "\n";
}
Prints
Graph Display:
{"Tom", 23, pred:0} --> {"Frank", 25, pred:0} {"John", 42, pred:0}
{"Frank", 25, pred:0} --> {"Emily", 22, pred:1}
{"John", 42, pred:0} --> {"Emily", 22, pred:1}
{"Emily", 22, pred:1} -->
Shortest Path: 0 -> 1 -> 3
Which is: {"Tom", 23, pred:0} -> {"Frank", 25, pred:0} -> {"Emily", 22, pred:1}

How to iterate over boost graph and find neighbor's neighbor also?

The following figure shows bi-directional graph. I have represented following graph using boost-graph.
I have iterated from v1 --> v2 and v1 --> v3 but I am not able to visit from v3 --> v4. How to do that ?
Here is my code:
(vertex = V1) and
(graph = boost graph )
//Finding out edges of vertex
boost::graph_traits<BGType>::out_edge_iterator ei, ei_end;
boost::tie(ei, ei_end) = out_edges( vertex, graph );
for( boost::tie(ei, ei_end) = out_edges(vertex, graph); ei != ei_end; ++ei)
{
auto target = boost::target ( *ei, graph );
graph[target]._isVisible = false;
}
//Finding in edges of vertex
boost::graph_traits<BGType>::in_edge_iterator ein, ein_end;
boost::tie(ein, ein_end) = in_edges( vertex, graph );
for( boost::tie(ein, ein_end) = in_edges(vertex, graph); ein != ein_end; ++ein)
{
auto source = boost::source ( *ein, graph );
graph[source]._isVisible = false;
}
It helps if you get your terminology straight. L1, L2, L3 are edges not vertices.
So, you want to hide all edges and additionally all vertices with degree 0.
You could instead give edges a visibility flag:
struct EdgeProps {
bool _isVisible = true;
};
using BGType = boost::adjacency_list< //
boost::vecS, //
boost::vecS, //
boost::bidirectionalS, //
VertexProps, //
EdgeProps>;
Now I'd make that function:
void hide_connections(Name name, BGType& graph) {
auto it = graph.named_vertices.find(name);
assert(it != graph.named_vertices.end());
using boost::make_iterator_range;
for (auto e : make_iterator_range(out_edges(*it, graph)))
graph[e]._isVisible = false;
for (auto e : make_iterator_range(in_edges(*it, graph)))
graph[e]._isVisible = false;
}
Now, the visibility of vertices is a resultant - a derived property. You could calculate it on the fly:
auto visible = [&graph](V v) {
for (auto e : make_iterator_range(out_edges(v, graph)))
if (graph[e]._isVisible)
return true;
for (auto e : make_iterator_range(in_edges(v, graph)))
if (graph[e]._isVisible)
return true;
return false;
};
Indeed, this satisfies your requirements: Live On Compiler Explorer
#include <boost/graph/adjacency_list.hpp>
#include <boost/property_map/function_property_map.hpp>
#include <iostream>
using Name = std::string;
struct VertexProps {
VertexProps(Name n = "unnamed") : name(std::move(n)) {}
Name name;
};
struct EdgeProps {
bool _isVisible = true;
};
template <> struct boost::graph::internal_vertex_constructor<VertexProps> {
using type = boost::graph::vertex_from_name<VertexProps>;
};
template <> struct boost::graph::internal_vertex_name<VertexProps> {
struct type {
using result_type = Name;
Name const& operator()(VertexProps const& vp) const { return vp.name; }
Name& operator()(VertexProps& vp) const { return vp.name; }
};
};
using BGType = boost::adjacency_list< //
boost::vecS, //
boost::vecS, //
boost::bidirectionalS, //
VertexProps, //
EdgeProps>;
using V = BGType::vertex_descriptor;
using E = BGType::edge_descriptor;
void hide(Name name, BGType& graph) {
auto it = graph.named_vertices.find(name);
assert(it != graph.named_vertices.end());
for (auto e : make_iterator_range(out_edges(*it, graph)))
graph[e]._isVisible = false;
for (auto e : make_iterator_range(in_edges(*it, graph)))
graph[e]._isVisible = false;
}
int main() {
BGType graph;
for (auto [from, to] : {
std::pair{"V1", "V2"},
{"V1", "V3"},
{"V3", "V4"},
})
{
add_edge(from, to, graph);
add_edge(to, from, graph);
}
auto visible = [&graph](V v) { return 0 != degree(v, graph); };
auto names = get(&VertexProps::name, graph);
auto print = [&](std::ostream& os = std::cout) {
for (auto v : boost::make_iterator_range(vertices(graph))) {
if (!visible(v))
continue;
os << names[v] << " -->";
for (auto e : make_iterator_range(out_edges(v, graph))) {
if (graph[e]._isVisible)
os << " " << names[target(e, graph)];
}
os << "\n";
}
};
print();
hide("V1", graph);
print(std::cout << "\nConnections of V1 hidden:\n");
}
Prints
V2 --> V1
V1 --> V2 V3
V3 --> V1 V4
V4 --> V3
Connections of V1 hidden:
V3 --> V4
V4 --> V3
Outside The Box
This is clumsy and inefficient:
auto visible = [&graph](V v) {
for (auto e : make_iterator_range(out_edges(v, graph)))
if (graph[e]._isVisible)
return true;
for (auto e : make_iterator_range(in_edges(v, graph)))
if (graph[e]._isVisible)
return true;
return false;
};
What you really want to be able to say is:
auto visible = [&graph](V v) { return 0 != degree(v, graph); };
However, it won't work because you don't actually delete anything, so BGL will think the edges are still there.
You can fix the model by using a filtered_graph_adaptor where you store the filterables OUTSIDE the graph model.
Filtered Graph
So, this shifts the perspective back to your original: hiding vertices. Let's start out simple:
std::set<V> vhidden;
std::set<E> ehidden;
This is the set that will contain all hidden vertex descriptors.
Now we can setup filter predicates and the adapted graph:
std::function epred = [&](E e) { return not ehidden.contains(e); };
std::function vpred = [&](V v) { return not vhidden.contains(v); };
boost::filtered_graph f(graph, epred, vpred);
Adding some helpers to hide edges/vertices.
auto ehide = [&](E e) {
if (auto u = target(e, graph); 0 == degree(u, f))
vhidden.insert(u);
ehidden.insert(e);
};
Notice that we're being lazy and using degree(v, f) on the filtered graph (!) so that we don't have to manually count the number of edges that were already filtered.
auto vhide = [&](Name const& name) {
auto it = graph.named_vertices.find(name);
assert(it != graph.named_vertices.end());
V v = *it;
vhidden.insert(v);
for (auto e: make_iterator_range(out_edges(v, graph)))
ehide(e);
for (auto e: make_iterator_range(in_edges(v, graph)))
ehide(e);
};
Hiding a vertex traverses exactly one level to neighbours unconditionally. That's good enough as a stopping condition (in response to your comment) because the degree cannot change unless by hiding more edges to the same target node.
Using the filtered view:
std::cout << "Filtered edges(" << ehidden.size() << "), vertices(" << vhidden.size() << ")\n";
print_graph(f, names, std::cout << "Filtered view: \n");
vhide("V1");
std::cout << "Filtered edges(" << ehidden.size() << "), vertices(" << vhidden.size() << ")\n";
print_graph(f, names, std::cout << "Connections of V1 hidden:\n");
See it Live On Compiler Explorer
Filtered edges(0), vertices(0)
Filtered view:
V2 --> V1
V1 --> V2 V3
V3 --> V1 V4
V4 --> V3
Filtered edges(4), vertices(2)
Connections of V1 hidden:
V3 --> V4
V4 --> V3
Full Listing (Filtered Graph)
For posterity: Live On Compiler Explorer
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/filtered_graph.hpp>
#include <boost/graph/graph_utility.hpp>
#include <boost/property_map/function_property_map.hpp>
#include <iostream>
using Name = std::string;
struct VertexProps {
VertexProps(Name n = "unnamed") : name(std::move(n)) {}
Name name;
};
struct EdgeProps {
bool _isVisible = true;
};
template <> struct boost::graph::internal_vertex_constructor<VertexProps> {
using type = boost::graph::vertex_from_name<VertexProps>;
};
template <> struct boost::graph::internal_vertex_name<VertexProps> {
struct type {
using result_type = Name;
Name const& operator()(VertexProps const& vp) const { return vp.name; }
Name& operator()(VertexProps& vp) const { return vp.name; }
};
};
using BGType = boost::adjacency_list< //
boost::vecS, //
boost::vecS, //
boost::bidirectionalS, //
VertexProps, //
EdgeProps>;
using V = BGType::vertex_descriptor;
using E = BGType::edge_descriptor;
int main() {
BGType graph;
for (auto [from, to] : {
std::pair{"V1", "V2"},
{"V1", "V3"},
{"V3", "V4"},
})
{
add_edge(from, to, graph);
add_edge(to, from, graph);
}
auto names = get(&VertexProps::name, graph);
print_graph(graph, names, std::cout << "Unfiltered graph:\n");
std::set<V> vhidden;
std::set<E> ehidden;
std::function epred = [&](E e) { return not ehidden.contains(e); };
std::function vpred = [&](V v) { return not vhidden.contains(v); };
boost::filtered_graph f(graph, epred, vpred);
auto ehide = [&](E e) {
if (auto u = target(e, graph); 0 == degree(u, f))
vhidden.insert(u);
ehidden.insert(e);
};
auto vhide = [&](Name const& name) {
auto it = graph.named_vertices.find(name);
assert(it != graph.named_vertices.end());
V v = *it;
vhidden.insert(v);
for (auto e: make_iterator_range(out_edges(v, graph)))
ehide(e);
for (auto e: make_iterator_range(in_edges(v, graph)))
ehide(e);
};
std::cout << "Filtered edges(" << ehidden.size() << "), vertices(" << vhidden.size() << ")\n";
print_graph(f, names, std::cout << "Filtered view: \n");
vhide("V1");
std::cout << "Filtered edges(" << ehidden.size() << "), vertices(" << vhidden.size() << ")\n";
print_graph(f, names, std::cout << "Connections of V1 hidden:\n");
}

Boost library, how to get neighbouring nodes?

After generating a graph with n nodes, and adding the edges at random, how would I go around getting all the neighbours of a specific node. Is there a function similar to NetworkX's G.neighbors(i)?
This is what I've got so far, creating adjacency list
#include <iostream>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/connected_components.hpp>
using namespace boost;
using namespace std;
int main() {
int N = 10000;
struct status_t{
typedef vertex_property_tag kind;
};
typedef
property <status_t, string> status;
typedef
adjacency_list<vecS, vecS, undirectedS, status> MyGraph;
MyGraph g (N);
// add some random edges
add_edge(0, 1, g);
add_edge(100, 153, g);
add_edge(634, 12, g);
add_edge(94, 3, g);
property_map<MyGraph, status_t>::type status_map = get(status_t(), g);
for (int i = 0; i < 10; i++){
status_map[i] = "S";
}
return 0;
}
auto neighbours = boost::adjacent_vertices(94, g);
Print them like e.g.
for (auto vd : make_iterator_range(neighbours))
std::cout << "94 has adjacent vertex " << vd << "\n";
Prints
94 has adjacent vertex 93
94 has adjacent vertex 3
If you wanted outgoing edges only, that assumes directedS or bidirectionalS, in which case you can also do:
for (auto ed : make_iterator_range(boost::out_edges(94, g)))
std::cout << "outgoing: " << ed << "\n";
for (auto ed : make_iterator_range(boost::in_edges(94, g)))
std::cout << "incident: " << ed << "\n";
Live Demo
Live On Coliru
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/connected_components.hpp>
#include <iostream>
using namespace boost;
using namespace std;
int main() {
int N = 10000;
struct status_t { typedef vertex_property_tag kind; };
typedef property<status_t, string> status;
typedef adjacency_list<vecS, vecS, bidirectionalS, status> MyGraph;
MyGraph g(N);
// add some random edges
add_edge(0, 1, g);
add_edge(100, 153, g);
add_edge(634, 12, g);
add_edge(93, 94, g);
add_edge(94, 3, g);
property_map<MyGraph, status_t>::type status_map = get(status_t(), g);
for (int i = 0; i < 10; i++) {
status_map[i] = "S";
}
{
auto neighbours = boost::adjacent_vertices(94, g);
for (auto vd : make_iterator_range(neighbours))
std::cout << "94 has adjacent vertex " << vd << "\n";
// prints
// for undirected:
// 94 has adjacent vertex 93
// 94 has adjacent vertex 3
// for directed/bidirectionalS:
// 94 has adjacent vertex 3
}
{ // for bidirectionalS:
for (auto ed : make_iterator_range(boost::out_edges(94, g)))
std::cout << "outgoing: " << ed << "\n";
for (auto ed : make_iterator_range(boost::in_edges(94, g)))
std::cout << "incident: " << ed << "\n";
}
}
Printing
94 has adjacent vertex 3
outgoing: (94,3)
incident: (93,94)

Need to find sub graphs from one big graph using boost::graph

PH -> PH1
PH -> PH2
PH1 -> N1
PH1 -> N2
PH2 -> N3
PH2 -> N4
required output as :
sub graph 1 :
PH1 -> N1
PH1 -> N2
sub graph 2 :
PH2 -> N3
PH2 -> N3
This is almost trivial using connected_components.
The complicating thing is to ignore the PH node. You didn't say whether this node is given or should be detected. I have written some code to try to detect it.
Let's Start
#include <boost/graph/adjacency_list.hpp>
using Graph = boost::adjacency_list<boost::vecS, boost::vecS, boost::bidirectionalS,
boost::property<boost::vertex_name_t, std::string> >;
We want to implement roughly the following steps:
using ComponentId = int;
using Mappings = std::vector<ComponentId>;
using Graphs = std::vector<Graph>;
Graph build();
Mappings map_components(Graph const&);
Graphs split(Graph const&, Mappings const&);
And the main program will look like
#include <boost/graph/graph_utility.hpp>
int main() {
Graph g = build();
Mappings components = map_components(g);
for (auto& sub : split(g, components)) {
std::cout << "\n========================\n";
print_graph(sub, get(boost::vertex_name, sub));
}
}
Sample Data
This is straight-forward since we used the vertex_name property:
using Vertex = Graph::vertex_descriptor;
Graph build() {
Graph g;
Vertex PH = add_vertex({"PH"}, g);
Vertex PH1 = add_vertex({"PH1"}, g);
Vertex PH2 = add_vertex({"PH2"}, g);
Vertex N1 = add_vertex({"N1"}, g);
Vertex N2 = add_vertex({"N2"}, g);
Vertex N3 = add_vertex({"N3"}, g);
Vertex N4 = add_vertex({"N4"}, g);
add_edge(PH, PH1, g);
add_edge(PH, PH2, g);
add_edge(PH1, N1, g);
add_edge(PH1, N2, g);
add_edge(PH2, N3, g);
add_edge(PH2, N4, g);
return g;
}
Mapping Components
This is not too bad:
#include <boost/graph/connected_components.hpp> // connected_components
Mappings naive_components(Graph const& g) {
Mappings mappings(num_vertices(g));
int num = boost::connected_components(g, mappings.data());
return mappings;
}
Except, everything is connected, so we get 1 component containing all the vertices... Let's use articulation_points to "ignore" a vertex first:
#include <boost/graph/biconnected_components.hpp> // articulation_points
#include <boost/graph/connected_components.hpp> // connected_components
#include <boost/graph/filtered_graph.hpp>
#include <boost/function.hpp>
using Filtered = boost::filtered_graph<Graph, boost::keep_all, boost::function<bool(Vertex)> >;
Mappings map_components(Graph const& g) {
Mappings mappings(num_vertices(g));
std::vector<Vertex> ap;
articulation_points(g, back_inserter(ap));
if (!ap.empty()) {
// get the articulation point with the lowest degree
nth_element(ap.begin(), ap.begin()+1, ap.end(), [&](Vertex a, Vertex b) { return degree(a, g) < degree(b, g); });
Vertex ignored = ap.front();
std::cout << "Igoring articulation point " << get(boost::vertex_name, g, ignored) << " from graph\n";
Filtered fg(g, {}, [&](Vertex v) { return ignored != v; });
int num = boost::connected_components(fg, mappings.data());
mappings[ignored] = num; // make sure the ignored vertex is in its own component
}
return mappings;
}
That's basically doing the same thing, but it ignores the PH node. Note that we try to make sure we cut as few edges as possible (by sorting by degree).
Splitting
Splitting into separate graphs is almost a formality (re-using the same Filtered graph declarations):
#include <boost/graph/copy.hpp>
Graphs split(Graph const& g, Mappings const& components) {
if (components.empty())
return {};
Graphs results;
auto highest = *std::max_element(components.begin(), components.end());
for (int c = 0; c <= highest; ++c) {
results.emplace_back();
boost::copy_graph(Filtered(g, {}, [c, &components](Vertex v) { return components.at(v) == c; }), results.back());
}
return results;
}
Full Listing
Live On Coliru
#include <boost/graph/adjacency_list.hpp>
using Graph = boost::adjacency_list<boost::vecS, boost::vecS, boost::bidirectionalS, boost::property<boost::vertex_name_t, std::string> >;
using ComponentId = int;
using Mappings = std::vector<ComponentId>;
using Graphs = std::vector<Graph>;
Graph build();
Mappings map_components(Graph const&);
Graphs split(Graph const&, Mappings const&);
#include <boost/graph/graph_utility.hpp>
int main() {
Graph g = build();
Mappings components = map_components(g);
for (auto& sub : split(g, components)) {
std::cout << "\n========================\n";
print_graph(sub, get(boost::vertex_name, sub));
}
}
using Vertex = Graph::vertex_descriptor;
Graph build() {
Graph g;
Vertex PH = add_vertex({"PH"}, g);
Vertex PH1 = add_vertex({"PH1"}, g);
Vertex PH2 = add_vertex({"PH2"}, g);
Vertex N1 = add_vertex({"N1"}, g);
Vertex N2 = add_vertex({"N2"}, g);
Vertex N3 = add_vertex({"N3"}, g);
Vertex N4 = add_vertex({"N4"}, g);
add_edge(PH, PH1, g);
add_edge(PH, PH2, g);
add_edge(PH1, N1, g);
add_edge(PH1, N2, g);
add_edge(PH2, N3, g);
add_edge(PH2, N4, g);
return g;
}
#include <boost/graph/biconnected_components.hpp> // articulation_points
#include <boost/graph/connected_components.hpp> // connected_components
#include <boost/graph/filtered_graph.hpp>
#include <boost/function.hpp>
using Filtered = boost::filtered_graph<Graph, boost::keep_all, boost::function<bool(Vertex)> >;
Mappings map_components(Graph const& g) {
Mappings mappings(num_vertices(g));
std::vector<Vertex> ap;
articulation_points(g, back_inserter(ap));
if (!ap.empty()) {
// get the articulation point with the lowest degree
nth_element(ap.begin(), ap.begin()+1, ap.end(), [&](Vertex a, Vertex b) { return degree(a, g) < degree(b, g); });
Vertex ignored = ap.front();
std::cout << "Igoring articulation point " << get(boost::vertex_name, g, ignored) << " from graph\n";
Filtered fg(g, {}, [&](Vertex v) { return ignored != v; });
int num = boost::connected_components(fg, mappings.data());
mappings[ignored] = num; // make sure the ignored vertex is in its own component
}
return mappings;
}
#include <boost/graph/copy.hpp>
Graphs split(Graph const& g, Mappings const& components) {
if (components.empty())
return {};
Graphs results;
auto highest = *std::max_element(components.begin(), components.end());
for (int c = 0; c <= highest; ++c) {
results.emplace_back();
boost::copy_graph(Filtered(g, {}, [c, &components](Vertex v) { return components.at(v) == c; }), results.back());
}
return results;
}
Prints
Igoring articulation point PH from graph
========================
PH1 --> N1 N2
N1 -->
N2 -->
========================
PH2 --> N3 N4
N3 -->
N4 -->
========================
PH -->

exporting a vector variable to graph via boost graph

I want to export a vector which is storing sequence of string values to boost graph(dot file). .
The last four lines can explain the problem/help required. I know the code is wrong, but need guidlines to address this issue. I want to store vector toponodedist2 to graph dot file.Here I want store an array/vector which will store the sequence of values at indexes like (image01, image02, iamge 03 ....). later I will export this vector via write_graphviz_dp to dot file. Thanks
#include<iostream>
#include<boost/graph/adjacency_list.hpp>
#include<boost/graph/graphviz.hpp>
#include<boost/graph/properties.hpp>
#include<boost/graph/property_maps/container_property_map.hpp>
#include<boost/graph/named_function_params.hpp>
#include <cstdlib>
#include<fstream>
struct VertexData
{
std:: string image_path;
int id;
int image_num;
std:: vector<std::vector<std::string>> toponodedist2;
};
int main(int,char*[])
{
VertexData v11;
std:: vector<std::vector<std::string>> toponodedist;
std:: vector<std::string> toponodedist1;
toponodedist1.push_back("this");
toponodedist1.push_back("This is first node2");
toponodedist1.push_back("This is first node3");
v11.toponodedist2.push_back(toponodedist1);
toponodedist.push_back(toponodedist1);
toponodedist1.clear();
toponodedist1.push_back("This is first node1");
toponodedist1.push_back("This is first node2");
toponodedist1.push_back("This is first node3");
toponodedist.push_back(toponodedist1);
v11.toponodedist2.push_back(toponodedist1);
for(int i=0;i<=2;i++)
for(int j=0;j<=2;j++)
std::cout<< "this is "<<v11.toponodedist2[i][j]<<std::endl;
/// mention vertex data in declaring adjacency list
typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS, VertexData,boost::no_property> MyGraphType;
MyGraphType G;
auto v1 =add_vertex(G);
auto v2 =add_vertex(G);
auto v3 =add_vertex(G);
auto v4 =add_vertex(G);
auto v5 =add_vertex(G);
auto v6 =add_vertex(G);
auto v7 =add_vertex(G);
auto v8 =add_vertex(G);
auto v9 =add_vertex(G);
auto e1 =add_edge(v1,v2,G);
auto e2 =add_edge(v2,v3,G);
auto e3 =add_edge(v3,v4,G);
auto e4 =add_edge(v4,v5,G);
auto e5 =add_edge(v5,v6,G);
auto e6 =add_edge(v7,v7,G);
auto e7 =add_edge(v5,v2,G);
auto e8 =add_edge(v1,v4,G);
auto e9 =add_edge(v3,v7,G);
auto e10 =add_edge(v2,v7,G);
auto e11 =add_edge(v2,v6,G);
auto vpair=vertices(G);
int numberOfInEdges = boost::out_degree(8,G);
std::cout<< "The number of vertices are "<<numberOfInEdges;
;
std::ofstream dotfile1;
dotfile1.open("dotgraph1.txt", std::ios::app);
boost::dynamic_properties dp;
dp.property("node_id", get(&VertexData::id, G));
// this is the place where I need help to export toponodeist2 to graph dot
// file ditfile1, but I am not able to do it. thanks
dp.property("path_Ismage", get(&VertexData::toponodedist2, G));
/// the line need to be addressed
boost::write_graphviz_dp(dotfile1,G,dp);
dotfile1.close();
return 0;
}
Just transform the property to a string in some form, e.g.:
dp.property("toponodedist", boost::make_transform_value_property_map(topo_attr, get(boost::vertex_index, g)));
with e.g.
auto topo_attr = [&g](MyGraphType::vertex_descriptor const& v) {
auto& vd = g[v];
std::string s;
for (auto& row : vd.topo) {
for (auto& el : row) s += el + " ";
s += '\n';
}
return s;
};
Live On Coliru
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graphviz.hpp>
#include <boost/property_map/transform_value_property_map.hpp>
#include <fstream>
#include <iostream>
#include <sstream>
using string_table = std::vector<std::vector<std::string>>;
struct VertexData {
std::string image_path;
int id;
int image_num;
string_table topo;
};
int main(int, char *[]) {
/// mention vertex data in declaring adjacency list
typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS, VertexData>
MyGraphType;
MyGraphType g(9);
add_edge(0, 1, g);
add_edge(1, 2, g);
add_edge(2, 3, g);
add_edge(3, 4, g);
add_edge(4, 5, g);
add_edge(6, 6, g);
add_edge(4, 1, g);
add_edge(0, 3, g);
add_edge(2, 6, g);
add_edge(1, 6, g);
add_edge(1, 5, g);
std::srand(time(0));
auto gen_topo = []() -> string_table {
auto ranch = [] { return std::string(1, rand()%26 + 'a'); };
return {
{ ranch(), ranch(), ranch() },
{ ranch(), ranch(), ranch() },
{ ranch(), ranch(), ranch() },
};
};
for (auto v : boost::make_iterator_range(boost::vertices(g))) {
auto& data = g[v];
data.id = v;
data.topo = gen_topo();
}
std::cout << "out-degree: " << boost::out_degree(8, g);
{
std::ofstream dotfile;
dotfile.open("dotgraph.txt");
boost::dynamic_properties dp;
dp.property("node_id", get(&VertexData::id, g));
auto topo_attr = [&g](MyGraphType::vertex_descriptor const& v) {
auto& vd = g[v];
std::string s;
for (auto& row : vd.topo) {
for (auto& el : row) s += el + " ";
s += '\n';
}
return s;
};
dp.property("toponodedist", boost::make_transform_value_property_map(topo_attr, get(boost::vertex_index, g)));
boost::write_graphviz_dp(dotfile, g, dp);
}
}
The dot-graph looks like:
graph G {
0 [toponodedist="q c i
z a e
o x c
"];
1 [toponodedist="i u t
c i l
s k w
"];
2 [toponodedist="z q m
q j l
n o t
"];
3 [toponodedist="u k q
g d u
o c v
"];
4 [toponodedist="u t s
w d n
r g x
"];
5 [toponodedist="e y h
b x z
n q i
"];
6 [toponodedist="a f y
w z j
m f o
"];
7 [toponodedist="g v s
d r l
w o p
"];
8 [toponodedist="l h x
i m x
s n v
"];
0--1 ;
1--2 ;
2--3 ;
3--4 ;
4--5 ;
6--6 ;
4--1 ;
0--3 ;
2--6 ;
1--6 ;
1--5 ;
}
Demo Viz
Just using the label attribute instead of toponodedest there to make it show on a standard dot render: