BG
BG
Contents
1 Introduction 5
1.1 Why this tutorial . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.2 Tutorial style . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.3 Coding style . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.4 License . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.5 Feedback . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.6 Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.7 Outline . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1
2.7 Get the vertex iterators . . . . . . . . . . . . . . . . . . . . . . . 19
2.8 Get all vertex descriptors . . . . . . . . . . . . . . . . . . . . . . 20
2.9 Add an edge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
2.10 boost::add_edge result . . . . . . . . . . . . . . . . . . . . . . . . 23
2.11 Getting the edge iterators . . . . . . . . . . . . . . . . . . . . . . 23
2.12 Edge descriptors . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
2.13 Get all edge descriptors . . . . . . . . . . . . . . . . . . . . . . . 25
2.14 Creating a directed graph . . . . . . . . . . . . . . . . . . . . . . 26
2.14.1 Graph . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
2.14.2 Function to create such a graph . . . . . . . . . . . . . . . 27
2.14.3 Creating such a graph . . . . . . . . . . . . . . . . . . . . 27
2.14.4 The .dot file produced . . . . . . . . . . . . . . . . . . . . 28
2.14.5 The .svg file produced . . . . . . . . . . . . . . . . . . . . 28
2.15 Creating K2 , a fully connected undirected graph with two vertices 29
2.15.1 Graph . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
2.15.2 Function to create such a graph . . . . . . . . . . . . . . . 29
2.15.3 Creating such a graph . . . . . . . . . . . . . . . . . . . . 30
2.15.4 The .dot file produced . . . . . . . . . . . . . . . . . . . . 31
d
2.15.5 The .svg file produced . . . . . . . . . . . . . . . . . . . . 31
2.16 Creating K3 , a fully connected undirected graph with three
vertices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
2.16.1 Graph . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
2.16.2 Function to create such a graph . . . . . . . . . . . . . . . 32
2.16.3 Creating such a graph . . . . . . . . . . . . . . . . . . . . 33
2.16.4 The .dot file produced . . . . . . . . . . . . . . . . . . . . 33
d
2.16.5 The .svg file produced . . . . . . . . . . . . . . . . . . . . 34
2.17 Creating a path graph . . . . . . . . . . . . . . . . . . . . . . 34
2.17.1 Graph . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
2.17.2 Function to create such a graph . . . . . . . . . . . . . . . 35
2.17.3 Creating such a graph . . . . . . . . . . . . . . . . . . . . 35
2.17.4 The .dot file produced . . . . . . . . . . . . . . . . . . . . 36
d
2.17.5 The .svg file produced . . . . . . . . . . . . . . . . . . . . 36
2.18 Creating a Peterson graph . . . . . . . . . . . . . . . . . . . . 37
2.18.1 Graph . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
2.18.2 Function to create such a graph . . . . . . . . . . . . . . . 38
2.18.3 Creating such a graph . . . . . . . . . . . . . . . . . . . . 39
2.18.4 The .dot file produced . . . . . . . . . . . . . . . . . . . . 40
2.18.5 The .svg file produced . . . . . . . . . . . . . . . . . . . . 41
d
3.1 Getting the vertices’ out degree . . . . . . . . . . . . . . . . . . . 43
d
3.2 Is there an edge between two vertices? . . . . . . . . . . . . . 45
dd
3.3 Get the edge between two vertices . . . . . . . . . . . . . . . . 46
dd
3.4 Create a direct-neighbour subgraph from a vertex descriptor 48
3.5 Create a direct-neighbour subgraph from a vertex descriptor
including inward edges . . . . . . . . . . . . . . . . . . . . . . . . 50
2
3.6 dd Creating all direct-neighbour subgraphs from a graph with-
3.7 d Are two graphs isomorphic? . . . . . . . . . . . . . . . . . . .
out properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
3
6 Building graphs with bundled edges and vertices 95
6.1 Creating the bundled edge class . . . . . . . . . . . . . . . . . . . 96
6.2 Create an empty directed graph with bundled edges and vertices 98
6.3 Create an empty undirected graph with bundled edges and vertices 99
6.4 Add a bundled edge . . . . . . . . . . . . . . . . . . . . . . . . . 100
6.5 Getting the bundled edges my_edges . . . . . . . . . . . . . . . . 102
6.6 Creating a Markov-chain with bundled edges and vertices . . . . 103
6.6.1 Graph . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
6.6.2 Function to create such a graph . . . . . . . . . . . . . . . 104
6.6.3 Creating such a graph . . . . . . . . . . . . . . . . . . . . 106
6.6.4 The .dot file produced . . . . . . . . . . . . . . . . . . . . 106
6.6.5 The .svg file produced . . . . . . . . . . . . . . . . . . . . 108
6.7 Creating K3 with bundled edges and vertices . . . . . . . . . . . 109
6.7.1 Graph . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
6.7.2 Function to create such a graph . . . . . . . . . . . . . . . 111
6.7.3 Creating such a graph . . . . . . . . . . . . . . . . . . . . 112
6.7.4 The .dot file produced . . . . . . . . . . . . . . . . . . . . 112
6.7.5 The .svg file produced . . . . . . . . . . . . . . . . . . . . 114
4
8.6.5 The .svg file produced . . . . . . . . . . . . . . . . . . . . 138
12 Errors 147
12.1 Formed reference to void . . . . . . . . . . . . . . . . . . . . . . . 147
12.2 No matching function for call to ‘clear_out_edges’ . . . . . . . . 148
12.3 No matching function for call to ‘clear_in_edges’ . . . . . . . . . 148
12.4 Undefined reference to boost::detail::graph::read_graphviz_new . 148
12.5 Property not found: node_id . . . . . . . . . . . . . . . . . . . . 148
12.6 Stream zeroes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149
13 Appendix 151
13.1 List of all edge, graph and vertex properties . . . . . . . . . . . . 151
13.2 Graphviz attributes . . . . . . . . . . . . . . . . . . . . . . . . . . 151
1 Introduction
This is ’Boost.Graph Cookbook 1: Basics’ (formerly: ’A well-connected C++14
Boost.Graph tutorial’), version 3.0.
5
What I had were the book [8] and the Boost.Graph website, both did not satisfy
these requirements.
d
the code presented is given, including references to the literature. Chapters
marked with ’ ’ are optional, less verbose and bring no new information to the
storyline.
Index In the index, I did first put all my long-named functions there literally,
but this resulted in a very sloppy layout. Instead, the function ’do_something’
can be found as ’Do something’ in the index. On the other hand, STL and Boost
functions like ’std::do_something’ and ’boost::do_something’ can be found as
such in the index.
C++14 All coding snippets are taken from compiled and tested C++14 code.
I chose to use C++14 because it was available to me on all local and remote
computers. Next to this, it makes code even shorter then just C++11.
Coding standard I use the coding style from the Core C++ Guidelines. At
the time of this writing, the Core C++ Guidelines were still in early develop-
ment, so I can only hope the conventions I then chose to follow are still Good
Ideas.
6
No comments in code It is important to add comments to code. In this
tutorial, however, I have chosen not to put comments in code, as I already
describe the function in the tutorial its text. This way, it prevents me from
saying the same things twice.
Long function names and readability Due to my long function names and
the limitation of ≈50 characters per line, sometimes the code does get to look
a bit awkward. I am sorry for this.
Use of auto I prefer to use the keyword auto over doubling the lines of code
for using statements. Often the ‘do’ functions return an explicit data type, these
can be used for reference. Sometime I deduce the return type using decltype
and a function with the same return type. When C++17 gets accessible, I
will use ‘decltype(auto)’. If you really want to know a type, you can use the
‘get_type_name’ function (chapter 11.1).
Use of STL algorithms I try to use STL algorithms wherever I can. Also you
should prefer algorithm calls over hand-written for-loops ([9] chapter 18.12.1,
[7] item 43). Sometimes using these algorithms becomes a burden on the lines
of code. This is because in C++11, a lambda function argument (use by the
algorithm) must have its data type specified. It may take multiple lines of ‘using’
statements being able to do so. In C++14 one can use ‘auto’ there as well. So,
only if it shortens the number of lines significantly, I use raw for-loops, even
though you shouldn’t.
7
Re-use of functions The functions I develop in this tutorial are re-used from
that moment on. This improves to readability of the code and decreases the
number of lines.
Tested to compile All functions in this tutorial are tested to compile using
Travis CI in both debug and release mode.
Tested to work All functions in this tutorial are tested, using the Boost.Test
library. Travis CI calls these tests after each push to the repository.
Availability The code, as well as this tutorial, can be downloaded from the
GitHub at www.github.com/richelbilderbeek/BoostGraphTutorial.
1.4 License
This tutorial is licensed under Creative Commons license 4.0. All C++ code is
licensed under GPL 3.0.
1.5 Feedback
This tutorial is not intended to be perfect yet. For that, I need help and feed-
back from the community. All referenced feedback is welcome, as well as any
constructive feedback.
I have tried hard to strictly follow the style as described above. If you find I
deviated from these decisions somewhere, I would be grateful if you’d let know.
Next to this, there are some sections that need to be coded or have its code
improved.
1.6 Acknowledgements
These are users that improved this tutorial and/or the code behind this tutorial,
in chronological order:
• m-dudley, http://stackoverflow.com/users/111327/m-dudley
• E. Kawashima
• mat69, https://www.reddit.com/user/mat69
• danielhj, https://www.reddit.com/user/danieljh
• sehe, http://stackoverflow.com/users/85371/sehe
8
• cv_and_me, http://stackoverflow.com/users/2417774/cv-and-he
• mywtfmp3
1.7 Outline
The chapters of this tutorial are also like a well-connected graph. To allow for
quicker learners to skim chapters, or for beginners looking to find the patterns.
The distinction between the chapter is in the type of edges and vertices.
They can have:
d
figure 2.
There are also some bonus chapters, that I have labeled with a ’ ’. These
chapters are added I needed these functions myself and adding them would not
hurt. Just feel free to skip them, as there will be less theory explained.
9
10
Figure 2: The relations between sub-chapters
A B C
In this chapter, we will build two directed and two undirected graphs:
• An empty (directed) graph, which is the default type: see chapter 2.1
• An empty (undirected) graph: see chapter 2.2
• A two-state Markov chain, a directed graph with two vertices and four
edges, chapter 2.14
• K2 , an undirected graph with two vertices and one edge, chapter 2.15
Creating an empty graph may sound trivial, it is not, thanks to the versatility
of the Boost.Graph library.
In the process of creating graphs, some basic (sometimes bordering trivial)
functions are encountered:
• Counting the number of vertices: see chapter 2.3
• Counting the number of edges: see chapter 2.4
• Adding a vertex: see chapter 2.5
• Getting all vertices: see chapter 2.7
• Getting all vertex descriptors: see chapter 2.8
• Adding an edge: see chapter 2.9
• Getting all edges: see chapter 2.11
• Getting all edge descriptors: see chapter 2.13
These functions are mostly there for completion and showing which data types
are used.
The chapter also introduces some important concepts:
11
• Vertex descriptors: see chapter 2.6
• Edge insertion result: see chapter 2.10
• Edge descriptors: see chapter 2.12
After this chapter you may want to:
b o o s t : : a d j a c e n c y _ l i s t <>
create_empty_directed_graph ( ) n o e x c e p t
{
return { } ;
}
The code consists out of an #include and a function definition. The #in-
clude tells the compiler to read the header file ‘adjacency_list.hpp’. A header
file (often with a ‘.h’ or ‘.hpp’ extension) contains class and functions decla-
rations and/or definitions. The header file ‘adjacency_list.hpp’ contains the
boost::adjacency_list class definition. Without including this file, you will get
compile errors like ‘definition of boost::adjacency_list unknown’2 . The function
‘create_empty_directed_graph’ has:
• a return type: The return type is ‘boost::adjacency_list<>’, that is a
‘boost::adjacency_list’ with all template arguments set at their defaults
12
• a function body: all the function body does is implicitly create its re-
turn type by using the ‘{}’. An alternative syntax would be ‘return
boost::adjacency_list<>()’, which is needlessly longer
Algorithm 2 demonstrates the ‘create_empty_directed_graph’ function. This
demonstration is embedded within a Boost.Test unit test case. It includes a
Boost.Test header to allow to use the Boost.Test framework. Additionally, a
header file is included with the same name as the function4 . This allows use to
be able to use the function. The test case creates an empty graph and stores it.
Instead of specifying the data type explicitly, ‘auto’ is used (this is preferred,
[10] chapter 31.6), which lets the compiler figure out the type itself.
BOOST_AUTO_TEST_CASE( test_create_empty_directed_graph )
{
const auto g = create_empty_directed_graph ( ) ;
}
13
2.2 Creating an empty undirected graph
Let’s create another empty graph! This time, we even make it undirected!
Algorithm 3 shows how to create an undirected graph.
BOOST_AUTO_TEST_CASE( test_create_empty_undirected_graph )
{
const auto g = create_empty_undirected_graph ( ) ;
}
14
• The out edges and vertices are stored in a std::vector
• The graph is undirected
• Vertices, edges and graph have no properties
• Edges are stored in a std::list
15
Algorithm 6 Demonstration of the ‘get_n_vertices’ function
BOOST_AUTO_TEST_CASE( t e s t _ g e t _ n _ v e r t i c e s )
{
const auto g = create_empty_directed_graph ( ) ;
BOOST_CHECK( g e t _ n _ v e r t i c e s ( g ) == 0 ) ;
Note that the type of graph does not matter here. One can count the number
of vertices of every graph, as all graphs have vertices. Boost.Graph is very good
at detecting operations that are not allowed, during compile time.
16
which also returns an unsigned long.
The function ‘get_n_edges’ is demonstrated in algorithm 8, to measure the
number of edges of an empty directed and undirected graph.
BOOST_AUTO_TEST_CASE( test_get_n_edges )
{
const auto g = create_empty_directed_graph ( ) ;
BOOST_CHECK( get_n_edges ( g ) == 0 ) ;
The static_assert at the top of the function checks during compiling if the
function is called with a non-const graph. One can freely omit this static_assert:
you will get a compiler error anyways, be it a less helpful one.
17
Note that boost::add_vertex (in the ‘add_vertex’ function) returns a vertex
descriptor, which is ignored for now. Vertex descriptors are looked at in more
details at the chapter 2.6, as we need these to add an edge. To allow for this
already, ‘add_vertex’ also returns a vertex descriptor.
Algorithm 10 shows how to add a vertex to a directed and undirected graph.
BOOST_AUTO_TEST_CASE( test_add_vertex )
{
auto g = create_empty_undirected_graph ( ) ;
add_vertex ( g ) ;
BOOST_CHECK( b o o s t : : num_vertices ( g ) == 1 ) ;
auto h = create_empty_directed_graph ( ) ;
add_vertex ( h ) ;
BOOST_CHECK( b o o s t : : num_vertices ( h ) == 1 ) ;
}
This demonstration code creates two empty graphs, adds one vertex to each
and then asserts that the number of vertices in each graph is one. This works
for both types of graphs, as all graphs have vertices.
18
2.7 Get the vertex iterators
You cannot get the vertices. This may sound unexpected, as it must be possible
to work on the vertices of a graph. Working on the vertices of a graph is done
through these steps:
• Obtain a vertex iterator pair from the graph
• Dereferencing a vertex iterator to obtain a vertex descriptor
19
Algorithm 12 Demonstration of ‘get_vertex_iterators’
BOOST_AUTO_TEST_CASE( t e s t _ g e t _ v e r t e x _ i t e r a t o r s )
{
const auto g = create_empty_undirected_graph ( ) ;
const auto vip_g = g e t _ v e r t e x _ i t e r a t o r s ( g ) ;
BOOST_CHECK( vip_g . f i r s t == vip_g . s e c o n d ) ;
20
This is the first more complex piece of code. In the first lines, some ‘using’
statements allow for shorter type names6 .
The std::vector to serve as a return value is created at the needed size, which
is the number of vertices.
The function ‘vertices’ (not boost::vertices!) returns a vertex iterator pair.
These iterators are used by std::copy to iterator over. std::copy is an STL
algorithm to copy a half-open range. Prefer algorithm calls over hand-written
for-loops ([9] chapter 18.12.1, [7] item 43).
In this case, we copy all vertex descriptors in the range produced by ‘vertices’
to the std::vector.
This function will not be used in practice: one iterates over the vertices
directly instead, saving the cost of creating a std::vector. This function is only
shown as an illustration.
Algorithm 14 demonstrates that an empty graph has no vertex descriptors:
BOOST_AUTO_TEST_CASE( t e s t _ g e t _ v e r t e x _ d e s c r i p t o r s )
{
const auto g = create_empty_undirected_graph ( ) ;
const auto vds_g = g e t _ v e r t e x _ d e s c r i p t o r s ( g ) ;
BOOST_CHECK( vds_g . empty ( ) ) ;
Because all graphs have vertices and thus vertex descriptors, the type of
graph is unimportant for this code to compile.
21
Algorithm 15 Adding (two vertices and) an edge to a graph
22
Algorithm 16 Demonstration of ‘add_edge’
BOOST_AUTO_TEST_CASE( test_add_edge )
{
auto g = create_empty_undirected_graph ( ) ;
add_edge ( g ) ;
BOOST_CHECK( b o o s t : : num_vertices ( g ) == 2 ) ;
BOOST_CHECK( b o o s t : : num_edges ( g ) == 1 ) ;
auto h = create_empty_directed_graph ( ) ;
add_edge ( h ) ;
BOOST_CHECK( b o o s t : : num_vertices ( h ) == 2 ) ;
BOOST_CHECK( b o o s t : : num_edges ( h ) == 1 ) ;
}
The graph type is unimportant: as all graph types have vertices and edges,
edges can be added without possible compile problems.
‘edges’ (not boost::edges!) is used to obtain an edge iterator pair. The first edge
iterator points to the first edge (its descriptor, to be precise), the second points
to beyond the last edge (its descriptor, to be precise). In this tutorial, edge
iterator pairs have named prefixed with ‘eip_’, for example ‘eip_1’. Algorithm
17 shows how to obtain these:
23
Algorithm 17 Get the edge iterators of a graph
BOOST_AUTO_TEST_CASE( t e s t _ g e t _ e d g e _ i t e r a t o r s )
{
const auto g = create_empty_undirected_graph ( ) ;
const auto eip_g = g e t _ e d g e _ i t e r a t o r s ( g ) ;
BOOST_CHECK( eip_g . f i r s t == eip_g . s e c o n d ) ;
auto h = create_empty_directed_graph ( ) ;
const auto eip_h = g e t _ e d g e _ i t e r a t o r s ( h ) ;
BOOST_CHECK( eip_h . f i r s t == eip_h . s e c o n d ) ;
}
24
Edge descriptors are used to obtain the name, or other properties, of an edge
In this tutorial, edge descriptors have named prefixed with ‘ed_’, for example
‘ed_1’.
The only difference is that instead of the function ‘vertices’ (not boost::vertices!),
‘edges’ (not boost::edges!) is used.
Algorithm 20 demonstrates the ‘get_edge_descriptor’, by showing that empty
graphs do not have any edge descriptors.
25
Algorithm 20 Demonstration of get_edge_descriptors
BOOST_AUTO_TEST_CASE( t e s t _ g e t _ e d g e _ d e s c r i p t o r s )
{
const auto g = create_empty_directed_graph ( ) ;
const auto eds_g = g e t _ e d g e _ d e s c r i p t o r s ( g ) ;
BOOST_CHECK( eds_g . empty ( ) ) ;
2.14.1 Graph
This directed graph is a two-state Markov chain, with two vertices and four
edges, as depicted in figure 5:
A B
Note that directed graphs can have edges that start and end in the same
vertex. These are called self-loops.
26
2.14.2 Function to create such a graph
To create this two-state Markov chain, the following code can be used:
b o o s t : : a d j a c e n c y _ l i s t <>
create_markov_chain ( ) n o e x c e p t
{
auto g = create_empty_directed_graph ( ) ;
const auto vd_a = b o o s t : : add_vertex ( g ) ;
const auto vd_b = b o o s t : : add_vertex ( g ) ;
b o o s t : : add_edge ( vd_a , vd_a , g ) ;
b o o s t : : add_edge ( vd_a , vd_b , g ) ;
b o o s t : : add_edge ( vd_b , vd_a , g ) ;
b o o s t : : add_edge ( vd_b , vd_b , g ) ;
return g ;
}
27
Algorithm 22 Demonstration of the ‘create_markov_chain’
BOOST_AUTO_TEST_CASE( test_create_markov_chain )
{
const auto g = create_markov_chain ( ) ;
BOOST_CHECK( b o o s t : : num_vertices ( g ) == 2 ) ;
BOOST_CHECK( b o o s t : : num_edges ( g ) == 4 ) ;
}
From the .dot file one can already see that the graph is directed, because:
• The first word, ‘digraph’, denotes a directed graph (where ‘graph’ would
have indicated an undirected graph)
28
Figure 6: .svg file created from the ‘create_markov_chain’ function (algorithm
21) its .dot file and converted from .dot file to .svg using algorithm 136
This figure shows that the graph in directed, as the edges have arrow heads.
The vertices display the node index, which is the default behavior.
2.15.1 Graph
To create a fully connected undirected graph with two vertices (also called K2 ),
one needs two vertices and one (undirected) edge, as depicted in figure 7.
29
Algorithm 24 Creating K2 as depicted in figure 7
This code is very similar to the ‘add_edge’ function (algorithm 15). Instead
of typing the graph its type, we call the ‘create_empty_undirected_graph’
function and let auto figure it out. The vertex descriptors (see chapter 2.6)
created by two boost::add_vertex calls are stored to add an edge to the graph.
From boost::add_edge its return type (see chapter 2.10), it is only checked that
insertion has been successful.
Note that the graph lacks all properties: nodes do not have names, nor do
edges.
BOOST_AUTO_TEST_CASE( test_create_k2_graph )
{
const auto g = create_k2_graph ( ) ;
BOOST_CHECK( b o o s t : : num_vertices ( g ) == 2 ) ;
BOOST_CHECK( b o o s t : : num_edges ( g ) == 1 ) ;
}
30
2.15.4 The .dot file produced
Running a bit ahead, this graph can be converted to the .dot file as shown in
algorithm 26:
From the .dot file one can already see that the graph is undirected, because:
• The first word, ‘graph’, denotes an undirected graph (where ‘digraph’
would have indicated a directional graph)
• The edge between 0 and 1 is written as ‘–’ (where directed connections
would be written as ‘->’, ‘<-’ or ‘<>’)
Figure 8: .svg file created from the ‘create_k2_graph’ function (algorithm 24)
its .dot file, converted from .dot file to .svg using algorithm 136
Also this figure shows that the graph in undirected, otherwise the edge would
have one or two arrow heads. The vertices display the node index, which is the
default behavior.
31
2.16 d Creating K , a fully connected undirected graph
3
with three vertices
This is an extension of the previous chapter
2.16.1 Graph
To create a fully connected undirected graph with two vertices (also called K2 ),
one needs two vertices and one (undirected) edge, as depicted in figure 9.
32
Algorithm 27 Creating K3 as depicted in figure 9
BOOST_AUTO_TEST_CASE( test_create_k3_graph )
{
const auto g = create_k3_graph ( ) ;
BOOST_CHECK( b o o s t : : num_edges ( g ) == 3 ) ;
BOOST_CHECK( b o o s t : : num_vertices ( g ) == 3 ) ;
}
33
Algorithm 29 .dot file created from the ‘create_k3_graph’ function (algorithm
27), converted from graph to .dot file using algorithm 55
graph G {
0;
1;
2;
0--1 ;
1--2 ;
2--0 ;
}
Figure 10: .svg file created from the ‘create_k3_graph’ function (algorithm 27)
its .dot file, converted from .dot file to .svg using algorithm 136
34
2.17.1 Graph
Here I show a path graph with four vertices (see figure 11):
A B C D
35
Algorithm 31 Demonstration of ‘create_path_graph’
BOOST_AUTO_TEST_CASE( test_create_path_graph )
{
const auto g = create_path_graph ( 4 ) ;
BOOST_CHECK( b o o s t : : num_edges ( g ) == 3 ) ;
BOOST_CHECK( b o o s t : : num_vertices ( g ) == 4 ) ;
}
36
Figure 12: .svg file created from the ‘create_path_graph’ function (algorithm
30) its .dot file, converted from .dot file to .svg using algorithm 136
2.18.1 Graph
To create a Petersen graph, one needs five vertices and five undirected edges, as
depicted in figure 13.
37
Figure 13: A Petersen graph (from https://en.wikipedia.org/wiki/
Petersen_graph)
38
Algorithm 33 Creating Petersen graph as depicted in figure 13
auto g = create_empty_undirected_graph ( ) ;
s t d : : v e c t o r <vd> v ; // Outer
f o r ( int i =0; i ! = 5 ; ++i ) {
v . push_back ( b o o s t : : add_vertex ( g ) ) ;
}
s t d : : v e c t o r <vd> w ; // I n n e r
f o r ( int i =0; i ! = 5 ; ++i ) {
w . push_back ( b o o s t : : add_vertex ( g ) ) ;
}
// Outer r i n g
f o r ( int i =0; i ! = 5 ; ++i ) {
b o o s t : : add_edge ( v [ i ] , v [ ( i + 1 ) % 5 ] , g ) ;
}
// Spoke
f o r ( int i =0; i ! = 5 ; ++i ) {
b o o s t : : add_edge ( v [ i ] , w [ i ] , g ) ;
}
// I n n e r pentagram
f o r ( int i =0; i ! = 5 ; ++i ) {
b o o s t : : add_edge (w [ i ] , w [ ( i + 2 ) % 5 ] , g ) ;
}
return g ;
}
39
Algorithm 34 Demonstration of ‘create_k3_graph’
BOOST_AUTO_TEST_CASE( t e s t _ c r e a t e _ p e t e r s e n _ g r a p h )
{
const auto g = c r e a t e _ p e t e r s e n _ g r a p h ( ) ;
BOOST_CHECK( b o o s t : : num_edges ( g ) == 1 5 ) ;
BOOST_CHECK( b o o s t : : num_vertices ( g ) == 1 0 ) ;
}
40
Algorithm 35 .dot file created from the ‘create_petersen_graph’ function (al-
gorithm 33), converted from graph to .dot file using algorithm 55
graph G {
0;
1;
2;
3;
4;
5;
6;
7;
8;
9;
0--1 ;
1--2 ;
2--3 ;
3--4 ;
4--0 ;
0--5 ;
1--6 ;
2--7 ;
3--8 ;
4--9 ;
5--7 ;
6--8 ;
7--9 ;
8--5 ;
9--6 ;
}
41
Figure 14: .svg file created from the ‘create_petersen_graph’ function (algo-
rithm 33) its .dot file, converted from .dot file to .svg using algorithm 136
42
• Create a direct-neighbour subgraph from a vertex descriptor
• Create all direct-neighbour subgraphs from a graphs
• Saving a graph without properties to .dot file: see chapter 3.11
• Loading an undirected graph without properties from .dot file: see chapter
3.13
• Loading a directed graph without properties from .dot file: see chapter
3.12
• degree: sum of the in degree and out degree, using ‘degree’ (not ‘boost::degree’)
Algorithm 36 shows how to obtain these:
43
Algorithm 36 Get the vertices’ out degrees
s t d : : v e c t o r <int> v ( b o o s t : : num_vertices ( g ) ) ;
const auto v i p = v e r t i c e s ( g ) ;
s t d : : t r a n s f o r m ( v i p . f i r s t , v i p . second , s t d : : b e g i n ( v ) ,
[&g ] ( const vd& d ) {
return out_degree ( d , g ) ;
}
);
return v ;
}
44
Algorithm 37 Demonstration of the ‘get_vertex_out_degrees’ function
BOOST_AUTO_TEST_CASE( te s t _ ge t _ ve r t e x_ o u t_ d e g re e s )
{
const auto g = create_k2_graph ( ) ;
const s t d : : v e c t o r <int> expected_out_degrees_g { 1 , 1 } ;
const s t d : : v e c t o r <int> vertex_out_degrees_g {
get_vertex_out_degrees ( g )
};
BOOST_CHECK( expected_out_degrees_g
== vertex_out_degrees_g
);
It is expected that K2 has one out-degree for every vertex, where the two-
state Markov chain is expected to have two out-degrees per vertex.
45
Algorithm 38 Check if there exists an edge between two vertices
This code uses the function ‘edge’ (not boost::edge: it returns a pair consist-
ing of an edge descriptor and a boolean indicating if it is a valid edge descriptor.
The boolean will be true if there exists an edge between the two vertices and
false if not.
The demo shows that there is an edge between the two vertices of a K2 graph,
but there are no self-loops (edges that original and end at the same vertex).
BOOST_AUTO_TEST_CASE( test_has_edge_between_vertices )
{
const auto g = create_k2_graph ( ) ;
const auto vd_1 = ∗ v e r t i c e s ( g ) . f i r s t ;
const auto vd_2 = ∗(++ v e r t i c e s ( g ) . f i r s t ) ;
BOOST_CHECK( has_edge_between_vertices ( vd_1 , vd_2 , g ) ) ;
BOOST_CHECK( ! has_edge_between_vertices ( vd_1 , vd_1 , g ) ) ;
}
46
Algorithm 40 Get the edge between two vertices
This code does assume that there is an edge between the two vertices.
The demo shows how to get the edge between two vertices, deleting it, and
checking for success.
47
Algorithm 41 Demonstration of the ‘get_edge_between_vertices’ function
BOOST_AUTO_TEST_CASE( t e s t _ g e t _ e d g e _ b e t w e e n _ v e r t i c e s )
{
auto g = create_k2_graph ( ) ;
const auto vd_1 = ∗ v e r t i c e s ( g ) . f i r s t ;
const auto vd_2 = ∗(++ v e r t i c e s ( g ) . f i r s t ) ;
BOOST_CHECK( has_edge_between_vertices ( vd_1 , vd_2 , g ) ) ;
const auto ed = get_edge_between_vertices ( vd_1 , vd_2 , g
);
b o o s t : : remove_edge ( ed , g ) ;
BOOST_CHECK( b o o s t : : num_edges ( g ) == 0 ) ;
}
48
Algorithm 42 Get the direct-neighbour subgraph from a vertex descriptor
#include <map>
#include <b o o s t / graph / a d j a c e n c y _ l i s t . hpp>
s t d : : map<v e r t e x _ d e s c r i p t o r , v e r t e x _ d e s c r i p t o r > m;
{
const auto vd_h = b o o s t : : add_vertex ( h ) ;
m. i n s e r t ( s t d : : make_pair ( vd , vd_h ) ) ;
}
//Copy v e r t i c e s
{
const auto v d s i = b o o s t : : a d j a c e n t _ v e r t i c e s ( vd , g ) ;
f o r ( auto i = v d s i . f i r s t ; i != v d s i . s e c o n d ; ++i )
{
i f (m. f i n d ( ∗ i ) == m. end ( ) )
{
const auto vd_h = b o o s t : : add_vertex ( h ) ;
m. i n s e r t ( s t d : : make_pair ( ∗ i , vd_h ) ) ;
}
}
}
//Copy e d g e s
{
const auto e i p = e d g e s ( g ) ;
const auto j = e i p . s e c o n d ;
f o r ( auto i = e i p . f i r s t ; i != j ; ++i )
{
const auto vd_from = s o u r c e ( ∗ i , g ) ;
const auto vd_to = t a r g e t ( ∗ i , g ) ;
i f (m. f i n d ( vd_from ) == s t d : : end (m) ) continue ;
i f (m. f i n d ( vd_to ) == s t d : : end (m) ) continue ;
b o o s t : : add_edge (m[ vd_from ] ,m[ vd_to ] , h ) ;
}
}
return h ;
}
49
This demonstration code shows that the direct-neighbour graph of each ver-
tex of a K2 graphs is ... a K2 graph!
BOOST_AUTO_TEST_CASE(
test_create_direct_neighbour_subgraph )
{
const auto g = create_k2_graph ( ) ;
const auto v i p = v e r t i c e s ( g ) ;
const auto j = v i p . s e c o n d ;
f o r ( auto i=v i p . f i r s t ; i != j ; ++i ) {
const auto h = c r e a t e _ d i r e c t _ n e i g h b o u r _ s u b g r a p h (
∗i , g
);
BOOST_CHECK( b o o s t : : num_vertices ( h ) == 2 ) ;
BOOST_CHECK( b o o s t : : num_edges ( h ) == 1 ) ;
}
}
Note that this algorithm works on both undirected and directional graphs.
If the graph is directional, only the out edges will be copied. To also copy the
vertices connected with inward edges, use 3.5
50
Algorithm 44 Get the direct-neighbour subgraph from a vertex descriptor
v e r t e x _ d e s c r i p t o r vd_h = 0 ;
m. i n s e r t ( s t d : : make_pair ( vd , vd_h++)) ;
f o r ( const e d g e _ d e s c r i p t o r ed : b o o s t : :
make_iterator_range ( e d g e s ( g ) ) ) {
const auto vd_from = s o u r c e ( ed , g ) ;
const auto vd_to = t a r g e t ( ed , g ) ;
i f ( vd == vd_from ) {
conn_edges . emplace_back ( vd_from , vd_to ) ;
m. i n s e r t ( s t d : : make_pair ( vd_to , vd_h++)) ;
}
i f ( vd == vd_to ) {
conn_edges . emplace_back ( vd_from , vd_to ) ;
m. i n s e r t ( s t d : : make_pair ( vd_from , vd_h++)) ;
}
}
f o r ( v p a i r& vp : conn_edges ) {
vp . f i r s t = m[ vp . f i r s t ] ;
vp . s e c o n d = m[ vp . s e c o n d ] ;
}
s t d : : v e c t o r <graph> v ( b o o s t : : num_vertices ( g ) ) ;
const auto v i p = v e r t i c e s ( g ) ;
std : : transform (
v i p . f i r s t , v i p . second ,
std : : begin (v) ,
[&g ] ( const vd& d )
{
return c r e a t e _ d i r e c t _ n e i g h b o u r _ s u b g r a p h (
d, g
);
}
);
return v ;
}
52
Algorithm 46 Demo of the ‘create_all_direct_neighbour_subgraphs’ func-
tion
#include <b o o s t / t e s t / u n i t _ t e s t . hpp>
#include " c r e a t e _ a l l _ d i r e c t _ n e i g h b o u r _ s u b g r a p h s . h"
#include " create_k2_graph . h"
BOOST_AUTO_TEST_CASE(
test_create_all_direct_neighbour_subgraphs )
{
const auto v
= create_all_direct_neighbour_subgraphs (
create_k2_graph ( ) ) ;
BOOST_CHECK( v . s i z e ( ) == 2 ) ;
f o r ( const auto g : v )
{
BOOST_CHECK( b o o s t : : num_vertices ( g ) == 2 ) ;
BOOST_CHECK( b o o s t : : num_edges ( g ) == 1 ) ;
}
}
53
Algorithm 48 Demo of the ‘is_isomorphic’ function
BOOST_AUTO_TEST_CASE( t e s t _ i s _ i s o m o r p h i c )
{
const auto g = create_path_graph ( 3 ) ;
const auto h = create_k3_graph ( ) ;
BOOST_CHECK( i s _ i s o m o r p h i c ( g , g ) ) ;
BOOST_CHECK( ! i s _ i s o m o r p h i c ( g , h ) ) ;
}
54
Algorithm 49 Count the number of connected components
55
Algorithm 50 Demo of the ‘count_directed_graph_connected_components’
function
#include <b o o s t / t e s t / u n i t _ t e s t . hpp>
#include " create_empty_directed_graph . h"
#include " add_edge . h"
#include " count_directed_graph_connected_components . h"
BOOST_AUTO_TEST_CASE(
test_count_directed_graph_connected_components )
{
auto g = create_empty_directed_graph ( ) ;
BOOST_CHECK( count_directed_graph_connected_components ( g
) == 0 ) ;
const auto vd_a = b o o s t : : add_vertex ( g ) ;
const auto vd_b = b o o s t : : add_vertex ( g ) ;
const auto vd_c = b o o s t : : add_vertex ( g ) ;
b o o s t : : add_edge ( vd_a , vd_b , g ) ;
b o o s t : : add_edge ( vd_b , vd_c , g ) ;
b o o s t : : add_edge ( vd_c , vd_a , g ) ;
BOOST_CHECK( count_directed_graph_connected_components ( g
) == 1 ) ;
const auto vd_d = b o o s t : : add_vertex ( g ) ;
const auto vd_e = b o o s t : : add_vertex ( g ) ;
const auto vd_f = b o o s t : : add_vertex ( g ) ;
b o o s t : : add_edge ( vd_d , vd_e , g ) ;
b o o s t : : add_edge ( vd_e , vd_f , g ) ;
b o o s t : : add_edge ( vd_f , vd_d , g ) ;
BOOST_CHECK( count_directed_graph_connected_components ( g
) == 2 ) ;
}
56
Figure 16: Example of an undirected graph with two components
57
Algorithm 52 Demo of the ‘count_undirected_graph_connected_components’
function
#include <b o o s t / t e s t / u n i t _ t e s t . hpp>
#include " create_empty_undirected_graph . h"
#include " add_edge . h"
#include " count_undirected_graph_connected_components . h"
BOOST_AUTO_TEST_CASE(
test_count_undirected_graph_connected_components )
{
auto g = create_empty_undirected_graph ( ) ;
BOOST_CHECK( count_undirected_graph_connected_components
( g ) == 0 ) ;
add_edge ( g ) ;
BOOST_CHECK( count_undirected_graph_connected_components
( g ) == 1 ) ;
add_edge ( g ) ;
BOOST_CHECK( count_undirected_graph_connected_components
( g ) == 2 ) ;
}
58
Algorithm 53 Count the number of levels in an undirected graph
// C o l l e c t a l l n e i g h b o u r s
// I f t h e r e a r e no new n e i g h b o u r s , t h e l e v e l i s found
while ( 1 )
{
//How many nodes a r e known now
const auto s z _ b e f o r e = s . s i z e ( ) ;
const auto t = s ;
f o r ( const auto v : t )
{
const auto n e i g h b o u r s = b o o s t : : a d j a c e n t _ v e r t i c e s ( v ,
g) ;
f o r ( auto n = n e i g h b o u r s . f i r s t ; n != n e i g h b o u r s .
s e c o n d ; ++n )
{
s . i n s e r t (∗n) ;
}
}
BOOST_AUTO_TEST_CASE( t e s t _ c o u n t _ u n d i r e c t e d _ g r a p h _ l e v e l s )
{
auto g = create_empty_undirected_graph ( ) ;
const auto vd_a = b o o s t : : add_vertex ( g ) ;
const auto vd_b = b o o s t : : add_vertex ( g ) ;
const auto vd_c = b o o s t : : add_vertex ( g ) ;
const auto vd_d = b o o s t : : add_vertex ( g ) ;
BOOST_CHECK( c o u n t _ u n d i r e c t e d _ g r a p h _ l e v e l s ( vd_a , g ) ==
0) ;
b o o s t : : add_edge ( vd_a , vd_b , g ) ;
BOOST_CHECK( c o u n t _ u n d i r e c t e d _ g r a p h _ l e v e l s ( vd_a , g ) ==
1) ;
b o o s t : : add_edge ( vd_b , vd_c , g ) ;
BOOST_CHECK( c o u n t _ u n d i r e c t e d _ g r a p h _ l e v e l s ( vd_a , g ) ==
2) ;
b o o s t : : add_edge ( vd_c , vd_d , g ) ;
BOOST_CHECK( c o u n t _ u n d i r e c t e d _ g r a p h _ l e v e l s ( vd_a , g ) ==
3) ;
}
60
Algorithm 55 Saving a graph to a .dot file
All the code does is create an std::ofstream (an output-to-file stream) and
use boost::write_graphviz to write the DOT description of our graph to that
stream. Instead of ‘std::ofstream’, one could use std::cout (a related output
stream) to display the DOT language on screen directly.
Algorithm 56 shows how to use the ‘save_graph_to_dot’ function:
BOOST_AUTO_TEST_CASE( test_save_graph_to_dot )
{
const auto g = create_k2_graph ( ) ;
save_graph_to_dot ( g , " create_k2_graph . dot " ) ;
61
Algorithm 57 Loading a directed graph from a .dot file
b o o s t : : a d j a c e n c y _ l i s t <>
load_directed_graph_from_dot (
const s t d : : s t r i n g& d o t _ f i l e n a m e
)
{
i f ( ! i s _ r e g u l a r _ f i l e ( dot_filename ) )
{
s t d : : s t r i n g s t r e a m msg ;
msg << __func__ << " : ␣ f i l e ␣ ’ "
<< d o t _ f i l e n a m e << " ’ ␣ not ␣ found "
;
throw s t d : : invalid_argument ( msg . s t r ( ) ) ;
}
std : : i f s t r e a m f ( dot_filename . c_str ( ) ) ;
auto g = create_empty_directed_graph ( ) ;
b o o s t : : d y n a m i c _ p r o p e r t i e s dp (
boost : : ignore_other_properties
);
b o o s t : : read_graphviz ( f , g , dp ) ;
return g ;
}
In this algorithm, first it is checked if the file to load exists, using the
‘is_regular_file’ function (algorithm 137), after which an std::ifstream is opened.
Then an empty directed graph is created, which saves us writing down the tem-
plate arguments explicitly. Then, a boost::dynamic_properties is created with
the ‘boost::ignore_other_properties’ in its constructor (using a default con-
structor here results in the run-time error ‘property not found: node_id’, see
chapter 12.5). From this and the empty graph, ‘boost::read_graphviz’ is called
to build up the graph.
Algorithm 58 shows how to use the ‘load_directed_graph_from_dot’ func-
tion:
62
Algorithm 58 Demonstration of the ‘load_directed_graph_from_dot’ func-
tion
#include <b o o s t / t e s t / u n i t _ t e s t . hpp>
#include " create_markov_chain . h"
#include " load_directed_graph_from_dot . h"
#include " save_graph_to_dot . h"
BOOST_AUTO_TEST_CASE( test_load_directed_graph_from_dot )
{
using b o o s t : : num_edges ;
using b o o s t : : num_vertices ;
This demonstration shows how the Markov chain is created using the ‘cre-
ate_markov_chain_graph’ function (algorithm 21), saved and then loaded.
The loaded graph is then checked to be a two-state Markov chain.
63
Algorithm 59 Loading an undirected graph from a .dot file
The only difference with loading a directed graph, is that the initial empty
graph is undirected instead. Chapter 3.12 describes the rationale of this func-
tion.
Algorithm 60 shows how to use the ‘load_undirected_graph_from_dot’
function:
64
Algorithm 60 Demonstration of the ‘load_undirected_graph_from_dot’
function
#include <b o o s t / t e s t / u n i t _ t e s t . hpp>
#include " create_k2_graph . h"
#include " load_undirected_graph_from_dot . h"
#include " save_graph_to_dot . h"
BOOST_AUTO_TEST_CASE( test_load_undirected_graph_from_dot )
{
using b o o s t : : num_edges ;
using b o o s t : : num_vertices ;
This demonstration shows how the K2 graph is created using the ‘cre-
ate_k2_graph’ function (algorithm 24), saved and then loaded. The loaded
graph is checked to be a K2 graph.
65
• Getting the vertices ‘my_bundled_vertex’-es: see chapter 4.5
These functions are mostly there for completion and showing which data types
are used.
struct my_bundled_vertex
{
e x p l i c i t my_bundled_vertex (
const s t d : : s t r i n g& name = " " ,
const s t d : : s t r i n g& d e s c r i p t i o n = " " ,
const double x = 0 . 0 ,
const double y = 0 . 0
) noexcept ;
s t d : : s t r i n g m_name ;
std : : s t r i n g m_description ;
double m_x;
double m_y;
};
66
• It is copyable
• It is comparable for equality (it has operator==), which is needed for
searching
‘my_bundled_vertex’ does not have to have the stream operators defined for
file I/O, as this goes via the public member variables.
This graph:
• has its out edges stored in a std::vector (due to the first boost::vecS)
67
4.3 Create the empty undirected graph with bundled ver-
tices
This code is very similar to the code described in chapter 4.2, except that the di-
rectness (the third template argument) is undirected (due to the boost::undirectedS).
When having added a new (abstract) vertex to the graph, the vertex de-
scriptor is used to set the ‘my_bundled_vertex’ in the graph.
68
4.5 Getting the bundled vertices’ my_vertexes8
When the vertices of a graph have any bundled ‘my_bundled_vertex’, one can
extract these as such:
s t d : : v e c t o r <my_bundled_vertex> v ( b o o s t : : num_vertices ( g )
);
const auto v i p = v e r t i c e s ( g ) ;
s t d : : t r a n s f o r m ( v i p . f i r s t , v i p . second , s t d : : b e g i n ( v ) ,
[&g ] ( const vd& d ) { return g [ d ] ; }
);
return v ;
}
my_vertex
69
Sunny, Yellow, 1.0, 2.0 Not rainy, Not grey, 3.0, 4.0
Figure 18: A two-state Markov chain where the vertices have bundled properties
and the edges have no properties. The vertices’ properties are nonsensical
70
Algorithm 66 Creating the two-state Markov chain as depicted in figure 18
71
Algorithm 67 Demo of the ‘create_bundled_vertices_markov_chain’ func-
tion (algorithm 66)
BOOST_AUTO_TEST_CASE(
test_create_bundled_vertices_markov_chain )
{
const auto g
= create_bundled_vertices_markov_chain ( ) ;
const s t d : : v e c t o r <my_bundled_vertex> e x p e c t e d {
my_bundled_vertex ( "Sunny" , " Yellow " , 1 . 0 , 2 . 0 ) ,
my_bundled_vertex ( "Not␣ r a i n y " , "Not␣ g r e y " , 3 . 0 , 4 . 0 )
};
const auto found = get_my_bundled_vertexes ( g ) ;
BOOST_CHECK( e x p e c t e d == found ) ;
}
72
73
4.6.5 The .svg file produced
74
4.7 Creating K2 with bundled vertices
4.7.1 Graph
We reproduce the K2 with named vertices of chapter ?? , but with our bundled
vertices instead, as show in figure 20:
75
4.7.2 Function to create such a graph
const my_bundled_vertex a (
"Me" , " M y s e l f " , 1 . 0 , 2 . 0
);
const my_bundled_vertex b (
"My␣ computer " , "Not␣me" , 3 . 0 , 4 . 0
);
const auto vd_a = add_bundled_vertex ( a , g ) ;
const auto vd_b = add_bundled_vertex ( b , g ) ;
b o o s t : : add_edge ( vd_a , vd_b , g ) ;
return g ;
}
76
Algorithm 70 Demo of the ‘create_bundled_vertices_k2_graph’ function (al-
gorithm 69)
BOOST_AUTO_TEST_CASE(
test_create_bundled_vertices_k2_graph )
{
const auto g = create_bundled_vertices_k2_graph ( ) ;
BOOST_CHECK( b o o s t : : num_edges ( g ) == 1 ) ;
BOOST_CHECK( b o o s t : : num_vertices ( g ) == 2 ) ;
BOOST_CHECK( has_bundled_vertex_with_my_vertex (
my_bundled_vertex ( "Me" , " M y s e l f " , 1 . 0 , 2 . 0 ) , g )
);
BOOST_CHECK( has_bundled_vertex_with_my_vertex (
my_bundled_vertex ( "My␣ computer " , "Not␣me" , 3 . 0 , 4 . 0 ) , g )
);
}
77
78
4.7.5 The .svg file produced
79
5 Working on graphs with bundled vertices
When using graphs with bundled vertices, their state gives a way to find a vertex
and working with it. This chapter shows some basic operations on graphs with
bundled vertices.
• Check if there exists a vertex with a certain ‘my_bundled_vertex’: chap-
ter 5.1
• Find a vertex with a certain ‘my_bundled_vertex’: chapter 5.2
80
Algorithm 72 Find if there is vertex with a certain my_bundled_vertex
const auto v i p = v e r t i c e s ( g ) ;
return s t d : : f i n d _ i f ( v i p . f i r s t , v i p . second ,
[&v , &g ] ( const vd& d )
{
return g [ d ] == v ;
}
) != v i p . s e c o n d ;
}
81
Algorithm 73 Demonstration of the ‘has_bundled_vertex_with_my_vertex’
function
#include <b o o s t / t e s t / u n i t _ t e s t . hpp>
#include " add_bundled_vertex . h"
#include " create_empty_undirected_bundled_vertices_graph .
h"
#include " has_bundled_vertex_with_my_vertex . h"
#include " my_bundled_vertex . h"
BOOST_AUTO_TEST_CASE(
test_has_bundled_vertex_with_my_vertex )
{
auto g = create_empty_undirected_bundled_vertices_graph
() ;
BOOST_CHECK( ! has_bundled_vertex_with_my_vertex (
my_bundled_vertex ( " F e l i x " ) , g ) ) ;
add_bundled_vertex ( my_bundled_vertex ( " F e l i x " ) , g ) ;
BOOST_CHECK( has_bundled_vertex_with_my_vertex (
my_bundled_vertex ( " F e l i x " ) , g ) ) ;
}
Note that this function only finds if there is at least one bundled vertex with
that my_bundled_vertex: it does not tell how many bundled vertices with that
my_bundled_vertex exist in the graph.
82
Algorithm 74 Find the first vertex with a certain my_bundled_vertex
With the vertex descriptor obtained, one can read and modify the vertex
and the edges surrounding it. Algorithm 75 shows some examples of how to do
so.
83
Algorithm 75 Demonstration of the ‘find_first_bundled_vertex_with_my_vertex’
function
#include <b o o s t / t e s t / u n i t _ t e s t . hpp>
#include " create_bundled_vertices_k2_graph . h"
#include " find_first_bundled_vertex_with_my_vertex . h"
BOOST_AUTO_TEST_CASE(
test_find_first_bundled_vertex_with_my_vertex )
{
const auto g = create_bundled_vertices_k2_graph ( ) ;
const auto vd =
find_first_bundled_vertex_with_my_vertex (
my_bundled_vertex ( "Me" , " M y s e l f " , 1 . 0 , 2 . 0 ) ,
g
);
BOOST_CHECK( out_degree ( vd , g ) == 1 ) ;
BOOST_CHECK( i n _ d e g r e e ( vd , g ) == 1 ) ;
}
Algorithm 76 Get a bundled vertex its my_vertex from its vertex descriptor
One can just use the graph as a property map and let it be looked-up.
To use ‘get_bundled_vertex_my_vertex’, one first needs to obtain a vertex
descriptor. Algorithm 77 shows a simple example.
84
Algorithm 77 Demonstration if the ‘get_bundled_vertex_my_vertex’ func-
tion
#include <b o o s t / t e s t / u n i t _ t e s t . hpp>
#include " add_bundled_vertex . h"
#include " create_empty_undirected_bundled_vertices_graph .
h"
#include " find_first_bundled_vertex_with_my_vertex . h"
#include " get_my_bundled_vertex . h"
BOOST_AUTO_TEST_CASE( test_get_my_bundled_vertex )
{
auto g
= create_empty_undirected_bundled_vertices_graph ( ) ;
const my_bundled_vertex v{ "Dex" } ;
add_bundled_vertex ( v , g ) ;
const auto vd
= find_first_bundled_vertex_with_my_vertex ( v , g ) ;
BOOST_CHECK( get_my_bundled_vertex ( vd , g ) == v ) ;
}
Algorithm 78 Set a bundled vertex its my_vertex from its vertex descriptor
85
To use ‘set_bundled_vertex_my_vertex’, one first needs to obtain a vertex
descriptor. Algorithm 79 shows a simple example.
BOOST_AUTO_TEST_CASE( test_set_my_bundled_vertex )
{
auto g = create_empty_undirected_bundled_vertices_graph
() ;
const my_bundled_vertex old_name{ "Dex" } ;
add_bundled_vertex ( old_name , g ) ;
const auto vd =
find_first_bundled_vertex_with_my_vertex ( old_name , g )
;
BOOST_CHECK( get_my_bundled_vertex ( vd , g ) == old_name ) ;
const my_bundled_vertex new_name{ " Diggy " } ;
set_my_bundled_vertex ( new_name , vd , g ) ;
BOOST_CHECK( get_my_bundled_vertex ( vd , g ) == new_name ) ;
}
86
Algorithm 80 Setting the bundled vertices’ ‘my_bundled_vertex’-es
87
Algorithm 81 Storing a graph with bundled vertices as a .dot file
88
Algorithm 83 The ‘bundled_vertices_writer’ function
#include <ostream>
#include " graphviz_encode . h"
#include " i s _ g r a p h v i z _ f r i e n d l y . h"
template <
typename graph
>
class bundled_vertices_writer {
public :
bundled_vertices_writer (
graph g
) : m_g{ g }
{
}
template <c l a s s v e r t e x _ d e s c r i p t o r >
void operator ( ) (
s t d : : ostream& out ,
const v e r t e x _ d e s c r i p t o r& vd
) const n o e x c e p t {
out
<< " [ l a b e l =\""
<< graphviz_encode (
m_g[ vd ] . m_name
)
<< " \ " , comment=\""
<< graphviz_encode (
m_g[ vd ] . m _ d e s c r i p t i o n
)
<< " \ " , width="
<< m_g[ vd ] . m_x
<< " , h e i g h t="
<< m_g[ vd ] . m_y
<< " ] "
;
}
private :
graph m_g;
};
Here, some interesting things are happening: the writer needs the bundled
property maps to work with and thus copies the whole graph to its internals.
I have chosen to map the ‘my_bundled_vertex’ member variables to Graphviz
89
attributes (see chapter 13.2 for most Graphviz attributes) as shown in table 1:
my_bundled_vertex variable C++ data type Graphviz data type Graphviz attribute
m_name std::string string label
m_description std::string string comment
m_x double double width
m_y double double height
Important in this mapping is that the C++ and the Graphviz data types
match. I also chose attributes that matched as closely as possible.
The writer also encodes the std::string of the name and description to a
Graphviz-friendly format. When loading the .dot file again, this will have to be
undone again.
90
Algorithm 84 Loading a directed graph with bundled vertices from a .dot file
b o o s t : : d y n a m i c _ p r o p e r t i e s dp ( b o o s t : :
ignore_other_properties ) ;
dp . p r o p e r t y ( " l a b e l " , g e t (&my_bundled_vertex : : m_name, g ) )
;
dp . p r o p e r t y ( "comment" , g e t (&my_bundled_vertex : :
m_description , g ) ) ;
dp . p r o p e r t y ( " width " , g e t (&my_bundled_vertex : : m_x, g ) ) ;
dp . p r o p e r t y ( " h e i g h t " , g e t (&my_bundled_vertex : : m_y, g ) ) ;
b o o s t : : read_graphviz ( f , g , dp ) ;
// Decode v e r t i c e s
const auto v i p = v e r t i c e s ( g ) ;
const auto j = v i p . s e c o n d ;
f o r ( auto i = v i p . f i r s t ; i != j ; ++i )
{
g [ ∗ i ] . m_name = graphviz_decode ( g [ ∗ i ] . m_name) ;
g [ ∗ i ] . m _ d e s c r i p t i o n = graphviz_decode ( g [ ∗ i ] .
m_description ) ;
} 91
return g ;
}
In this algorithm, first it is checked if the file to load exists. Then an empty
directed graph is created, to save typing the typename explicitly.
Then a boost::dynamic_properties is created with its default constructor,
after which we set it to follow the same mapping as in the previous chapter.
From this and the empty graph, ‘boost::read_graphviz’ is called to build up the
graph.
At the moment the graph is created, all ‘my_bundled_vertex’ their names
and description are in a Graphviz-friendly format. By obtaining all vertex iter-
ators and vertex descriptors, the encoding is made undone.
Algorithm 85 shows how to use the ‘load_directed_bundled_vertices_graph_from_dot’
function:
BOOST_AUTO_TEST_CASE(
test_load_directed_bundled_vertices_graph_from_dot )
{
using b o o s t : : num_edges ;
using b o o s t : : num_vertices ;
const auto g
= create_bundled_vertices_markov_chain ( ) ;
const s t d : : s t r i n g f i l e n a m e {
" create_bundled_vertices_markov_chain . dot "
};
save_bundled_vertices_graph_to_dot ( g , f i l e n a m e ) ;
const auto h
= load_directed_bundled_vertices_graph_from_dot (
filename ) ;
BOOST_CHECK( num_edges ( g ) == num_edges ( h ) ) ;
BOOST_CHECK( num_vertices ( g ) == num_vertices ( h ) ) ;
BOOST_CHECK( get_my_bundled_vertexes ( g ) ==
get_my_bundled_vertexes ( h ) ) ;
}
This demonstration shows how the Markov chain is created using the ‘cre-
ate_bundled_vertices_markov_chain’ function (algorithm 66), saved and then
92
loaded. The loaded graph is checked to be the same as the original.
93
Algorithm 86 Loading an undirected graph with bundled vertices from a .dot
file
#include <f s t r e a m >
#include <b o o s t / graph / g r a p h v i z . hpp>
#include " create_empty_undirected_bundled_vertices_graph .
h"
#include " graphviz_decode . h"
#include " i s _ r e g u l a r _ f i l e . h"
b o o s t : : d y n a m i c _ p r o p e r t i e s dp ( b o o s t : :
ignore_other_properties ) ;
dp . p r o p e r t y ( " l a b e l " , g e t (&my_bundled_vertex : : m_name, g ) )
;
dp . p r o p e r t y ( "comment" , g e t (&my_bundled_vertex : :
m_description , g ) ) ;
dp . p r o p e r t y ( " width " , g e t (&my_bundled_vertex : : m_x, g ) ) ;
dp . p r o p e r t y ( " h e i g h t " , g e t (&my_bundled_vertex : : m_y, g ) ) ;
b o o s t : : read_graphviz ( f , g , dp ) ;
// Decode v e r t i c e s
const auto v i p = v e r t i c e s ( g ) ;
const auto j = v i p . s e c o n d ;
f o r ( auto i = v i p . f i r s t ; i != j ; ++i )
{
g [ ∗ i ] . m_name = graphviz_decode ( g [ ∗ i ] . m_name) ;
g [ ∗ i ] . m _ d e s c r i p t i o n = graphviz_decode
94 (g [∗ i ] .
m_description ) ;
}
return g ;
}
The only difference with loading a directed graph, is that the initial empty
graph is undirected instead. Chapter 5.7 describes the rationale of this function.
Algorithm 87 shows how to use the ‘load_undirected_bundled_vertices_graph_from_dot’
function:
BOOST_AUTO_TEST_CASE(
test_load_undirected_bundled_vertices_graph_from_dot )
{
using b o o s t : : num_edges ;
using b o o s t : : num_vertices ;
const auto g
= create_bundled_vertices_k2_graph ( ) ;
const s t d : : s t r i n g f i l e n a m e {
" create_bundled_vertices_k2_graph . dot "
};
save_bundled_vertices_graph_to_dot ( g , f i l e n a m e ) ;
const auto h
= load_undirected_bundled_vertices_graph_from_dot (
filename ) ;
BOOST_CHECK( get_my_bundled_vertexes ( g )
== get_my_bundled_vertexes ( h )
);
}
This demonstration shows how K2 with bundled vertices is created using the
‘create_bundled_vertices_k2_graph’ function (algorithm 69), saved and then
loaded. The loaded graph is checked to be the same as the original.
95
‘my_bundled_edge’ and ‘my_bundled_edge’ type9 .
• An empty directed graph that allows for bundled edges and vertices: see
chapter 6.2
• An empty undirected graph that allows for bundled edges and vertices:
see chapter 6.3
• A two-state Markov chain with bundled edges and vertices: see chapter
6.6
• K3 with bundled edges and vertices: see chapter 6.7
In the process, some basic (sometimes bordering trivial) functions are shown:
• Creating the ‘my_bundled_edge’ class: see chapter 6.1
• Adding a bundled ‘my_bundled_edge’: see chapter 6.4
These functions are mostly there for completion and showing which data types
are used.
96
Algorithm 88 Declaration of my_bundled_edge
c l a s s my_bundled_edge
{
public :
e x p l i c i t my_bundled_edge (
const s t d : : s t r i n g& name = " " ,
const s t d : : s t r i n g& d e s c r i p t i o n = " " ,
const double width = 1 . 0 ,
const double h e i g h t = 1 . 0
) noexcept ;
s t d : : s t r i n g m_name ;
std : : s t r i n g m_description ;
double m_width ;
double m_height ;
};
97
6.2 Create an empty directed graph with bundled edges
and vertices
Algorithm 89 Creating an empty directed graph with bundled edges and ver-
tices
#include <b o o s t / graph / a d j a c e n c y _ l i s t . hpp>
#include "my_bundled_edge . h"
#include " my_bundled_vertex . h"
This code is very similar to the code described in chapter ??, except that there
is a new, fifth template argument:
b o o s t : : p r o p e r t y <b o o s t : : edge_bundled_type_t , my_edge>
This can be read as: “edges have the property ‘boost::edge_bundled_type_t’,
which is of data type ‘my_bundled_edge’”. Or simply: “edges have a bundled
type called my_bundled_edge”.
Demo:
98
Algorithm 90 Demonstration of the ‘cre-
ate_empty_directed_bundled_edges_and_vertices_graph’ function
BOOST_AUTO_TEST_CASE(
test_create_empty_directed_bundled_edges_and_vertices_graph
)
{
const auto g =
create_empty_directed_bundled_edges_and_vertices_graph
() ;
BOOST_CHECK( b o o s t : : num_edges ( g ) == 0 ) ;
BOOST_CHECK( b o o s t : : num_vertices ( g ) == 0 ) ;
}
This code is very similar to the code described in chapter 6.2, except that the di-
rectness (the third template argument) is undirected (due to the boost::undirectedS).
99
Demo:
BOOST_AUTO_TEST_CASE(
test_create_empty_undirected_bundled_edges_and_vertices_graph
)
{
const auto g
=
create_empty_undirected_bundled_edges_and_vertices_graph
() ;
BOOST_CHECK( b o o s t : : num_edges ( g ) == 0 ) ;
BOOST_CHECK( b o o s t : : num_vertices ( g ) == 0 ) ;
}
100
Algorithm 93 Add a bundled edge
When having added a new (abstract) edge to the graph, the edge descriptor
is used to set the my_edge in the graph.
Here is the demo:
101
Algorithm 94 Demo of ‘add_bundled_edge’
BOOST_AUTO_TEST_CASE( test_add_bundled_edge )
{
auto g =
create_empty_directed_bundled_edges_and_vertices_graph
() ;
const auto vd_from = add_bundled_vertex (
my_bundled_vertex ( "From" ) , g ) ;
const auto vd_to = add_bundled_vertex ( my_bundled_vertex
( "To" ) , g ) ;
add_bundled_edge ( vd_from , vd_to , my_bundled_edge ( "X" ) ,
g) ;
BOOST_CHECK( b o o s t : : num_vertices ( g ) == 2 ) ;
BOOST_CHECK( b o o s t : : num_edges ( g ) == 1 ) ;
}
102
Algorithm 95 Get the edges’ my_bundled_edges
The ‘my_bundled_edge’ object associated with the edges are obtained from
the graph its property_map and then put into a std::vector.
Note: the order of the my_bundled_edge objects may be different after
saving and loading.
When trying to get the edges’ my_bundled_edge objects from a graph with-
out bundled edges objects associated, you will get the error ‘formed reference
to void’ (see chapter 12.1).
103
Green cols,Stay cool,6,7
Red,Heat,1,2
Orange,Lose heat,3,4
Yellow cold,Heat,4,5
Figure 22: A two-state Markov chain where the edges and vertices have bundled
properties. The edges’ and vertices’ properties are nonsensical
104
Algorithm 96 Creating the two-state Markov chain as depicted in figure 22
105
6.6.3 Creating such a graph
Here is the demo:
BOOST_AUTO_TEST_CASE(
test_create_bundled_edges_and_vertices_markov_chain )
{
const auto g =
create_bundled_edges_and_vertices_markov_chain ( ) ;
const s t d : : v e c t o r <my_bundled_edge> edge_my_edges{
get_my_bundled_edges ( g )
};
const s t d : : v e c t o r <my_bundled_edge> expected_my_edges {
my_bundled_edge ( "Red" , " Heat " , 1 . 0 , 2 . 0 ) ,
my_bundled_edge ( " Orange " , " Lose ␣ h e a t " , 3 . 0 , 4 . 0 ) ,
my_bundled_edge ( " Yellow ␣ c o l d " , " Heat " , 5 . 0 , 6 . 0 ) ,
my_bundled_edge ( " Green ␣ c o l d " , " Stay ␣ c o o l " , 7 . 0 , 8 . 0 )
};
BOOST_CHECK( edge_my_edges == expected_my_edges ) ;
}
106
107
6.6.5 The .svg file produced
108
6.7.1 Graph
We reproduce the K3 with named edges and vertices of chapter ?? , but with
our bundled edges and vertices instead:
Red,Not green,1,2
Figure 24: K3 : a fully connected graph with three named edges and vertices
109
110
6.7.2 Function to create such a graph
#include "
create_empty_undirected_bundled_edges_and_vertices_graph
. h"
#include " add_bundled_edge . h"
#include " add_bundled_vertex . h"
BOOST_AUTO_TEST_CASE(
test_create_bundled_edges_and_vertices_k3_graph )
{
auto g
= create_bundled_edges_and_vertices_k3_graph ( ) ;
BOOST_CHECK( b o o s t : : num_edges ( g ) == 3 ) ;
BOOST_CHECK( b o o s t : : num_vertices ( g ) == 3 ) ;
}
112
113
6.7.5 The .svg file produced
115
Algorithm 103 Demonstration of the ‘has_bundled_edge_with_my_edge’
function
#include <b o o s t / t e s t / u n i t _ t e s t . hpp>
#include " create_bundled_edges_and_vertices_k3_graph . h"
#include "has_bundled_edge_with_my_edge . h"
BOOST_AUTO_TEST_CASE( test_has_bundled_edge_with_my_edge )
{
auto g
= create_bundled_edges_and_vertices_k3_graph ( ) ;
BOOST_CHECK(
has_bundled_edge_with_my_edge (
my_bundled_edge ( "Oxygen" , " Air " , 1 . 0 , 2 . 0 ) , g
)
);
}
Note that this function only finds if there is at least one edge with that
my_bundled_edge: it does not tell how many edges with that my_bundled_edge
exist in the graph.
116
Algorithm 104 Find the first bundled edge with a certain my_bundled_edge
With the edge descriptor obtained, one can read and modify the edge and
the vertices surrounding it. Algorithm 105 shows some examples of how to do
so.
117
Algorithm 105 Demonstration of the ‘find_first_bundled_edge_with_my_edge’
function
#include <b o o s t / t e s t / u n i t _ t e s t . hpp>
#include " create_bundled_edges_and_vertices_k3_graph . h"
#include " find_first_bundled_edge_with_my_edge . h"
BOOST_AUTO_TEST_CASE(
test_find_first_bundled_edge_with_my_edge )
{
const auto g
= create_bundled_edges_and_vertices_k3_graph ( ) ;
const auto ed
= find_first_bundled_edge_with_my_edge (
my_bundled_edge ( "Oxygen" , " Air " , 1 . 0 , 2 . 0 ) ,
g
);
BOOST_CHECK( b o o s t : : s o u r c e ( ed , g )
!= b o o s t : : t a r g e t ( ed , g )
);
}
Algorithm 106 Get a vertex its my_bundled_vertex from its vertex descriptor
118
Algorithm 107 Demonstration if the ‘get_my_bundled_edge’ function
BOOST_AUTO_TEST_CASE( test_get_my_bundled_edge )
{
auto g
=
create_empty_undirected_bundled_edges_and_vertices_graph
() ;
const my_bundled_edge edge { "Dex" } ;
const auto vd_a = add_bundled_vertex (
my_bundled_vertex ( "A" ) , g
);
const auto vd_b = add_bundled_vertex (
my_bundled_vertex ( "B" ) , g
);
add_bundled_edge ( vd_a , vd_b , edge , g ) ;
const auto ed
= find_first_bundled_edge_with_my_edge ( edge , g ) ;
BOOST_CHECK( get_my_bundled_edge ( ed , g ) == edge ) ;
}
119
Algorithm 108 Set a bundled edge its my_bundled_edge from its edge de-
scriptor
120
Algorithm 109 Demonstration if the ‘set_bundled_edge_my_edge’ function
BOOST_AUTO_TEST_CASE( test_set_my_bundled_edge )
{
auto g
=
create_empty_undirected_bundled_edges_and_vertices_graph
() ;
const auto vd_a = add_bundled_vertex ( my_bundled_vertex {
"A" } , g ) ;
const auto vd_b = add_bundled_vertex ( my_bundled_vertex {
"B" } , g ) ;
const my_bundled_edge old_edge { "Dex" } ;
add_bundled_edge ( vd_a , vd_b , old_edge , g ) ;
const auto vd
= find_first_bundled_edge_with_my_edge ( old_edge , g ) ;
BOOST_CHECK( get_my_bundled_edge ( vd , g )
== old_edge
);
const my_bundled_edge new_edge{ " Diggy " } ;
set_my_bundled_edge ( new_edge , vd , g ) ;
BOOST_CHECK( get_my_bundled_edge ( vd , g )
== new_edge
);
}
121
Algorithm 110 Storing a graph with bundled edges and vertices as a .dot file
122
Algorithm 111 Loading a directed graph with bundled edges and vertices from
a .dot file
#include <f s t r e a m >
#include <b o o s t / graph / g r a p h v i z . hpp>
#include "
create_empty_directed_bundled_edges_and_vertices_graph
. h"
#include " i s _ r e g u l a r _ f i l e . h"
#include " graphviz_decode . h"
b o o s t : : d y n a m i c _ p r o p e r t i e s dp ( b o o s t : :
ignore_other_properties ) ;
dp . p r o p e r t y ( " l a b e l " , g e t (&my_bundled_vertex : : m_name, g ) )
;
dp . p r o p e r t y ( "comment" , g e t (&my_bundled_vertex : :
m_description , g ) ) ;
dp . p r o p e r t y ( " width " , g e t (&my_bundled_vertex : : m_x, g ) ) ;
dp . p r o p e r t y ( " h e i g h t " , g e t (&my_bundled_vertex : : m_y, g ) ) ;
dp . p r o p e r t y ( " edge_id " , g e t (&my_bundled_edge : : m_name, g ) )
;
dp . p r o p e r t y ( " l a b e l " , g e t (&my_bundled_edge : : m_name, g ) ) ;
dp . p r o p e r t y ( "comment" , g e t (&my_bundled_edge : :
m_description , g ) ) ;
123
dp . p r o p e r t y ( " width " , g e t (&my_bundled_edge : : m_width , g ) )
;
dp . p r o p e r t y ( " h e i g h t " , g e t (&my_bundled_edge : : m_height , g
));
b o o s t : : read_graphviz ( f , g , dp ) ;
// Decode v e r t i c e s
{
In this algorithm, first it is checked if the file to load exists. Then an empty
directed graph is created. Next to this, a boost::dynamic_properties is created
with its default constructor, after which we direct the boost::dynamic_properties
to find a ‘node_id’ and ‘label’ in the vertex name map, ‘edge_id’ and ‘label’ to
the edge name map. From this and the empty graph, ‘boost::read_graphviz’ is
called to build up the graph.
Algorithm 112 shows how to use the ‘load_directed_bundled_edges_and_vertices_graph_from_dot’
function:
124
Algorithm 112 Demonstration of the ‘load_directed_bundled_edges_and_vertices_graph_from_dot’functi
BOOST_AUTO_TEST_CASE(
test_load_directed_bundled_edges_and_vertices_graph_from_dot
)
{
using b o o s t : : num_edges ;
using b o o s t : : num_vertices ;
const auto g
= create_bundled_edges_and_vertices_markov_chain ( ) ;
const s t d : : s t r i n g f i l e n a m e {
" create_bundled_edges_and_vertices_markov_chain . dot "
};
save_bundled_edges_and_vertices_graph_to_dot ( g ,
filename ) ;
const auto h
=
load_directed_bundled_edges_and_vertices_graph_from_dot
(
filename
);
BOOST_CHECK( num_edges ( g ) == num_edges ( h ) ) ;
BOOST_CHECK( num_vertices ( g ) == num_vertices ( h ) ) ;
BOOST_CHECK( get_sorted_bundled_vertex_my_vertexes ( g )
== get_sorted_bundled_vertex_my_vertexes ( h )
);
}
This demonstration shows how the Markov chain is created using the ‘cre-
ate_bundled_edges_and_vertices_markov_chain’ function (algorithm 96), saved
and then loaded.
125
7.7 Load an undirected graph with bundled edges and ver-
tices from a .dot file
When loading a graph from file, one needs to specify a type of graph. In this
example, an undirected graph with bundled edges and vertices is loaded, as
shown in algorithm 113:
126
Algorithm 113 Loading an undirected graph with bundled edges and vertices
from a .dot file
#include <f s t r e a m >
#include <b o o s t / graph / g r a p h v i z . hpp>
#include "
create_empty_undirected_bundled_edges_and_vertices_graph
. h"
#include " i s _ r e g u l a r _ f i l e . h"
#include " graphviz_decode . h"
b o o s t : : d y n a m i c _ p r o p e r t i e s dp ( b o o s t : :
ignore_other_properties ) ;
dp . p r o p e r t y ( " l a b e l " , g e t (&my_bundled_vertex : : m_name, g ) )
;
dp . p r o p e r t y ( "comment" , g e t (&my_bundled_vertex : :
m_description , g ) ) ;
dp . p r o p e r t y ( " width " , g e t (&my_bundled_vertex : : m_x, g ) ) ;
dp . p r o p e r t y ( " h e i g h t " , g e t (&my_bundled_vertex : : m_y, g ) ) ;
dp . p r o p e r t y ( " edge_id " , g e t (&my_bundled_edge : : m_name, g ) )
;
dp . p r o p e r t y ( " l a b e l " , g e t (&my_bundled_edge : : m_name, g ) ) ;
dp . p r o p e r t y ( "comment" , g e t (&my_bundled_edge : :
m_description , g ) ) ; 127
dp . p r o p e r t y ( " width " , g e t (&my_bundled_edge : : m_width , g ) )
;
dp . p r o p e r t y ( " h e i g h t " , g e t (&my_bundled_edge : : m_height , g
));
b o o s t : : read_graphviz ( f , g , dp ) ;
// Decode v e r t i c e s
The only difference with loading a directed graph, is that the initial empty
graph is undirected instead. Chapter 7.6 describes the rationale of this function.
Algorithm 114 shows how to use the ‘load_undirected_bundled_vertices_graph_from_dot’
function:
BOOST_AUTO_TEST_CASE(
test_load_undirected_bundled_edges_and_vertices_graph_from_dot
)
{
using b o o s t : : num_edges ;
using b o o s t : : num_vertices ;
const auto g
= create_bundled_edges_and_vertices_k3_graph ( ) ;
const s t d : : s t r i n g f i l e n a m e {
" create_bundled_edges_and_vertices_k3_graph . dot "
};
save_bundled_edges_and_vertices_graph_to_dot ( g ,
filename ) ;
const auto h
=
load_undirected_bundled_edges_and_vertices_graph_from_dot
(
filename
);
BOOST_CHECK( num_edges ( g ) == num_edges ( h ) ) ;
BOOST_CHECK( num_vertices ( g ) == num_vertices ( h ) ) ;
BOOST_CHECK( get_sorted_bundled_vertex_my_vertexes ( g )
== get_sorted_bundled_vertex_my_vertexes ( h )
);
}
This demonstration shows how K2 with bundled vertices is created using the
‘create_bundled_vertices_k2_graph’ function (algorithm ??), saved and then
loaded. The loaded graph is checked to be a graph similar to the original.
128
8 Building graphs with a graph name
Up until now, the graphs created have had no properties themselves. Sure, the
edges and vertices have had properties, but the graph itself has had none. Until
now.
In this chapter, graphs will be created with a graph name of type std::string
• An empty directed graph with a graph name: see chapter
• An empty undirected graph with a graph name: see chapter
129
• the first ‘boost::vecS’: select (that is what the ‘S’ means) that out edges
are stored in a std::vector. This is the default way.
• the second ‘boost::vecS’: select that the graph vertices are stored in a
std::vector. This is the default way.
BOOST_AUTO_TEST_CASE(
test_create_empty_directed_graph_with_graph_name )
{
auto g
= create_empty_directed_graph_with_graph_name ( ) ;
BOOST_CHECK( b o o s t : : num_edges ( g ) == 0 ) ;
BOOST_CHECK( b o o s t : : num_vertices ( g ) == 0 ) ;
}
130
Algorithm 117 Creating an empty undirected graph with a graph name
This code is very similar to the code described in chapter 115, except that the
directness (the third template argument) is undirected (due to the boost::undirectedS).
Algorithm 118 demonstrates the ‘create_empty_undirected_graph_with_graph_name’
function.
BOOST_AUTO_TEST_CASE(
test_create_empty_undirected_graph_with_graph_name )
{
auto g = create_empty_undirected_graph_with_graph_name
() ;
BOOST_CHECK( b o o s t : : num_edges ( g ) == 0 ) ;
BOOST_CHECK( b o o s t : : num_vertices ( g ) == 0 ) ;
}
131
8.3 Get a graph its name property
BOOST_AUTO_TEST_CASE( test_get_graph_name )
{
auto g = create_empty_directed_graph_with_graph_name ( ) ;
const s t d : : s t r i n g name{ "Dex" } ;
set_graph_name ( name , g ) ;
BOOST_CHECK( get_graph_name ( g ) == name ) ;
}
132
8.4 Set a graph its name property
BOOST_AUTO_TEST_CASE( test_set_graph_name )
{
auto g = create_empty_directed_graph_with_graph_name ( ) ;
const s t d : : s t r i n g name{ "Dex" } ;
set_graph_name ( name , g ) ;
BOOST_CHECK( get_graph_name ( g ) == name ) ;
}
133
8.5.2 Function to create such a graph
Algorithm 123 shows the function to create an empty directed graph with a
graph name.
134
Algorithm 124 Demonstration of ‘create_markov_chain_with_graph_name’
BOOST_AUTO_TEST_CASE(
test_create_markov_chain_with_graph_name )
{
const auto g = create_markov_chain_with_graph_name ( ) ;
BOOST_CHECK( b o o s t : : num_vertices ( g ) == 2 ) ;
BOOST_CHECK( b o o s t : : num_edges ( g ) == 4 ) ;
BOOST_CHECK( get_graph_name ( g ) == "Two−s t a t e ␣Markov␣
chain " ) ;
}
135
8.5.5 The .svg file produced
136
Algorithm 126 Creating a K2 graph with a graph name
return g ;
}
BOOST_AUTO_TEST_CASE( test_create_k2_graph_with_graph_name
)
{
const auto g = create_k2_graph_with_graph_name ( ) ;
BOOST_CHECK( b o o s t : : num_vertices ( g ) == 2 ) ;
BOOST_CHECK( b o o s t : : num_edges ( g ) == 1 ) ;
BOOST_CHECK( get_graph_name ( g ) == "K2" ) ;
}
137
8.6.4 The .dot file produced
138
Algorithm 129 Storing a graph with a graph name as a .dot file
139
Algorithm 130 Loading a directed graph with a graph name from a .dot file
i f ( ! i s _ r e g u l a r _ f i l e ( dot_filename ) )
{
s t d : : s t r i n g s t r e a m msg ;
msg << __func__ << " : ␣ f i l e ␣ ’ "
<< d o t _ f i l e n a m e << " ’ ␣ not ␣ found "
;
throw s t d : : invalid_argument ( msg . s t r ( ) ) ;
}
graph g ;
b o o s t : : ref_property_map<graph ∗ , s t d : : s t r i n g >
graph_name{
g e t _ p r o p e r t y ( g , b o o s t : : graph_name )
};
b o o s t : : d y n a m i c _ p r o p e r t i e s dp{
boost : : ignore_other_properties
}; 140
dp . p r o p e r t y ( "name" , graph_name ) ;
141
Algorithm 131 Loading an undirected graph with a graph name from a .dot
file
#include <f s t r e a m >
#include <s t r i n g >
#include <b o o s t / graph / g r a p h v i z . hpp>
#include " create_empty_undirected_graph_with_graph_name . h
"
#include " i s _ r e g u l a r _ f i l e . h"
b o o s t : : ref_property_map<graph ∗ , s t d : : s t r i n g >
graph_name{
g e t _ p r o p e r t y ( g , b o o s t : : graph_name )
};
b o o s t : : d y n a m i c _ p r o p e r t i e s 142
dp{
boost : : ignore_other_properties
};
dp . p r o p e r t y ( "name" , graph_name ) ;
s t d : : s t r i n g graphviz_encode ( s t d : : s t r i n g s ) n o e x c e p t
{
b o o s t : : a l g o r i t h m : : r e p l a c e _ a l l ( s , " , " , "$$$COMMA$$$" ) ;
b o o s t : : a l g o r i t h m : : r e p l a c e _ a l l ( s , " ␣ " , "$$$SPACE$$$" ) ;
b o o s t : : a l g o r i t h m : : r e p l a c e _ a l l ( s , " \" " , "$$$QUOTE$$$" ) ;
return s ;
}
s t d : : s t r i n g graphviz_decode ( s t d : : s t r i n g s ) n o e x c e p t
{
b o o s t : : a l g o r i t h m : : r e p l a c e _ a l l ( s , "$$$COMMA$$$" , " , " ) ;
b o o s t : : a l g o r i t h m : : r e p l a c e _ a l l ( s , "$$$SPACE$$$" , " ␣ " ) ;
b o o s t : : a l g o r i t h m : : r e p l a c e _ a l l ( s , "$$$QUOTE$$$" , " \" " ) ;
return s ;
}
143
Algorithm 134 Check if a std::string is Graphviz-friendly
11 Misc functions
These are some function I needed for creating this tutorial. Although they are
not important for working with graphs, I used these heavily. These functions
may be compiler-dependent, platform-dependent and/or there may be superior
alternatives. I just add them for completeness.
144
Algorithm 135 Getting a data type its name as a std::string
template<typename T>
s t d : : s t r i n g get_type_name ( ) n o e x c e p t
{
s t d : : s t r i n g tname = typeid (T) . name ( ) ;
int s t a t u s = −1;
char ∗ const demangled_name{
a b i : : __cxa_demangle (
tname . c _ s t r ( ) , NULL, NULL, &s t a t u s
)
};
i f ( s t a t u s == 0 ) {
tname = demangled_name ;
s t d : : f r e e ( demangled_name ) ;
}
return tname ;
}
145
Algorithm 136 Convert a .dot file to a .svg
void convert_dot_to_svg (
const s t d : : s t r i n g& dot_filename ,
const s t d : : s t r i n g& s v g _ f i l e n a m e
)
{
i f ( ! has_dot ( ) )
{
s t d : : s t r i n g s t r e a m msg ;
msg << __func__ << " : ␣ ’ dot ’ ␣ cannot ␣ be ␣ found . ␣ "
<< " type ␣ ’ sudo ␣ apt−g e t ␣ i n s t a l l ␣ g r a p h v i z ’ ␣ i n ␣ t h e ␣
command␣ l i n e "
;
throw s t d : : r u n t i m e _ e r r o r ( msg . s t r ( ) ) ;
}
i f ( ! is_valid_dot_file ( dot_filename ) )
{
s t d : : s t r i n g s t r e a m msg ;
msg << __func__ << " : ␣ f i l e ␣ ’ " << d o t _ f i l e n a m e
<< " ’ ␣ i s ␣ not ␣ a ␣ v a l i d ␣DOT␣ l a n g u a g e "
;
throw s t d : : invalid_argument ( msg . s t r ( ) ) ;
}
s t d : : s t r i n g s t r e a m cmd ;
cmd << " dot ␣−Tsvg ␣ " << d o t _ f i l e n a m e << " ␣−o ␣ " <<
svg_filename ;
const int e r r o r {
s t d : : system (cmd . s t r ( ) . c _ s t r ( ) )
};
if ( error )
{
s t d : : c e r r << __func__ << " : ␣ warning : ␣command␣ ’ "
<< cmd . s t r ( ) << " ’ ␣ r e s u l t i n g ␣ i n ␣ e r r o r ␣ "
<< e r r o r ;
}
i f ( ! i s _ r e g u l a r _ f i l e ( svg_filename ) )
{
s t d : : s t r i n g s t r e a m msg ;
msg << __func__ << " : ␣ f a146 i l e d ␣ t o ␣ c r e a t e ␣SVG␣ output ␣
f i l e ␣ ’"
<< s v g _ f i l e n a m e << " ’ "
;
throw s t d : : r u n t i m e _ e r r o r ( msg . s t r ( ) ) ;
}
}
‘convert_dot_to_svg’ makes a system call to the program ‘dot’ to convert
the .dot file to an .svg file.
12 Errors
Some common errors.
void formed_reference_to_void ( ) n o e x c e p t
{
get_vertex_names ( create_k2_graph ( ) ) ;
}
147
12.2 No matching function for call to ‘clear_out_edges’
This compile-time error occurs when you want to clear the outward edges from
a vertex in an undirected graph.
Algorithm 139 Creating the error ‘no matching function for call to
clear_out_edges’
void no_matching_function_for_call_to_clear_out_edges ( )
noexcept
{
auto g = create_k2_graph ( ) ;
const auto vd = ∗ v e r t i c e s ( g ) . f i r s t ;
b o o s t : : c l e a r _ i n _ e d g e s ( vd , g ) ;
}
148
Algorithm 140 Creating the error ‘Property not found: node_id’
void property_not_found_node_id ( ) n o e x c e p t
{
const s t d : : s t r i n g d o t _ f i l e n a m e { "
property_not_found_node_id . dot " } ;
// C r e a t e a f i l e
{
const auto g = create_k2_graph ( ) ;
save_graph_to_dot ( g , d o t _ f i l e n a m e ) ;
a s s e r t ( i s _ r e g u l a r _ f i l e ( dot_filename ) ) ;
}
// Try t o re ad t h a t f i l e
std : : i f s t r e a m f ( dot_filename . c_str ( ) ) ;
auto g = create_empty_undirected_graph ( ) ;
try {
b o o s t : : read_graphviz ( f , g , dp ) ;
}
catch ( s t d : : e x c e p t i o n &) {
return ; // S h o u l d g e t h e r e
}
a s s e r t ( ! " Should ␣ not ␣ g e t ␣ h e r e " ) ;
}
149
i s >> s ; // s has an XML format
a s s e r t ( s != " 0 " ) ;
a n y _ c l a s s = my_class ( s ) ;
return i s ;
}
This was because I misconfigured the reader. I did (heavily simplified code):
graph load_from_dot ( c o n s t s t d : : s t r i n g& d o t _ f i l e n a m e )
{
std : : i f s t r e a m f ( dot_filename . c_str ( ) ) ;
graph g ;
b o o s t : : d y n a m i c _ p r o p e r t i e s dp ;
dp . p r o p e r t y ( " node_id " , g e t ( b o o s t : : vertex_custom_type , g ) ) ;
dp . p r o p e r t y ( " l a b e l " , g e t ( b o o s t : : vertex_custom_type , g ) ) ;
b o o s t : : read_graphviz ( f , g , dp ) ;
return g ;
}
Where it should have been:
graph load_from_dot ( c o n s t s t d : : s t r i n g& d o t _ f i l e n a m e )
{
std : : i f s t r e a m f ( dot_filename . c_str ( ) ) ;
graph g ;
b o o s t : : d y n a m i c _ p r o p e r t i e s dp ( b o o s t : : i g n o r e _ o t h e r _ p r o p e r t i e s ) ;
dp . p r o p e r t y ( " l a b e l " , g e t ( b o o s t : : vertex_custom_type , g ) ) ;
b o o s t : : read_graphviz ( f , g , dp ) ;
return g ;
}
The explanation is that by setting the boost::dynamic_property ‘node_id’
to ‘boost::vertex_custom_type’, operator>> will receive the node indices.
An alternative, but less clean solution, is to let operator>> ignore the node
indices:
s t d : : i s t r e a m& r i b i : : cmap : : o p e r a t o r >>(s t d : : i s t r e a m& i s , my_class& a n y _ c l a s s ) n o e x
{
std : : s t r i n g s ;
i s >> s ; // s has an XML format
i f ( ! is_xml ( s ) ) { // I g n o r e node i n d e x
a n y _ c l a s s _ c l a s s = my_class ( ) ;
}
else {
a n y _ c l a s s _ c l a s s = my_class ( s ) ;
}
return i s ;
}
150
13 Appendix
13.1 List of all edge, graph and vertex properties
The following list is obtained from the file ‘boost/graph/properties.hpp’.
Edge Graph Vertex
edge_all graph_all vertex_all
edge_bundle graph_bundle vertex_bundle
edge_capacity graph_name vertex_centrality
edge_centrality graph_visitor vertex_color
edge_color vertex_current_degree
edge_discover_time vertex_degree
edge_finished vertex_discover_time
edge_flow vertex_distance
edge_global vertex_distance2
edge_index vertex_finish_time
edge_local vertex_global
edge_local_index vertex_in_degree
edge_name vertex_index
edge_owner vertex_index1
edge_residual_capacity vertex_index2
edge_reverse vertex_local
edge_underlying vertex_local_index
edge_update vertex_lowpoint
edge_weight vertex_name
edge_weight2 vertex_out_degree
vertex_owner
vertex_potential
vertex_predecessor
vertex_priority
vertex_rank
vertex_root
vertex_underlying
vertex_update
151
Edge Graph Vertex
arrowhead _background color
arrowsize bgcolor colorscheme
arrowtail center comment
color charset distortion
colorscheme color fillcolor
comment colorscheme fixedsize
decorate comment fontcolor
dir concentrate fontname
fillcolor fillcolor fontsize
fontcolor fontcolor gradientangle
fontname fontname height
fontsize fontpath image
gradientangle fontsize imagescale
headclip forcelabels label
headlabel gradientangle labelloc
headport imagepath layer
label label margin
labelangle labeljust nojustify
labeldistance labelloc orientation
labelfloat landscape penwidth
labelfontcolor layerlistsep peripheries
labelfontname layers pos
labelfontsize layerselect regular
layer layersep samplepoints
nojustify layout shape
penwidth margin shapefile
pos nodesep sides
style nojustify skew
tailclip orientation sortv
taillabel outputorder style
tailport pack width
weight packmode xlabel
xlabel pad z
page
pagedir
penwidth
quantum
ratio
rotate
size
sortv
splines
style
viewport
152
References
[1] Eckel Bruce. Thinking in c++, volume 1. 2002.
[2] Marshall P Cline, Greg Lomow, and Mike Girou. C++ FAQs. Pearson
Education, 1998.
[3] Jarrod Hollingworth, Bob Swart, and Jamie Allsop. C++ Builder 5 Devel-
oper’s Guide with Cdrom. Sams, 2000.
[4] John Lakos. Large-scale C++ software design, volume 10. Addison-Wesley
Reading, 1996.
[5] Jesse Liberty. Sams teach yourself C++ in 24 hours. Sams Publishing,
2001.
[6] Steve McConnell. Code complete. Pearson Education, 2004.
[7] Scott Meyers. Effective C++: 55 specific ways to improve your programs
and designs. Pearson Education, 2005.
[8] Jeremy G Siek, Lie-Quan Lee, and Andrew Lumsdaine. Boost Graph Li-
brary: User Guide and Reference Manual, The. Pearson Education, 2001.
[9] Bjarne Stroustrup. The C++ Programming Language (3rd edition). 1997.
[10] Bjarne Stroustrup. The C++ Programming Language (4th edition). 2013.
[11] Herb Sutter and Andrei Alexandrescu. C++ coding standards: 101 rules,
guidelines, and best practices. Pearson Education, 2004.
153
Index
#include, 12 boost::vertices does not exist, 19, 21,
K2 , create, 29 25
K3 , create, 32 boost::write_graphviz, 61
bundled_vertices_writer, 89
Add a vertex, 17
Add an edge, 21 const, 13
Add bundled edge, 101 const-correctness, 13
Add bundled vertex, 68 Convert dot to svg, 146
Add vertex, 17 Count connected components, 55, 57
add_edge, 22 Count undirected graph levels, 59
aer_, 23 Counting the number of edges, 16
All edge properties, 151 Counting the number of vertices, 15
All graph properties, 151 Create K2 , 29
All vertex properties, 151 Create K2 graph, 30
assert, 15, 22 Create K3 , 32
auto, 13 Create K3 graph, 33
Create .dot from graph, 60
boost::add_edge, 21, 22, 27, 30 Create .dot from graph with bundled
boost::add_edge result, 23 edges and vertices, 121
boost::add_vertex, 17, 27, 30 Create all direct-neighbour subgraphs,
boost::adjacency_list, 13 52
boost::adjacency_matrix, 13 Create an empty directed graph, 12
boost::degree does not exist, 43 Create an empty graph, 14
boost::directedS, 14, 67, 130 Create bundled edges and vertices K3
boost::dynamic_properties, 62, 92, 124, graph, 111
148 Create bundled edges and vertices Markov
boost::edge does not exist, 46 chain, 105
boost::edge_bundled_type_t, 98 Create bundled vertices K2 graph, 76
boost::edges does not exist, 23–25 Create bundled vertices Markov chain,
boost::get does not exist, 7 71
boost::graph_name, 130 Create direct-neighbour subgraph, 49
boost::graph_name_t, 130 Create direct-neighbour subgraph_including_in_edges,
boost::ignore_other_properties, 62, 148 51
boost::in_degree does not exist, 43 Create directed graph, 26
boost::no_property, 130 Create directed graph from .dot, 61
boost::num_edges, 16 Create empty directed bundled edges
boost::num_vertices, 15 and vertices graph, 98
boost::out_degree does not exist, 43, Create empty directed bundled vertices
44 graph, 67
boost::property, 98, 130 Create empty directed graph, 12
boost::read_graphviz, 62, 92, 124, 148 Create empty directed graph with graph
boost::undirectedS, 14, 68, 99, 131 name, 129
boost::vecS, 14, 67, 130
154
Create empty undirected bundled edges Get bundled vertex my_vertexes, 69
and vertices graph, 99 Get edge between vertices, 47
Create empty undirected bundled ver- Get edge descriptors, 25
tices graph, 68 Get edge iterators, 24
Create empty undirected graph, 14 Get edge my_bundled_edges, 103
Create empty undirected graph with Get graph name, 132
graph name, 131 Get my_bundled_edge, 118
Create K2 graph with graph name, 137 Get n edges, 16
Create Markov chain, 27 Get n vertices, 15
Create Markov chain with graph name, Get type name, 145
134 Get vertex descriptors, 20
Create path graph, 34, 35 Get vertex iterators, 19
Create Petersen graph, 37, 39 Get vertex out degrees, 44
Create undirected graph from .dot, 63 Get vertices, 19
Create undirected graph with bundled Graph properties, 151
edges and vertices from .dot, Graphviz, 60
126 graphviz decode, 143
graphviz encode, 143
Declaration, my_bundled_edge, 97
Declaration, my_bundled_vertex, 66 Has bundled edge with my_bundled_edge,
decltype(auto), 7 115
directed graph, 9 Has bundled vertex with my_vertex,
Directed graph, create, 26 81
Has edge between vertices, 46
ed_, 25 header file, 12
edge, 46
Edge descriptor, 24 idegree, 43
Edge descriptors, get, 25 in_degree, 43
Edge iterator, 23 Is isomorphic, 53
Edge iterator pair, 23 Is regular file, 147
Edge properties, 151 is_graphviz_friendly, 144
Edge, add, 21
link, 148
edges, 23, 25
Load directed bundled edges and ver-
Edges, counting, 16
tices graph from dot, 123
eip_, 23
Load directed bundled vertices graph
Empty directed graph, create, 12
from dot, 91
Empty graph, create, 14
Load directed custom edges and ver-
Find first bundled edge with my_bundled_edge, tices graph from dot, 140
117 Load directed graph from .dot, 61
Find first bundled vertex with my_vertex,Load directed graph from dot, 62
83 Load undirected bundled edges and ver-
Formed reference to void, 147 tices graph from dot, 127
Load undirected bundled vertices graph
get, 7 from dot, 94
Get bundled vertex my_bundled_vertex,Load undirected custom edges and ver-
84 tices graph from dot, 142
155
Load undirected graph from .dot, 63 Set bundled edge my_bundled_edge,
Load undirected graph from_dot, 64 120
Load undirected graph with bundled Set bundled vertex my_bundled_vertexes,
edges and vertices from .dot, 87
126 Set graph name, 133
Set vertex my_vertex, 85
m_, 66, 97 static_assert, 17
make_bundled_vertices_writer, 88 static_cast, 15
member, 66, 97 std::copy, 21
my_bundled_edge, 97 std::cout, 61
my_bundled_edge declaration, 97 std::ifstream, 62
my_bundled_edge.h, 97 std::list, 13
my_bundled_vertex, 66, 67 std::ofstream, 61
my_bundled_vertex.h, 66 std::pair, 22
my_edge, 98 std::vector, 13
my_vertex declaration, 66 STL, 13
156