[Boost-users] boost::graph subgraph + write_graphviz()

426 views
Skip to first unread message

Chris Cooper

unread,
Aug 1, 2012, 6:57:22 AM8/1/12
to boost...@lists.boost.org
Hi,

I'm trying to output an image showing subgraphs [0] using
write_graphviz() in boost 1.48.0. I've tried to modify an example [1]
by adding a write_graphviz() call. eg...

std::ofstream ofs("out.dot");
const char* names[] = { "A", "B", "C", "D", "E", "F" };
write_graphviz(ofs, G0.m_graph, make_label_writer(names));

This works fine for displaying the underlying graph, but no subgraphs
are shown. When I do the following...

write_graphviz(ofs, G0, make_label_writer(names));

I get a host of errors including...

include/boost/graph/graphviz.hpp:377: error: no matching function
for call to ‘get(boost::label_writer<const char**>&, Vertex&)’

I notice a comment in graphviz.hpp [2] 'requires graph_name graph
property' so I had a look at the subgraph_properties.cpp example [3].
This seemed to only be showing vertex properties but didn't compile
out of the box. Is it possible for someone experienced in
boost::graph to post a modified version of [1] that can produce
something like image [0], or point me in the right direction?

Thanks,
Chris


[0] http://www.boost.org/doc/libs/1_48_0/libs/graph/doc/figs/subgraph.gif
[1] http://www.boost.org/doc/libs/1_48_0/libs/graph/example/subgraph.cpp
[2] http://www.boost.org/doc/libs/1_48_0/boost/graph/graphviz.hpp
[3] http://www.boost.org/doc/libs/1_48_0/libs/graph/example/subgraph_properties.cpp
_______________________________________________
Boost-users mailing list
Boost...@lists.boost.org
http://lists.boost.org/mailman/listinfo.cgi/boost-users

Jeremiah Willcock

unread,
Aug 1, 2012, 11:33:51 AM8/1/12
to boost...@lists.boost.org
On Wed, 1 Aug 2012, Chris Cooper wrote:

> Hi,
>
> I'm trying to output an image showing subgraphs [0] using
> write_graphviz() in boost 1.48.0. I've tried to modify an example [1]
> by adding a write_graphviz() call. eg...
>
> std::ofstream ofs("out.dot");
> const char* names[] = { "A", "B", "C", "D", "E", "F" };
> write_graphviz(ofs, G0.m_graph, make_label_writer(names));
>
> This works fine for displaying the underlying graph, but no subgraphs
> are shown. When I do the following...
>
> write_graphviz(ofs, G0, make_label_writer(names));
>
> I get a host of errors including...
>
> include/boost/graph/graphviz.hpp:377: error: no matching function
> for call to �get(boost::label_writer<const char**>&, Vertex&)�

Can you please try using write_graphviz_dp instead of write_graphviz?
That might work better, and allows you to specify properties to write more
easily.

> I notice a comment in graphviz.hpp [2] 'requires graph_name graph
> property' so I had a look at the subgraph_properties.cpp example [3].
> This seemed to only be showing vertex properties but didn't compile
> out of the box. Is it possible for someone experienced in
> boost::graph to post a modified version of [1] that can produce
> something like image [0], or point me in the right direction?

I just committed a fixed version of subgraph_properties.cpp to the trunk,
so that one should compile now.

-- Jeremiah Willcock

Chris Cooper

unread,
Aug 1, 2012, 9:39:29 PM8/1/12
to boost...@lists.boost.org
Thanks Jeremiah,

I've now got something that compiles and runs, but still doesn't
include 'subgraph' clauses in the dot output and corresponding
rectangles when rendered. Interestingly if I now change back to
write_graphviz(ofs, G0) I do get some subgraph clauses, but the names
are messed up presumably since they're now being set up using dynamic
properties. Also the subgraphs still don't render in this case.

Thanks,
Chris


#include <boost/graph/graphviz.hpp>
#include <boost/graph/subgraph.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <string>
#include <sstream>

using namespace boost;
using namespace std;

