简体   繁体   中英

How to traverse a graph and extract edge weights along the way with boost?

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.

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:

The Sample Graph

Live On Coliru

#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: 在此处输入图片说明

Adding DFS

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)

Write - Access

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

Full Demo

Live On Coliru

#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 : 在此处输入图片说明

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM