Reputation: 1191
Suppose I have the following undirected graph
A("")--2.31--B("Hello")--1.036--C("")
Where A and C are empty strings and the real numbers are weights. Now I want to get from B to A. I know in boost, that can be done with
auto vp = boost::vertices(g);
for(auto vit = vp.first; vit != vp.second; ++vit)
{
std::cout << *vit << " " << std::endl;
}
When I get to vertex A, I want to be able to randomize the string from B, based on some math on the weight of the edge from B to A. So I want to be able to do something like g[A].name = newString(weight from B to A);
.
The problem is I don't really know how to do that. I have edge weights and they are imported properly from a file that I am reading in. And I know how to get the weights if I iterate through this way:
auto es = boost::edges(g);
for(auto eit = es.first; eit != es.second; ++eit)
{
std::cout << get(weight,*eit) << std::endl;
}
The issue with this is that this will iterate through the nodes of the graph and not necessarily caring about any of the edges.
Is there something in boost that will do what I want? I have tried looking at this implementation from Stack Overflow, and I understand how to start at a specific vertex. However I am not sure if there is a way to tweak this algorithm to use my edge weights to achieve what I want, and to be honest the documentation for DFS is hard to understand. I believe DFS is what I want, but I am not sure how to implement this easily without breaking down and writing my own DFS which I don't really want to do.
Upvotes: 1
Views: 1157
Reputation: 393769
Now I want to get from B to A. I know in boost, that can be done with [...]
No, the code just enumerates vertices, even those without edges and in no particular order.
When I get to vertex A
What does it mean to "get to vertex A"? Do you need have a path there, or are you simply going to enumerate all vertices and traverse edges from there?
I want to be able to randomize the string from B, based on some math on the weight of the edge from B to A
I read this as "I want to calculate a string based on the weight of edges discovered and update the label string for the destination vertex.
That's at least slightly confusing, because it suggests a direction on edges of an undirected graph. I'm assuming you indeed want to use a direction (the direction of traversal during edge discovery).
And I know how to get the weights if I iterate through this way: [...snip...] The issue with this is that this will iterate through the nodes of the graph and not necessarily caring about any of the edges.
Huh. That's completely flipped around. that loop does iterate edges, specifically. Did you swap the code samples?
Is there something in boost that will do what I want?
That's unanswerable until you know what you want.
I believe DFS is what I want
Okay... Let's get you started a little bit:
#include <boost/graph/adjacency_list.hpp>
Let's define a graph that can store labels (name
) and weights:
struct Vertex { std::string name; };
struct Edge { double weight; };
using Graph = boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS, Vertex, Edge>;
Now, let's print the sample graph in Graphviz format:
Graph make_sample();
void dump(Graph&);
int main() {
Graph g = make_sample();
dump(g);
}
That's a bit like cheating, but it generally helps to start out with the big picture and hide the details, so let's implement make_sample
:
Graph make_sample() {
Graph g;
auto A = add_vertex({""}, g);
auto B = add_vertex({"Hello"}, g);
auto C = add_vertex({""}, g);
add_edge(A, B, {2.31}, g);
add_edge(B, C, {1.036}, g);
return g;
}
And dump
:
#include <boost/graph/graphviz.hpp>
void dump(Graph& g) {
boost::dynamic_properties dp;
dp.property("node_id", get(boost::vertex_index, g));
dp.property("label", get(&Vertex::name, g));
dp.property("label", get(&Edge::weight, g));
write_graphviz_dp(std::cout, g, dp);
}
The graph looks like this:
Search is simple:
#include <boost/graph/depth_first_search.hpp>
struct Visitor : boost::default_dfs_visitor {
};
void operate_on(Graph& g) {
Visitor vis;
std::vector<boost::default_color_type> color(num_vertices(g));
boost::depth_first_search(g, vis, color.data());
}
But you wanted to do the magic:
struct Visitor : boost::default_dfs_visitor {
void examine_edge(Graph::edge_descriptor e, const Graph& g) const {
std::cout << "Examining edge " << e << "\n";
}
};
Now already, we print:
Examining edge (0,1)
Examining edge (1,0)
Examining edge (1,2)
Examining edge (2,1)
Now, let's check the properties of the related vertices:
Vertex const& s = g[source(e, g)];
Vertex const& t = g[target(e, g)];
Now you can do some logic:
if (s.name.empty() && !t.name.empty()) { // use your own logic here
std::cout << "Updating label for '" << t.name << "' in edge " << e << "\n";
}
Which already prints:
Updating label for 'Hello' in edge (0,1)
Updating label for 'Hello' in edge (2,1)
For safety reasons, Graph
is received by const&
in the visitor. To be able to mutate, we need a writable reference. One way is to store a Graph&
inside the visitor:
struct Visitor : boost::default_dfs_visitor {
Graph& _g;
Visitor(Graph& g) : _g(g) {}
void examine_edge(Graph::edge_descriptor e, const Graph&) const {
// get vertex bundles
Vertex& s = _g[source(e, _g)];
Vertex& t = _g[target(e, _g)];
if (s.name.empty() && !t.name.empty()) { // use your own logic here
t.name += '(' + std::to_string(_g[e].weight) + ')';
}
return;
}
};
Perhaps more idiomatic would be to use Property Maps - which have similar effect:
struct Visitor : boost::default_dfs_visitor {
boost::property_map<Graph, std::string Vertex::*>::type _name;
boost::property_map<Graph, double Edge::*>::const_type _weight;
Visitor(Graph& g)
: _name(get(&Vertex::name, g)),
_weight(get(&Edge::weight, static_cast<Graph const&>(g)))
{ }
void examine_edge(Graph::edge_descriptor e, const Graph& g) const {
auto& sname = _name[source(e, g)];
auto& tname = _name[target(e, g)];
if (sname.empty() && !tname.empty()) { // use your own logic here
tname += '(' + std::to_string(_weight[e]) + ')';
}
return;
}
};
Caveat: write access during algorithm is dangerous. Be careful not to violate the pre-conditions/invariants of the algorithm
#include <boost/graph/adjacency_list.hpp>
struct Vertex { std::string name; };
struct Edge { double weight; };
using Graph = boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS, Vertex, Edge>;
Graph make_sample();
void dump(Graph&);
void operate_on(Graph&);
int main() {
Graph g = make_sample();
operate_on(g);
dump(g);
}
Graph make_sample() {
Graph g;
auto A = add_vertex({""}, g);
auto B = add_vertex({"Hello"}, g);
auto C = add_vertex({""}, g);
add_edge(A, B, {2.31}, g);
add_edge(B, C, {1.036}, g);
return g;
}
#include <boost/graph/graphviz.hpp>
void dump(Graph& g) {
boost::dynamic_properties dp;
dp.property("node_id", get(boost::vertex_index, g));
dp.property("label", get(&Vertex::name, g));
dp.property("label", get(&Edge::weight, g));
write_graphviz_dp(std::cout, g, dp);
}
#include <boost/graph/depth_first_search.hpp>
struct Visitor : boost::default_dfs_visitor {
boost::property_map<Graph, std::string Vertex::*>::type _name;
boost::property_map<Graph, double Edge::*>::const_type _weight;
Visitor(Graph& g)
: _name(get(&Vertex::name, g)),
_weight(get(&Edge::weight, static_cast<Graph const&>(g)))
{ }
void examine_edge(Graph::edge_descriptor e, const Graph& g) const {
auto& sname = _name[source(e, g)];
auto& tname = _name[target(e, g)];
if (sname.empty() && !tname.empty()) { // use your own logic here
tname += '(' + std::to_string(_weight[e]) + ')';
}
}
};
void operate_on(Graph& g) {
Visitor vis { g };
std::vector<boost::default_color_type> color(num_vertices(g));
boost::depth_first_search(g, vis, color.data());
}
Prints:
graph G {
0 [label=""];
1 [label="Hello(2.310000)(1.036000)"];
2 [label=""];
0--1 [label=2.31];
1--2 [label=1.036];
}
Which looks like:
Upvotes: 2