I'm struggling trying to manually colouring graph's vertices using boost. I wrote the code below but I can't figure it out why the generated file does not have any colour.
int main(int,char*[]) {
typedef property<edge_name_t, string> EdgeProperties;
typedef property<vertex_name_t, string, property<vertex_color_t, default_color_type>> VertexProperties;
typedef adjacency_list<vecS, vecS, directedS, VertexProperties, EdgeProperties> Graph;
typedef graph_traits<Graph>::vertex_descriptor Vertex;
typedef graph_traits<Graph>::edge_descriptor Edge;
Graph g;
property_map<Graph, vertex_name_t>::type vertex_label = get(vertex_name, g);
property_map<Graph, vertex_color_t>::type color_map = get(vertex_color, g);
property_map<Graph, edge_name_t>::type edge_label = get(edge_name, g);
Vertex v1 = add_vertex(g);
vertex_label[v1] = "v1";
put(color_map, v1, boost::red_color);
std::ofstream outf("example.gv");
write_graphviz(outf, g,
make_label_writer(vertex_label),
make_label_writer(edge_label)
);
return 0;
}
Vertex colourings are really an algorithmic detail. There is no "automatic" provision to translate it to graphviz, as far as I know.
You can add custom attributes though.
I'd usually change it around to use dynamic properties:
std::ofstream outf("example.gv");
dynamic_properties dp;
dp.property("node_id", get(vertex_index, g));
dp.property("label", vertex_label);
dp.property("label", edge_label);
write_graphviz_dp(outf, g, dp);
Now it's as simple as adding a new vertex attribute to the dynamic set:
dp.property("color", make_transform_value_property_map(&color_to_string, color_map));
The only complication is that you need to supply the conversion from default_color_type to text. This is another symptom of the fact that the color maps aren't usually used for representational purposes. Here's a fully working sample:
Live On Coliru
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graphviz.hpp>
#include <iostream>
using namespace boost;
inline char const* color_to_string(default_color_type c) {
switch(c) {
case default_color_type::red_color: return "red";
case default_color_type::green_color: return "green";
case default_color_type::gray_color: return "gray";
case default_color_type::white_color: return "white";
case default_color_type::black_color: return "black";
}
return ""; // not known
}
int main() {
typedef property<edge_name_t, std::string> EdgeProperties;
typedef property<vertex_name_t, std::string, property<vertex_color_t, default_color_type>> VertexProperties;
typedef adjacency_list<vecS, vecS, directedS, VertexProperties, EdgeProperties> Graph;
typedef graph_traits<Graph>::vertex_descriptor Vertex;
Graph g;
property_map<Graph, vertex_name_t>::type vertex_label = get(vertex_name, g);
property_map<Graph, vertex_color_t>::type color_map = get(vertex_color, g);
property_map<Graph, edge_name_t>::type edge_label = get(edge_name, g);
Vertex v1 = add_vertex(g);
vertex_label[v1] = "v1";
put(color_map, v1, boost::red_color);
dynamic_properties dp;
dp.property("node_id", get(vertex_index, g));
dp.property("label", vertex_label);
dp.property("label", edge_label);
dp.property("color", make_transform_value_property_map(&color_to_string, color_map));
write_graphviz_dp(std::cout, g, dp);
}
Prints:
digraph G {
0 [color=red, label=v1];
}
Related
I'm studying BGL now and I found a tutorial for it. Everything worked fine from it until I reached function to add_named_vertex. Here is a piece of code I have, that does not work as I (and tutorial) expect:
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graphviz.hpp>
#include <type_traits>
#include <iostream>
#include <sstream>
#include <string>
boost::adjacency_list<
boost::vecS,
boost::vecS,
boost::directedS,
boost::property<boost::vertex_name_t, std::string>
>
create_empty_graph() { return {}; }
template<typename graph, typename name_type>
typename boost::graph_traits<graph>::vertex_descriptor
add_named_vertex(const name_type& vertex_name, graph& g) noexcept {
const auto vd = boost::add_vertex(g);
auto vertex_name_map = get(boost::vertex_name, g);
put(vertex_name_map, vd, vertex_name);
return vd;
}
int main()
{
auto g = create_empty_graph();
const auto va = add_named_vertex("One", g);
const auto vb = add_named_vertex("Two", g);
boost::add_edge(va,vb, g);
std::stringstream f;
boost::write_graphviz(f, g);
std::cout << f.str() << std::endl;
return 0;
}
I expect:
digraph G {
0[label=One];
1[label=Two];
0->1;
}
But here is what I got:
digraph G {
0;
1;
0->1;
}
As you can see, there are no labels in the output of this code. Could you please tell me, what am I missing? Is it expected behavior?
Tried both clang++ and gcc and for range of Boost version (1.69 - 1.71).
Yes it is expectd behavior. To print the labels, add a property writer:
auto vlw = boost::make_label_writer(boost::get(boost::vertex_name, g));
boost::write_graphviz(f, g, vlw);
See it Live on Coliru
Or, as I prefer, use write_graphviz_dp to use dynamic_properties:
boost::dynamic_properties dp;
dp.property("node_id", boost::get(boost::vertex_index, g));
dp.property("label", boost::get(boost::vertex_name, g));
boost::write_graphviz_dp(f, g, dp);
See it Live on Coliru
It may seem like more work, but it is easy and flexible with many vertex/edge properties. You can search my answers for good examples of this.
Both the above solutions print
digraph G {
0[label=One];
1[label=Two];
0->1 ;
}
BONUS
You don't need the add_named_vertex function. You can initialize the property directly with boost::add_vertex:
const auto va = add_vertex({"One"}, g);
const auto vb = add_vertex({"Two"}, g);
add_edge(va, vb, g);
I'm trying to generate a .dot which displays an horizontal graph with Boost Graph Library.
My code when creating my graph looks like this:
struct VertexP {
std::string tag;
};
struct EdgeP {
std::string symbol;
};
struct GraphP{
std::string orientation;
};
typedef boost::adjacency_list<boost::vecS,
boost::vecS, boost::directedS,
VertexP, EdgeP, GraphP> Graph;
GraphP property;
property.orientation = "LR";
Graph graph(property);
// Then fill the graph
The code I'm using to generate the .dot file is something like this:
Graph g = creator.AutomatonToGraph(&automaton);
ofstream dot_file("automaton.dot");
dynamic_properties dp;
dp.property("node_id", get(&VertexP::tag, g));
dp.property("label", get(&VertexP::tag, g));
dp.property("label", get(&EdgeP::symbol, g));
write_graphviz_dp(dot_file, g, dp);
This writes the .dot file perfectly with nodes and edges labels, but my problem is that I want to add the rankdir=LR graph property to the output file. I tried with:
Graph g = creator.AutomatonToGraph(&automaton);
ofstream dot_file("automaton.dot");
dynamic_properties dp;
dp.property("node_id", get(&VertexP::tag, g));
dp.property("label", get(&VertexP::tag, g));
dp.property("label", get(&EdgeP::symbol, g));
dp.property("rankdir", get(&GraphP::orientation, g));
write_graphviz_dp(dot_file, g, dp);
But I get a big chunk of errors starting by this:
/src/lab2.cc:48:55: required from here
/usr/include/boost/graph/detail/adjacency_list.hpp:2585:29: error: forming reference to void
typedef value_type& reference;
^~~~~~~~~
I'm really new using BGL, what I'm doing wrong?
Reading into the implementation of dynamic_graph_properties_writer, I figured out you should probably just do
dp.property("rankdir", boost::make_constant_property<Graph*>(std::string("LR")));
For dynamic retrieval you can use a function property map: (map set/get requests into C++ class/structure changes):
#include <boost/property_map/function_property_map.hpp>
dp.property("rankdir", boost::make_function_property_map<Graph*>([](Graph const* g) { return g->m_property->orientation; }));
Live Demo
See it Live On Wandbox
#include <boost/graph/adj_list_serialize.hpp>
#include <boost/graph/graphviz.hpp>
#include <boost/property_map/function_property_map.hpp>
#include <fstream>
using namespace boost;
struct VertexP { std::string tag; };
struct EdgeP { std::string symbol; };
struct GraphP { std::string orientation; };
typedef adjacency_list<vecS, vecS, directedS, VertexP, EdgeP, GraphP> Graph;
int main() {
Graph g(GraphP{"LR"});
// Then fill the graph
add_edge(
add_vertex(VertexP{ "tag1" }, g),
add_vertex(VertexP{ "tag2" }, g),
EdgeP{ "symbol" }, g
);
{
std::ofstream dot_file("automaton.dot");
dynamic_properties dp;
dp.property("node_id", get(&VertexP::tag, g));
dp.property("label", get(&VertexP::tag, g));
dp.property("label", get(&EdgeP::symbol, g));
dp.property("rankdir", boost::make_constant_property<Graph*>(std::string("LR")));
dp.property("dummy", boost::make_function_property_map<Graph*>([](Graph const* g) { return g->m_property->orientation; }));
write_graphviz_dp(dot_file, g, dp);
}
}
Which writes
digraph G {
dummy=LR;
rankdir=LR;
tag2 [label=tag2];
tag1 [label=tag1];
tag1->tag2 [label=symbol];
}
I am trying to run the fruchterman_reingold_force_directed_layout algorithm on a boost::adjacency_list<boost::listS, boost::listS, boost::bidirectionalS, Vertex, Edge> graph.
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/fruchterman_reingold.hpp>
#include <boost/graph/random_layout.hpp>
typedef boost::rectangle_topology<> RectTopology;
typedef RectTopology::point_type point;
struct Vertex {
Vertex(int i) : index(i) {};
int index;
point position;
};
struct Edge {
double weight;
};
typedef boost::adjacency_list<boost::listS, boost::listS, boost::bidirectionalS, Vertex, Edge> Graph;
int main() {
Graph graph;
auto vertexIdPropertyMap = boost::get(&Vertex::index, graph);
Vertex n1(0);
boost::add_vertex(std::move(n1), graph);
Vertex n2(1);
boost::add_vertex(std::move(n2), graph);
Vertex n3(2);
boost::add_vertex(std::move(n3), graph);
boost::add_edge(boost::vertex(0, graph), boost::vertex(1, graph), Edge(), graph);
boost::add_edge(boost::vertex(0, graph), boost::vertex(2, graph), Edge(), graph);
boost::minstd_rand gen;
RectTopology rectangleTopology(gen, -25, -25, 25, 25);
boost::random_graph_layout(graph, boost::get(&Vertex::position, graph), rectangleTopology);
boost::fruchterman_reingold_force_directed_layout(graph, boost::get(&Vertex::position, graph), rectangleTopology);
return 0;
}
When compiling, the first error is the following:
error: forming reference to void
typedef value_type& reference;
I have done some research and apparently this is due to the fact that it is a listS vertex list - with vecS it works.
However, I still want to use a listS and apparently it is possible:
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/fruchterman_reingold.hpp>
#include <boost/graph/random_layout.hpp>
enum vertex_position_t { vertex_position };
namespace boost { BOOST_INSTALL_PROPERTY(vertex, position); }
typedef boost::rectangle_topology<> RectTopology;
typedef RectTopology::point_type point;
struct simple_edge {
int first, second;
};
int main() {
enum { A, B, C };
simple_edge triangular_edges[2] = {
{A, B}, {A, C}
};
typedef boost::adjacency_list<boost::listS, boost::listS, boost::bidirectionalS,
// Vertex properties
boost::property<boost::vertex_index_t, int,
boost::property<vertex_position_t, point> >,
// Edge properties
boost::property<boost::edge_weight_t, double>> Graph;
Graph g(&triangular_edges[0], &triangular_edges[1], 3);
boost::minstd_rand gen;
RectTopology rect_top(gen, -25, -25, 25, 25);
boost::random_graph_layout(g, boost::get(vertex_position, g), rect_top);
boost::fruchterman_reingold_force_directed_layout(g, boost::get(vertex_position, g), rect_top);
return 0;
}
The problem here is that I don't want to use the "old-style" properties but the bundled properties instead. Here someone was attempting something similar, but the fruchterman_reingold_force_directed_layout does not offer this index map if I am not mistaking.
The following code has errors:
cannot bind ‘std::basic_ostream’ lvalue to ‘std::basic_ostream&&
#include <boost/graph/graphviz.hpp>
void foo(int,char*[])
{
using namespace boost;
typedef boost::adjacency_list<
boost::setS, // outedge list
boost::setS, // vertex list
boost::directedS, // undirected
boost::no_property, // vertex prop
boost::no_property, // edge prop
boost::no_property, // graph prop
boost::setS // edgelistc
> Graph;
Graph g;
std::ostringstream dotname;
dotname << "a.dot";
std::ofstream dot(dotname.str());
write_graphviz(dot, g);
}
It works when
boost::vecS, // vertex list
Is it expected?
Change the vertex container selector to setS changes the vertex descriptor into a type that is not streamable.
You should, as with many many other algorithms in BGL, pass a separate vertex index:
IN: VertexAndEdgeListGraph& g
A directed or undirected graph. The graph's type must be a model of
VertexAndEdgeListGraph. In most cases, the graph must have an internal
vertex_index property map.
External Vertex Id mapping
Live On Coliru
#include <boost/graph/graphviz.hpp>
#include <boost/graph/random.hpp>
#include <random>
int main(int,char*[])
{
using namespace boost;
typedef boost::adjacency_list<setS, setS, directedS> Graph;
Graph g;
std::mt19937 prng{std::random_device{}()};
generate_random_graph(g, 3, 5, prng);
std::map<Graph::vertex_descriptor, size_t> ids;
for (auto u : make_iterator_range(vertices(g)))
ids[u] = ids.size();
default_writer w;
write_graphviz(std::cout, g, w, w, w, make_assoc_property_map(ids));
}
Prints e.g.
digraph G {
1;
2;
3;
1->2 ;
2->1 ;
2->3 ;
3->1 ;
3->2 ;
}
Internal property map:
You can put the property internally without much changing:
Live On Coliru
#include <boost/graph/graphviz.hpp>
#include <boost/graph/random.hpp>
#include <random>
int main(int,char*[])
{
using namespace boost;
typedef boost::adjacency_list<setS, setS, directedS, property<vertex_index_t, size_t> > Graph;
Graph g;
std::mt19937 prng{std::random_device{}()};
generate_random_graph(g, 3, 5, prng);
auto ids = get(vertex_index, g);
size_t num = 0;
for (auto u : make_iterator_range(vertices(g)))
put(ids, u, num++);
write_graphviz(std::cout, g);
}
I have defined a Graph using the Boost graph library,
typedef boost::property<boost::edge_weight_t, int> EdgeWeightProperty;
typedef boost::adjacency_list<boost::listS, boost::vecS,boost::undirectedS,boost::no_property,EdgeWeightProperty> Graph;
It is fairly straightforward to add edges using
boost::add_edge(vertice1, vertice2, weight, graph);
I have yet to figure out how to change the edge weight once it has been set. One possible solution would be to delete the edge and re-add it with the updated the weight value, however, that seems a bit excessive.
One solution is to do the following
typedef boost::adjacency_list<boost::setS, boost::vecS, boost::undirectedS,boost::no_property,EdgeWeightProperty> Graph;
typedef Graph::edge_descriptor Edge;
Graph g;
std::pair<Edge, bool> ed = boost::edge(v1,v2,g);
int weight = get(boost::edge_weight_t(), g, ed.first);
int weightToAdd = 10;
boost::put(boost::edge_weight_t(), g, ed.first, weight+weightToAdd);
An alternate solution would be to use property maps. Here's an example.
// Edge weight.
typedef boost::property<boost::edge_weight_t, int> EdgeWeightProperty;
// Graph.
typedef boost::adjacency_list< boost::listS,
boost::vecS,
boost::undirectedS,
boost::no_property,
EdgeWeightProperty > Graph;
// Vertex descriptor.
typedef typename boost::graph_traits<Graph>::vertex_descriptor Vertex;
// The Graph object
Graph g;
// Populates the graph.
Vertex v1 = boost::add_vertex(g);
Vertex v2 = boost::add_vertex(g);
Vertex v3 = boost::add_vertex(g);
boost::add_edge(v1, v2, EdgeWeightProperty(2), g);
boost::add_edge(v1, v3, EdgeWeightProperty(4), g);
boost::add_edge(v2, v3, EdgeWeightProperty(5), g);
// The property map associated with the weights.
boost::property_map < Graph,
boost::edge_weight_t >::type EdgeWeightMap = get(boost::edge_weight, g);
// Loops over all edges and add 10 to their weight.
boost::graph_traits< Graph >::edge_iterator e_it, e_end;
for(std::tie(e_it, e_end) = boost::edges(g); e_it != e_end; ++e_it)
{
EdgeWeightMap[*e_it] += 10;
}
// Prints the weighted edgelist.
for(std::tie(e_it, e_end) = boost::edges(g); e_it != e_end; ++e_it)
{
std::cout << boost::source(*e_it, g) << " "
<< boost::target(*e_it, g) << " "
<< EdgeWeightMap[*e_it] << std::endl;
}