I am confused about how to actually create a Graph using the boost library, I have looked at the example code and there are no comments explaining what it does.
How do you make a graph, and add vertices and edges as you go?
Here's a simple example, using an adjacency list and executing a topological sort:
#include <iostream>
#include <deque>
#include <iterator>
#include "boost/graph/adjacency_list.hpp"
#include "boost/graph/topological_sort.hpp"
int main()
{
// Create a n adjacency list, add some vertices.
boost::adjacency_list<> g(num tasks);
boost::add_vertex(0, g);
boost::add_vertex(1, g);
boost::add_vertex(2, g);
boost::add_vertex(3, g);
boost::add_vertex(4, g);
boost::add_vertex(5, g);
boost::add_vertex(6, g);
// Add edges between vertices.
boost::add_edge(0, 3, g);
boost::add_edge(1, 3, g);
boost::add_edge(1, 4, g);
boost::add_edge(2, 1, g);
boost::add_edge(3, 5, g);
boost::add_edge(4, 6, g);
boost::add_edge(5, 6, g);
// Perform a topological sort.
std::deque<int> topo_order;
boost::topological_sort(g, std::front_inserter(topo_order));
// Print the results.
for(std::deque<int>::const_iterator i = topo_order.begin();
i != topo_order.end();
++i)
{
cout << tasks[v] << endl;
}
return 0;
}
I agree that the boost::graph documentation can be intimidating, but it's worth having a look.
I can't recall if the contents of the printed book is the same, I suspect it's a bit easier on the eyes. I actually learnt to use boost:graph from the book. The learning curve can feel pretty steep though. The book I refer to and reviews can be found here.
This is based off the example given on the boost::graph website, with comments added:
#include <iostream>
#include <utility>
#include <algorithm>
#include <vector>
#include "boost/graph/graph_traits.hpp"
#include "boost/graph/adjacency_list.hpp"
using namespace boost;
int main(int argc, char *argv[])
{
//create an -undirected- graph type, using vectors as the underlying containers
//and an adjacency_list as the basic representation
typedef adjacency_list<vecS, vecS, undirectedS> UndirectedGraph;
//Our set of edges, which basically are just converted into ints (0-4)
enum {A, B, C, D, E, N};
const char *name = "ABCDE";
//An edge is just a connection between two vertitices. Our verticies above
//are an enum, and are just used as integers, so our edges just become
//a std::pair<int, int>
typedef std::pair<int, int> Edge;
//Example uses an array, but we can easily use another container type
//to hold our edges.
std::vector<Edge> edgeVec;
edgeVec.push_back(Edge(A,B));
edgeVec.push_back(Edge(A,D));
edgeVec.push_back(Edge(C,A));
edgeVec.push_back(Edge(D,C));
edgeVec.push_back(Edge(C,E));
edgeVec.push_back(Edge(B,D));
edgeVec.push_back(Edge(D,E));
//Now we can initialize our graph using iterators from our above vector
UndirectedGraph g(edgeVec.begin(), edgeVec.end(), N);
std::cout << num_edges(g) << "\n";
//Ok, we want to see that all our edges are now contained in the graph
typedef graph_traits<UndirectedGraph>::edge_iterator edge_iterator;
//Tried to make this section more clear, instead of using tie, keeping all
//the original types so it's more clear what is going on
std::pair<edge_iterator, edge_iterator> ei = edges(g);
for(edge_iterator edge_iter = ei.first; edge_iter != ei.second; ++edge_iter) {
std::cout << "(" << source(*edge_iter, g) << ", " << target(*edge_iter, g) << ")\n";
}
std::cout << "\n";
//Want to add another edge between (A,E)?
add_edge(A, E, g);
//Print out the edge list again to see that it has been added
for(edge_iterator edge_iter = ei.first; edge_iter != ei.second; ++edge_iter) {
std::cout << "(" << source(*edge_iter, g) << ", " << target(*edge_iter, g) << ")\n";
}
//Finally lets add a new vertex - remember the verticies are just of type int
int F = add_vertex(g);
std::cout << F << "\n";
//Connect our new vertex with an edge to A...
add_edge(A, F, g);
//...and print out our edge set once more to see that it was added
for(edge_iterator edge_iter = ei.first; edge_iter != ei.second; ++edge_iter) {
std::cout << "(" << source(*edge_iter, g) << ", " << target(*edge_iter, g) << ")\n";
}
return 0;
}
I think you will find the following resources very helpful.
Graph Theory Primer
If you are unfamiliar with graph theory or need a refresher, then take a look at boost's Review of Elementary Graph Theory:
http://www.boost.org/doc/libs/1_58_0/libs/graph/doc/graph_theory_review.html
This primer is helpful in understanding the terminology, how data structures represent graphs (adjacency matrix, adjacency list, etc…), and algorithms (breadth-first search, depth-first search, shortest-path, etc…).
Sample Code Described in Detail
For sample code for creating graphs that is described in detail, then take a look at the following section of Boris Schäling's online book - The Boost C++ Libraries:
http://theboostcpplibraries.com/boost.graph-vertices-and-edges
Boris explains how to work with vertices and edges using the adjacenty_list. The code is thoroughly explained so you can understand each example.
Understanding adjacency_list Template Parameters
It is important to understand the template parameters for the adjacency_list. For example, do you want a directed or undirected graph? Do you want your graph to contain multiple edges with the same end nodes (i.e. multigraphs)? Performance also comes into play. Boris' book explains some of these, but you will find additional information on using the adjacenty_list here:
http://www.boost.org/doc/libs/1_58_0/libs/graph/doc/using_adjacency_list.html
Using Custom Objects for Vertices, Edges, or Graphs
If you want to use custom objects for the vertices, edges, or even the graph itself, then you will want to use bundled properties. The following links will be helpful for using bundled properties:
http://www.boost.org/doc/libs/1_58_0/libs/graph/doc/bundles.html
And perhaps this one too for an example:
adding custom vertices to a boost graph
Detecting Circular Dependencies (Cycles)
There are multiple ways to detect circular dependencies including:
Depth-First Search:
One simple way is by performing a depth-first search and detecting if the search runs into an already discovered vertex in the current search tree. Here is an example of detecting cyclic dependencies using boost's depth-first search:
http://www.boost.org/doc/libs/1_58_0/libs/graph/doc/file_dependency_example.html#sec:cycles
Topological Sort:
One can also detect cycles using a topological sort. boost provides a topological_sort algorithm:
http://www.boost.org/doc/libs/1_58_0/libs/graph/doc/topological_sort.html
A topological sort works on a directed acyclic graph (DAG). If a cyclic graph is passed in, then an exception is thrown, thus indicating that the graph has a circular dependency. topological_sort includes a depth-first search, but also provides a linear ordering of the vertices. Here is an example:
http://www.boost.org/doc/libs/1_58_0/libs/graph/doc/file_dependency_example.html#sec:cycles
Strongly Connected Components:
Additionally, finding strongly connected components can indicate whether or not a graph has cycles:
http://www.personal.kent.edu/~rmuhamma/Algorithms/MyAlgorithms/GraphAlgor/strongComponent.htm
boost's strong_components function computes the strongly connected components of a directed graph using Tarjan's algorithm.
http://www.boost.org/doc/libs/1_58_0/libs/graph/doc/strong_components.html
File Dependency Example
Another helpful link is one that was already provided - boost's File Dependency Example that shows how to setup a graph of source code files, order them based on their compilation order (topological sort), determine what files can be compiled simultaneously, and determine cyclic dependencies:
http://www.boost.org/doc/libs/1_58_0/libs/graph/doc/file_dependency_example.html
Boost's adjacency_list is a good way to go, this example creates a directed graph and outputs an image of the graph using AT&T's GraphViz:
#include <iostream>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graphviz.hpp>
int main()
{
using namespace std;
using namespace boost;
/* define the graph type
listS: selects the STL list container to store
the OutEdge list
vecS: selects the STL vector container to store
the vertices
directedS: selects directed edges
*/
typedef adjacency_list< listS, vecS, directedS > digraph;
// instantiate a digraph object with 8 vertices
digraph g(8);
// add some edges
add_edge(0, 1, g);
add_edge(1, 5, g);
add_edge(5, 6, g);
add_edge(2, 3, g);
add_edge(2, 4, g);
add_edge(3, 5, g);
add_edge(4, 5, g);
add_edge(5, 7, g);
// represent graph in DOT format and send to cout
write_graphviz(cout, g);
return 0;
}
The output is a DOT file that you can quickly feed into the dot utility that comes with GraphViz.
Some short and to-the-point recipes in getting started with the Boost C++ libraries can be found here:
Using the Boost Graph Library
These code samples listed on here appear reasonably up to date and appear to compile and work fine. I am finding that some of the online documentation concerning the use of the Boost Graph Library seems to be out of date or produces compilation errors.
There are a number of working examples here including creating directed and undirected graphs, printing the weights of edges, finding minimal spanning trees using Kruskal's algorithm, and maximum flow problems.
Related
I am trying to implement a direct acyclic graph with the boost::adjacency_list<> class. For that I re-implemented the idea from this question: boost graph that doesn't allow circular references
using Graph = boost::adjacency_list<boost::vecS, boost::vecS, boost::bidirectionalS>;
using Vertex = Graph::vertex_descriptor;
With that I was able to call the topological_sort after each add_edge to confirm I have a DAG:
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/topological_sort.hpp>
#include <boost/iterator/function_output_iterator.hpp>
int main()
{
Graph g;
// 1. build a graph structure
auto v1 = boost::add_vertex(g);
auto v2 = boost::add_vertex(g);
auto v3 = boost::add_vertex(g);
boost::add_edge(v1, v2, g);
boost::topological_sort(g, boost::make_function_output_iterator([](int) {}));
boost::add_edge(v2, v3, g);
boost::topological_sort(g, boost::make_function_output_iterator([](int) {}));
}
An additional requirement I have is to enable some undo/redo support on actions I do on my graph. One of the operation could be to remove a vertex and deleting all incoming and outgoing edges from the specified vertex. A sample code could look like this:
static void Print(const Graph& g)
{
std::cout << "Vertices: " << std::endl;
for (auto vertices = boost::vertices(g); vertices.first != vertices.second; ++vertices.first)
{
std::cout << *vertices.first << std::endl;
}
std::cout << "Edges: " << std::endl;
for (auto edges = boost::edges(g); edges.first != edges.second; ++edges.first)
{
auto edgeDescriptor = *edges.first;
std::cout << edgeDescriptor.m_source << "->" << edgeDescriptor.m_target << std::endl;
}
std::cout << std::endl;
}
int main()
{
Graph g;
// 1. build a graph structure
auto v1 = boost::add_vertex(g);
auto v2 = boost::add_vertex(g);
auto v3 = boost::add_vertex(g);
boost::add_edge(v1, v2, g);
boost::add_edge(v2, v3, g);
Print(g);
// 2. prepare for deletion of v2
std::vector<Vertex> outVertices;
for(auto vertices = boost::adjacent_vertices(v2, g); vertices.first != vertices.second; ++vertices.first)
{
outVertices.push_back(*vertices.first);
}
std::vector<Vertex> inVertices;
for (auto vertices = boost::inv_adjacent_vertices(v2, g); vertices.first != vertices.second; ++vertices.first)
{
inVertices.push_back(*vertices.first);
}
// 3. delete v2
boost::clear_vertex(v2, g);
boost::remove_vertex(v2, g);
Print(g);
// 4 undo the operation
v2 = boost::add_vertex(g);
for(auto& outVertex : outVertices)
{
boost::add_edge(v2, outVertex, g);
}
for (auto& inVertex : inVertices)
{
boost::add_edge(inVertex, v2, g);
}
Print(g);
}
Output:
Vertices:
0
1
2
Edges:
0->1
1->2
Vertices:
0
1
Edges:
Vertices:
0
1
2
Edges:
2->2
0->2
This doesn't work of course because the remove_vertex call invalidates my earlier saved vertex_descriptors. A solution I found for this problem was how to call "boost::remove_vertex" without re-indexing the vertices?
Here it proposed to use a list instead of a vector for saving the vertices and therefore the vertex_descriptors do not get invalidated.
using Graph = boost::adjacency_list<boost::listS, boost::listS, boost::bidirectionalS>;
This does work as expected and gives the desired output where the undo works:
Vertices:
000001DD1902AC90
000001DD19028F70
000001DD19028D80
Edges:
000001DD1902AC90->000001DD19028F70
000001DD19028F70->000001DD19028D80
Vertices:
000001DD1902AC90
000001DD19028D80
Edges:
Vertices:
000001DD1902AC90
000001DD19028D80
000001DD19028F70
Edges:
000001DD19028F70->000001DD19028D80
000001DD1902AC90->000001DD19028F70
The problem I have now is that the topological_sort does not compile anymore with my new Graph definition. For full error message: https://godbolt.org/z/WKjeK3ddP
The question I have (or the problem I am trying to solve is) how can I implement a direct acyclic graph in boost without invalidating the vertex_descriptors while removing vertices and having the topological_sort possibility?
Or maybe not that specific: How to impelement a DAG with boost on where I can enable/implement undo/redo possibilities?
Excellent question.
The topological sort
It fails because there's no longer an implicit vertex index. Topological sort requires it to get a default color map, so things that work:
std::map<Vertex, int> index;
for (auto v : boost::make_iterator_range(vertices(g)))
index.emplace(v, index.size());
boost::topological_sort(
g, boost::make_function_output_iterator([](Vertex) {}),
boost::vertex_index_map(boost::make_assoc_property_map(index)));
Or, just provide a color map directly, which is probably a better idea:
std::map<Vertex, boost::default_color_type> colors;
boost::topological_sort(
g, boost::make_function_output_iterator([](Vertex) {}),
boost::color_map(boost::make_assoc_property_map(colors)));
There's some more thinking behind this problem here:
What is required for a custom BGL graph to work with topological sort?
Big Picture
The thing I like most about your question is that after a very high level of detailed analysis, you still go back and wonder whether there's something else. This is you, trying to avoid tunnel vision or X/Y problem.
Or maybe not that specific: How to impelement a DAG with boost on where I can enable/implement undo/redo possibilities?
To be completely honest, I think you're shoe-horning a very specific datastructure into something BGL should have. And that's just improbable. I'd reverse the design priorities.
BGL is expressly designed to be almost completely generic. This means you can use the algorithms on your own data structures given the right instrumentation/adaptation.
Your requirements feel more like a versioned tree/forest. A Sean-Parent-style tree with shared_ptr<const T> nodes seems more applicable.
A quick demonstration of the concept, focusing on Copy-On-Write deep modification of sub-trees: Transforming trees in C++
A more advanced strategy could be e.g. splay trees, but I'd have to freshen up on theory myself before recommening/implementing anything like that.
Now, all of this doesn't really touch on any requirements for graph algorithms. The linked answer above incidentally shows topological sort on a custom graph model, so you can have a look and see whether that looks viable. At some point you might decide it's better to "just implement" BFS for your own datastructure.
I'm trying to create an application which displays a simple graph and since I'm using boost::graph for the underlying data structure, I'd like to use the layouting algorithms available in the library as well.
The answer presented here explains how to use a layout algorithm within the boost library to lay out vertices of graph:
How does the attractive force of Fruchterman Reingold work with Boost Graph Library
But sadly it does not explain how - after the layout has been calculated - the coordinates of the vertices can actually be accessed. Even though we get a vector of positions (or rather points), the float components are private, so that doesn't help. The boost::graph documentation also doesn't address this topic.
So how can simple (X,Y) coordinates of each vertex be retrieved after a layouting algorithm has been applied?
After reviewing the boost graph source code it turned out that this isn't so hard after all.
We can use a property map to iterate over the PositionsMap and the [] operator to access the coordinates:
template<typename Graph, typename Positions>
void print_positions(const Graph &g, const Positions &positions) {
auto index_map = boost::get(boost::vertex_index, graph);
using PropertyMap = boost::iterator_property_map<Positions::iterator, decltype(index_map)>;
PropertyMap position_map(positions.begin(), index_map);
BGL_FORALL_VERTICES(v, graph, Graph) {
Position pos = position_map[v];
cout << v << ": " << pos[0] << "|" << pos[1] << endl;
}
}
I'm having trouble getting boost's connected components algorithm working reliably. I want to use it to separate collections of 3D point data. I have a data set where there is are two obvious 'clusters' of 3D points separated by a large distance.
I create an undirected Boost graph object with bundled information which is a simple 'point' class, this contains an x, y and z property and a method to calculate distance of this point from another.
adjacency_list<vecS, vecS, undirectedS,Point> Graph;
I have a vector of point information (Point_dat) which I now transfer to vertices of the graph
for (size_t i = 0; i < Inter_dat.size(); i++)
{
// Create a new vertex for every element of the original data
add_vertex(Graph);
(Graph)[i] = Point_dat[i];
}
I now iterate through the vertices of the graph (in two loops) to calculate the distance from each vertex to each other vertex (I know there are more efficient ways of doing this but this is a first stab)
If the distance between two points is below a threshold, I add an edge between the two vertices.
typedef adjacency_list<vecS, vecS, undirectedS, Point>::vertex_iterator iterator;
std::pair<iterator, iterator> p = vertices(Graph);
for (iterator it = p.first; it != p.second; it++)
{
for (iterator it2 = it; it2 != p.second; it2++)
{
double thisdist = Graph[*it].Distance(Graph[*it2]);
if (thisdist < distance_thresh & thisdist > 0)
{
add_edge(*it,*it2, Graph);
}
}
}
I then calculate the connected components.
std::vector<int> comp(num_vertices(Graph));
int num = connected_components(Graph, &comp[0]);
The problem is, the data isn't being separated into the two clusters. The main cluster still contains some of the second. In fact if I re-run the analysis using the largest component (as selected by Boost) it finds a number of new components.
Why isn't it working? Do I need to add different parameters?
UPDATE: To give more context, I'm trying to implement a process I developed in Matlab in C++. To try and see the root of the problem I saved the edges of my graph
std::ofstream myfile("D:\\edges.txt");
auto q = edges(Graph);
for (auto it = q.first; it != q.second; ++it)
myfile << *it << std::endl;
myfile.close();
and imported the resulting data into Matlab. I then used Matlab's connected components process on this edge information and it gave me exactly the right answer. It seems that I'm setting the graph up correctly but Boosts connected components isn't giving me the right answer.
I am interested in implementing my computational geometry algorithms using the CGAL library.
Ideally, I am also interested in being able to animate my algorithm.CGAL has an interface to geomview built in which I am interested in using for illustrating these algorithms.
Based on what I little I understand of the CGAL geomview interface (from this example), below is a very simple code I wrote, which inserts 5 random points, and segments between some of the points.
However, once I render the objects to the screen, I don't know how unrender them or delete them from the geomview window, if they need to be deleted at the
next iteration(say) of my algorithm. So how would I modify the code below to do just that?
If someone knows of a better way than using geomview to animate geometry algorithms with CGAL that would also be helpful.
#include <iostream>
#include <vector>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <unistd.h>
#include <CGAL/IO/Geomview_stream.h>
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef K::Point_2 Point_2;
typedef K::Segment_2 Segment_2;
using namespace std;
int main(int argc, char *argv[])
{
Point_2 points[5] = { Point_2(0.,0.), Point_2(10.,0.),Point_2(10.,10.),Point_2(6.,5.),Point_2(4.,1.) };
CGAL::Geomview_stream gv(CGAL::Bbox_3(-12, -12, -0.1, 12,12,0.1));
gv << CGAL::RED; // red points
for (int i = 0; i <= 2; ++i)
{
gv << points[i];
}
gv << CGAL::BLUE;// bluepoints
for (int i = 3; i <= 4; ++i)
{
gv << points[i];
}
// segments between some points
gv << CGAL::BLACK;
Segment_2 AB = Segment_2(points[0],points[1]);
gv << CGAL::YELLOW << AB ;
Segment_2 CD = Segment_2(points[1],points[2]);
gv << CGAL::BLUE << CD ;
sleep(300);
return 0;
}
The current trend among CGAL developers is to use Qt framework and associated visualisation tools such as QGLViewer rather than Geomview which are more recent, fully portative and allows you to do much more (especially if you want to make a demo for your algorithms with user interactions).
If you want to do 3D visualisation with CGAL I will advise you to use QGLViewer as they are already a lot of demos in CGAL that uses that library. For instance as an entry point, I will suggest you to have a look to Alpha_shape_3 demo. The code of this demo is quite light and straightforward, you can easily add new features without understanding the whole Qt framework first (you may have to eventually but that way the learning curve will be less steep and you can quickly start implementing stuff).
If you want to do 2D visualisation, you may have a look to the Alpha_shape_2 demo and use QPainter from Qt (note that you can combine both 3d and 2d viewer in QGL viewer as shown in this example.
Let me first state that I just want direction, not necessarily actual code, unless a small snippet is the only way to get the point across.
I need to create a DIRECTED graph data structure using an adjacency list or matrix in C++, and add vertices/edges from standard input, which means dynamically.
I think I'd be able to create a graph fine if I was able to instantiate a set of Vertices first, then create edges and add them to the graph, but I don't understand how it is possible to add an edge which contains a vertex that hasn't been instantiated yet.
for example, the first line from standard input reads:
Miami -> New York/1100 -> Washington/1000 -> albuquerque/1700
How am I supposed to add an edge from Miami to New York if the New York vertex hasn't been added to the graph yet?
Thanks for the direction everyone!
how it is possible to add an edge which
contains a vertex that hasn't been instantiated yet.
Simple: instantiate it..
I do not see any issue with this. Assume V to be the vertex set seen so far. V is initially empty. As you read the input x->y, you get its end points (x and y). If any one of them is not instantiated (i.e., not in V), you instantiate it and add it to the vertex set.
Another way to look to it: imagine we are defining the graph by its edge set E. By definition any edge is a pair of vertices which in turn defines the vertex set of the graph.
How about you resize the adjacency list each time a new unique node comes in? You can maintain a set of unique node values and use its size to adjust the size of the adjacency list each time you have to add a node. Below is a some code that does the same.
class Graph
{
public:
// Add links in the graph
void addLink(int id1, int id2){
// Add to hashset
uniqueNodes.insert(id1);
uniqueNodes.insert(id2);
// Resize on the adjacency list based on how many nodes exists in the uniqueNodes set
adjList.resize(uniqueNodes.size());
// Make the connections assuming undirected graph
adjList[id1].push_back(id2);
adjList[id2].push_back(id1);
}
// Print the graph
void printGraph(){
for(int i = 0; i < adjList.size(); i++){
cout << i << ":";
for(auto it = adjList[i].begin(); it != adjList[i].end(); it++)
cout << *it << "->";
cout << "NULL\n";
}
}
private:
// Adjacency list for the graph
vector<list<int>> adjList;
// Hashset to help define the size of the adjacency list as nodes come in
set<int> uniqueNodes;
};