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.
As a sidenote, fixing the random to actually be seeded and reliably uniform:
#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.