int main()
{
// Vertex properties
typedef property<vertex_name_t, std::string, property<vertex_color_t,
float> > vertex_p;
// Edge properties
//typedef property<edge_weight_t, double> edge_p;
typedef property<edge_index_t, int> edge_p;
// Graph properties
typedef property<graph_name_t, std::string> graph_p;
// adjacency_list-based type
typedef subgraph< adjacency_list<vecS, vecS, directedS, vertex_p,
edge_p, graph_p> > graph_t;

// Construct an empty graph and prepare the dynamic_property_maps.
graph_t G0(6);
enum {A, B, C, D, E, F};

dynamic_properties dp;

property_map<graph_t, vertex_name_t>::type name = get(vertex_name, G0);
dp.property("node_id", name);

name[A] = "A";
name[B] = "B";
name[C] = "C";
name[D] = "D";
name[E] = "E";
name[F] = "F";

graph_t& G1 = G0.create_subgraph();
enum {A1, B1, C1};

add_vertex(C, G1); // global vertex C becomes local A1 for G1
add_vertex(E, G1); // global vertex E becomes local B1 for G1
add_vertex(F, G1); // global vertex F becomes local C1 for G1

property_map<graph_t, vertex_name_t>::type name1 = get(vertex_name_t(), G1);
name1[A1] = "A1";
name1[B1] = "B1";
name1[C1] = "C1";

graph_t& G2 = G0.create_subgraph();
enum {A2, B2};

add_vertex(A, G2); // global vertex A becomes local A2 for G2
add_vertex(C, G2); // global vertex C becomes local B2 for G2

property_map<graph_t, vertex_name_t>::type name2 = get(vertex_name_t(), G2);
name2[A2] = "A2";
name2[B2] = "B2";

add_edge(A, B, G0);
add_edge(B, C, G0);
add_edge(B, D, G0);
add_edge(E, B, G0);
add_edge(E, F, G0);
add_edge(F, D, G0);

property_map<graph_t, vertex_color_t>::type mass = get(vertex_color, G0);
dp.property("mass", mass);

property_map<graph_t, edge_index_t>::type edgeid = get(edge_index, G0);
dp.property("edgeid", edgeid);

#if 0
get_property(G0, graph_name) = "G0";
get_property(G1, graph_name) = "G1";
get_property(G2, graph_name) = "G2";
#else
// set up graph names
{
boost::ref_property_map<graph_t*, std::string>
gname0(get_property(G0, graph_name));
dp.property("name", gname0);
gname0[0] = "G0";

boost::ref_property_map<graph_t*, std::string>
gname1(get_property(G1, graph_name));
dp.property("name", gname1);
gname1[0] = "G1";

boost::ref_property_map<graph_t*, std::string>
gname2(get_property(G1, graph_name));
dp.property("name", gname2);
gname2[0] = "G2";
}
#endif

std::ofstream ofs("out.dot");
write_graphviz_dp(ofs, G0, dp, std::string("node_id"));

return 0;
}




On 2 August 2012 01:33, Jeremiah Willcock <jewi...@osl.iu.edu> wrote:
> On Wed, 1 Aug 2012, Chris Cooper wrote:
>
>> Hi,
>>
>> I'm trying to output an image showing subgraphs [0] using
>> write_graphviz() in boost 1.48.0. I've tried to modify an example [1]
>> by adding a write_graphviz() call. eg...
>>
>> std::ofstream ofs("out.dot");
>> const char* names[] = { "A", "B", "C", "D", "E", "F" };
>> write_graphviz(ofs, G0.m_graph, make_label_writer(names));
>>
>> This works fine for displaying the underlying graph, but no subgraphs
>> are shown. When I do the following...
>>
>> write_graphviz(ofs, G0, make_label_writer(names));
>>
>> I get a host of errors including...
>>
>> include/boost/graph/graphviz.hpp:377: error: no matching function
>> for call to ‘get(boost::label_writer<const char**>&, Vertex&)’
>
>
> Can you please try using write_graphviz_dp instead of write_graphviz? That
> might work better, and allows you to specify properties to write more
> easily.
>
>
>> I notice a comment in graphviz.hpp [2] 'requires graph_name graph
>> property' so I had a look at the subgraph_properties.cpp example [3].
>> This seemed to only be showing vertex properties but didn't compile
>> out of the box. Is it possible for someone experienced in
>> boost::graph to post a modified version of [1] that can produce
>> something like image [0], or point me in the right direction?
>
>
> I just committed a fixed version of subgraph_properties.cpp to the trunk, so
> that one should compile now.
>
> -- Jeremiah Willcock

