简体   繁体   中英

I'm using boost::filtered_graph, but the output of print_graph and write_graphviz differ -- any idea why?

I'm new to boost::graph (and boost really). I want to use boost::filtered_graph many times on the same original graph, and use the write_graphviz function to let me visualise the results. I think my understanding must be off though because the following code isn't doing what I think it should: to output the same graph with print_graph and write_graphviz .

MWE (compiled with C++14, gcc 9.3 on Ubuntu 20.04; boost version 1.73):

#include <cstdio>
#include <fstream>
#include <iostream>

#include <boost/graph/copy.hpp>
#include <boost/graph/filtered_graph.hpp>
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/graph_utility.hpp>
#include <boost/graph/graphviz.hpp>

using namespace std;

typedef boost::adjacency_list< boost::vecS, boost::vecS > Graph;

typedef boost::graph_traits<Graph>::vertex_descriptor vertex_descriptor;
typedef boost::graph_traits<Graph>::vertex_iterator vertex_iterator;

template <typename GraphType>
struct uniform_random_vertex_filter
{
    uniform_random_vertex_filter() : prob(1.0) {}   // default constructor is required
    uniform_random_vertex_filter(float p) : prob(p) {}
    bool operator()(const typename boost::graph_traits<GraphType>::vertex_descriptor& v) const
    {
        return drand48() < prob; // randomly select some vertices
    }
private:
    float prob;
};

int main(int argn, char **argc) {
    unsigned int n = 5;
    ofstream of;
    Graph g(n);
    vertex_iterator vit, uit, vend;
    // build a complete graph on n vertices (I'm sure there's a simpler command for this):
    for (boost::tie(vit,vend) = vertices(g); vit != vend; ++vit) {
        for (uit = vit; uit != vend; ++uit) {
            if (uit == vit) { continue; }
            add_edge(*vit, *uit, g);
        }
    }
    std::cout << "Original graph (OriginalGraph.dot):" << std::endl;
    boost::print_graph(g);
    of.open("OriginalGraph.dot", std::ios::trunc);
    boost::write_graphviz(of, g);
    of.close();

    uniform_random_vertex_filter<Graph> vfilter(0.5);

    boost::filtered_graph<Graph, boost::keep_all, uniform_random_vertex_filter<Graph> >
        filteredGraph(g, boost::keep_all(), vfilter);
    std::cout << "Filtered graph -- random selection of vertices (RandomVertexSelection.dot):" << std::endl;
    boost::print_graph(filteredGraph);
    Graph F;
    boost::copy_graph(filteredGraph,F);
    of.open("RandomVertexSelection.dot", std::ios::trunc);
    boost::write_graphviz(of, F);
    of.close();
    return 0;
}

Which produces this output:

> Debug/BoostGraphFilter
Original graph:
0 --> 1 2 3 4 
1 --> 2 3 4 
2 --> 3 4 
3 --> 4 
4 --> 
Filtered graph -- random selection of vertices (RandomVertexSelection.dot):
0 --> 1 2 3 4 
1 --> 2 3 
2 --> 3 
>

--- which is fine, but the dot files are:

> cat OriginalGraph.dot 
digraph G {
0;
1;
2;
3;
4;
0->1 ;
0->2 ;
0->3 ;
0->4 ;
1->2 ;
1->3 ;
1->4 ;
2->3 ;
2->4 ;
3->4 ;
}
> cat RandomVertexSelection.dot
digraph G {
0;
1;
2;
}

Hence the filtered_graph that's printed isn't the same as that written to .dot file (which has lost all the edges in this case).

Can someone please help me understand what I've done wrong?

Your filter is random. And since you didn't retain any state to make it transparent or deterministic, the results are random. Simple as that.

Ironically, at the same time you managed to get completely deterministic results across runs because you fail to use random correctly (eg seeding the generator).

In your case, the simplest would be to copy before first use: Live On Coliru

using Graph = boost::adjacency_list<boost::vecS, boost::vecS>;

Graph make_complete_graph(size_t n);
void  report(Graph const& g, std::string name);

struct uniform_random_vertex_filter {
    float prob = 1.0f;
    bool  operator()(auto v) const {
        return drand48() < prob;
    }
};

int main() {
    Graph g = make_complete_graph(5);

    report(g, "OriginalGraph");

    for (int pct = 30; pct < 100; pct+=10) {
        Graph F;

        boost::copy_graph(boost::filtered_graph( //
                              g,                 //
                              boost::keep_all{},
                              uniform_random_vertex_filter{pct / 100.0f}),
                          F);

        report(F, "RandomVertexSelection_" + std::to_string(pct));
    }
}

// build a complete graph on n vertices
Graph make_complete_graph(size_t n)
{
    Graph g(n);
    for (auto [vit, vend] = vertices(g); vit != vend; ++vit) {
        for (auto uit = vit; uit != vend; ++uit) {
            if (uit != vit)
                add_edge(*vit, *uit, g);
        }
    }
    return g;
}

void report(Graph const& g, std::string name) {
    boost::print_graph(g, std::cout << name << ":");

    std::ofstream of(name + ".dot");
    boost::write_graphviz(of, g);
}

If you want a stable random "cut" of a graph, make the filter stateful. This could be handy eg if you have a graph too large to copy.

Fixing The Random

As a sidenote, fixing the random to actually be seeded and reliably uniform:

Live On Coliru

#include <boost/graph/copy.hpp>
#include <boost/graph/filtered_graph.hpp>
#include <boost/graph/graph_utility.hpp>
#include <boost/graph/graphviz.hpp>
#include <fstream>
#include <iostream>
#include <random>

using Graph = boost::adjacency_list<boost::vecS, boost::vecS>;
using Filter = std::function<bool(Graph::vertex_descriptor)>;

Graph make_complete_graph(size_t n);
void  report(Graph const& g, std::string name);

int main() {
    Graph g = make_complete_graph(5);

    report(g, "OriginalGraph");

    std::mt19937 urbg{std::random_device{}()};

    for (int pct = 30; pct < 100; pct += 10) {
        Graph F;
        std::bernoulli_distribution dist(pct / 100.);
        boost::copy_graph(
            boost::filtered_graph(g, boost::keep_all{},
                Filter([&](auto) { return dist(urbg); })),
            F);

        report(F, "RandomVertexSelection_" + std::to_string(pct));
    }
}

// build a complete graph on n vertices
Graph make_complete_graph(size_t n)
{
    Graph g(n);
    for (auto [vit, vend] = vertices(g); vit != vend; ++vit) {
        for (auto uit = vit; uit != vend; ++uit) {
            if (uit != vit)
                add_edge(*vit, *uit, g);
        }
    }
    return g;
}

void report(Graph const& g, std::string name) {
    boost::print_graph(g, std::cout << name << ":");

    std::ofstream of(name + ".dot");
    boost::write_graphviz(of, g);
}

Now prints different random cuts every run.

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