Jeremiah Willcock

unread,
Aug 1, 2012, 9:58:33 PM8/1/12
to boost...@lists.boost.org
On Thu, 2 Aug 2012, Chris Cooper wrote:

> Thanks Jeremiah,
>
> I've now got something that compiles and runs, but still doesn't
> include 'subgraph' clauses in the dot output and corresponding
> rectangles when rendered. Interestingly if I now change back to
> write_graphviz(ofs, G0) I do get some subgraph clauses, but the names
> are messed up presumably since they're now being set up using dynamic
> properties. Also the subgraphs still don't render in this case.

It looks like you do need to use write_graphviz (without _dp) to get
subgraphs in the output. Could you please try your original code with the
Boost trunk and see what happens? That will allow your line numbers to
match up with mine. Also, a full code example using write_graphviz would
be helpful. Thank you.

Chris Cooper

unread,
Aug 1, 2012, 10:56:37 PM8/1/12
to boost...@lists.boost.org
With the dynamic property code I posted previously you should be able
to just change that write_graphviz_dp call to write_graphviz(ofs, G0).
Here's some code as per my original post. With boost trunk it's
currently failing with...

include/boost/pending/property.hpp:126:5: error: no type named ‘type’
in ‘struct boost::lookup_one_property_internal<boost::no_property,
boost::graph_graph_attribute_t>’

Thanks,
Chris



#include <boost/config.hpp>
#include <iostream>
#include <fstream>
#include <boost/graph/subgraph.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graph_utility.hpp>
#include <boost/graph/graphviz.hpp>

int main(int,char*[])
{
using namespace boost;
typedef adjacency_list_traits<vecS, vecS, directedS> Traits;
typedef property<vertex_color_t, int> vertex_p;
typedef property<edge_index_t, int> edge_p;
typedef property<graph_name_t, std::string> graph_p;
typedef subgraph< adjacency_list<vecS, vecS, directedS, vertex_p,
edge_p, graph_p > > Graph;

const int N = 6;
Graph G0(N);
enum { A, B, C, D, E, F}; // for conveniently refering to vertices in G0

Graph& G1 = G0.create_subgraph();
Graph& G2 = G0.create_subgraph();
enum { A1, B1, C1 }; // for conveniently refering to vertices in G1
enum { A2, B2 }; // for conveniently refering to vertices in G2

add_vertex(C, G1); // global vertex C becomes local A1 for G1
add_vertex(E, G1); // global vertex E becomes local B1 for G1
add_vertex(F, G1); // global vertex F becomes local C1 for G1

add_vertex(A, G2); // global vertex A becomes local A1 for G2
add_vertex(B, G2); // global vertex B becomes local B1 for G2

add_edge(A, B, G0);
add_edge(B, C, G0);
add_edge(B, D, G0);
add_edge(E, B, G0);
add_edge(E, F, G0);
add_edge(F, D, G0);

add_edge(A1, C1, G1); // (A1,C1) is subgraph G1 local indices for (C,F).

const char* names[] = { "A", "B", "C", "D", "E", "F" };
const char* enames[] = { "0", "1", "2", "3", "4", "5" };
const char* gnames[] = { "G0", "G1", "G2" };

std::ofstream ofs("out.dot");

#if 0
// works
write_graphviz(ofs, G0.m_graph, make_label_writer(names));
#else
write_graphviz(ofs, G0);
//write_graphviz(ofs, G0, make_label_writer(names),
make_label_writer(enames), make_label_writer(gnames));
#endif

return 0;

Jeremiah Willcock

unread,
Aug 2, 2012, 11:38:32 AM8/2/12
to boost...@lists.boost.org
On Thu, 2 Aug 2012, Chris Cooper wrote:

> With the dynamic property code I posted previously you should be able
> to just change that write_graphviz_dp call to write_graphviz(ofs, G0).
> Here's some code as per my original post. With boost trunk it's
> currently failing with...
>
> include/boost/pending/property.hpp:126:5: error: no type named �type�
> in �struct boost::lookup_one_property_internal<boost::no_property,
> boost::graph_graph_attribute_t>�

It looks like some of the property map defaults in write_graphviz got lost
when I changed how property lookup works, and write_graphviz on subgraphs
isn't using the default property writers like the normal version of
write_graphviz does. The workaround is to change your property
declarations to:

typedef std::map<std::string, std::string> smap;
typedef property<vertex_color_t, int,
property<vertex_attribute_t, smap> > vertex_p;
typedef property<edge_index_t, int,
property<edge_attribute_t, smap> > edge_p;
typedef property<graph_name_t, std::string,
property<graph_graph_attribute_t, smap,
property<graph_vertex_attribute_t, smap,
property<graph_edge_attribute_t, smap> > > >
graph_p;

You don't need to initialize the new properties added here; the
default-constructed empty versions are fine. If you want to save storage,
you can create a dummy class that acts like an empty std::map<string,
string> and use that instead of smap in the code.

-- Jeremiah Willcock

Chris Cooper

unread,
Aug 2, 2012, 8:28:58 PM8/2/12
to boost...@lists.boost.org
Ok thanks Jeremiah. This compiles and produces subgraph clauses, but
it seems from browsing the subgraph implementation in
boost/graph/graphviz.hpp that I'm currently unable to...

- use custom label writers with subgraphs
- insert appropriate per-subgraph formatting detail required to get
subgraphs rendering correctly via dot (eg as is shown in
libs/graph/example/graphviz_test.dot).

I think I'd better leave this for now unless the above is
straightforward. Thanks for your time. I may look into it again at
some stage, maybe even get around to submitting a patch if I find the
time...

Thanks,
Chris

On 3 August 2012 01:38, Jeremiah Willcock <jewi...@osl.iu.edu> wrote:
> On Thu, 2 Aug 2012, Chris Cooper wrote:
>
>> With the dynamic property code I posted previously you should be able
>> to just change that write_graphviz_dp call to write_graphviz(ofs, G0).
>> Here's some code as per my original post. With boost trunk it's
>> currently failing with...
>>
>> include/boost/pending/property.hpp:126:5: error: no type named ‘type’
>> in ‘struct boost::lookup_one_property_internal<boost::no_property,
>> boost::graph_graph_attribute_t>’

Jeremiah Willcock

unread,
Aug 2, 2012, 9:37:54 PM8/2/12
to boost...@lists.boost.org
On Fri, 3 Aug 2012, Chris Cooper wrote:

> Ok thanks Jeremiah. This compiles and produces subgraph clauses, but
> it seems from browsing the subgraph implementation in
> boost/graph/graphviz.hpp that I'm currently unable to...
>
> - use custom label writers with subgraphs

That doesn't look to be supported, but *_attribute_t maps should be able
to do almost everything that you could do with custom label writers.

> - insert appropriate per-subgraph formatting detail required to get
> subgraphs rendering correctly via dot (eg as is shown in
> libs/graph/example/graphviz_test.dot).

For subgraph names (and "cluster*" names that trigger special behavior in
Dot), you can use the graph_name property of each subgraph. I don't know
about getting edges between subgraphs.

> I think I'd better leave this for now unless the above is
> straightforward. Thanks for your time. I may look into it again at
> some stage, maybe even get around to submitting a patch if I find the
> time...

I think subgraph rendering is probably easy, depending on what you want,
but it does look like edges between subgraphs and custom writers are not
possible without a lot of work.

Given that subgraph writing is not documented at all, you might want to
just write your own application-specific output code, probably using the
helper functions (such as for string escaping) that are used in
write_graphviz. The ideal would probably be something that can write out
everything that's in the parser_result struct in
boost/graph/detail/read_graphviz_new.hpp, but that would likely be a lot
of work.

-- Jeremiah Willcock

>
> On 3 August 2012 01:38, Jeremiah Willcock <jewi...@osl.iu.edu> wrote:
>> On Thu, 2 Aug 2012, Chris Cooper wrote:
>>
>>> With the dynamic property code I posted previously you should be able
>>> to just change that write_graphviz_dp call to write_graphviz(ofs, G0).
>>> Here's some code as per my original post. With boost trunk it's
>>> currently failing with...
>>>
>>> include/boost/pending/property.hpp:126:5: error: no type named οΏ½typeοΏ½
>>> in οΏ½struct boost::lookup_one_property_internal<boost::no_property,
>>> boost::graph_graph_attribute_t>οΏ½
Reply all
Reply to author
Forward
0 new messages