So I'm posting this because I'm currently working on an algorithm project and might gonna use the boost library for building a graph from an input text file. So I have noticed that there is a descriptor for the vertex in a graph, but since I have a very large graph to be built, do I need to allocate a descriptor for every vertex in that graph? If I don't, can I traverse the whole graph after it's been built?
I'm a newbie to the boost library and this is a little emergent so if anybody can explain this I will be very grateful!
A vertex descriptor describes a vertex (in cheap, graph model independent way).
A graph model describes a graph.
Iterators
When you want to traverse a graph, you traverse the graph, using the iterators:
typedef boost::adjacency_list<> Graph;
typedef Graph::vertex_iterator Vit;
Vit begin, end;
boost::tie(begin, end) = vertices(g);
Dereferencing a valid iterator gives you the descriptor (same thing goes for edge iterators).
Simple demo:
Live On Coliru
#include <boost/graph/adjacency_list.hpp>
#include <iostream>
int main () {
typedef boost::adjacency_list<> Graph;
typedef Graph::vertex_iterator Vit;
Graph g(10);
add_edge(2,5,g);
add_edge(5,3,g);
add_edge(3,8,g);
Vit begin, end;
boost::tie(begin, end) = vertices(g);
for (Vit it = begin; it != end; ++it) {
unsigned edges = out_degree(*it, g);
if (edges)
std::cout << "vertex #" << *it << " has " << edges << " outgoing edge(s)\n";
}
}
Prints:
vertex #2 has 1 outgoing edge(s)
vertex #3 has 1 outgoing edge(s)
vertex #5 has 1 outgoing edge(s)
Graph Model With Node-Based Vertex Containers
Not all graph have integral vertex descriptors, so adding edges becomes more complicated, and printing them doesn't look so "friendly"
Live On Coliru
#include <boost/graph/adjacency_list.hpp>
#include <iostream>
int main () {
typedef boost::adjacency_list<boost::setS, boost::listS/*, boost::undirectedS*/> Graph;
typedef Graph::vertex_iterator Vit;
Graph g(10);
Vit begin, end;
boost::tie(begin, end) = vertices(g);
{
std::vector<Graph::vertex_descriptor> vindex(begin, end);
add_edge(vindex[2], vindex[5], g);
add_edge(vindex[5], vindex[3], g);
add_edge(vindex[3], vindex[8], g);
}
for (Vit it = begin; it != end; ++it) {
unsigned edges = out_degree(*it, g);
if (edges)
std::cout << "vertex #" << *it << " has " << edges << " outgoing edge(s)\n";
}
}
Printing
vertex #0x994d00 has 1 outgoing edge(s)
vertex #0x994d70 has 1 outgoing edge(s)
vertex #0x994e50 has 1 outgoing edge(s)
Properties
In such cases, consider adding a property bundle.
This way, arbitrary application-specific information can be attached to model vertices (or edges, or graphs).
The main downside, in my opinion, of this is that there's no natural indexing for the properties, so looking up a graph entity by its (bundled) property could get bad performance (linear search) unless you keep an extra index outside of the graph manually.
Live On Coliru
#include <boost/graph/adjacency_list.hpp>
#include <iostream>
struct VertexProperties {
std::string name;
VertexProperties(std::string name) : name(name) {}
};
int main () {
typedef boost::adjacency_list<boost::setS, boost::listS, boost::directedS, VertexProperties> Graph;
typedef Graph::vertex_iterator Vit;
Graph g;
add_vertex(VertexProperties ("zero"), g);
add_vertex(VertexProperties ("one"), g);
add_vertex(VertexProperties ("two"), g);
add_vertex(VertexProperties ("three"), g);
add_vertex(VertexProperties ("four"), g);
add_vertex(VertexProperties ("five"), g);
add_vertex(VertexProperties ("six"), g);
add_vertex(VertexProperties ("seven"), g);
add_vertex(VertexProperties ("eight"), g);
add_vertex(VertexProperties ("nine"), g);
Vit begin, end;
boost::tie(begin, end) = vertices(g);
{
std::vector<Graph::vertex_descriptor> vindex(begin, end);
add_edge(vindex[2], vindex[5], g);
add_edge(vindex[5], vindex[3], g);
add_edge(vindex[3], vindex[8], g);
}
for (Vit it = begin; it != end; ++it) {
unsigned edges = out_degree(*it, g);
if (edges)
std::cout << "vertex '" << g[*it].name << "' has " << edges << " outgoing edge(s)\n";
}
}
Prints
vertex 'two' has 1 outgoing edge(s)
vertex 'three' has 1 outgoing edge(s)
vertex 'five' has 1 outgoing edge(s)
Related
I am a Boost (and C++) newbie, going through the graph library tutorial. I can create a graph and give it vertices and edges. I would like to use maximum cardinality matching in Boost to return a set of edges that form the maximum matching in the graph.
I have looked through max_cardinality_matching.hpp, but am not quite sure how to use it, or which functions to use, to return the maximum matching set of edges.
Here is my code so far:
#include <iostream>
#include <boost/graph/max_cardinality_matching.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graph_traits.hpp>
using namespace boost;
int main(int argc, const char * argv[]) {
// create a typedef for the Graph type
typedef adjacency_list<vecS, vecS, bidirectionalS> Graph;
// Make convenient labels for the vertices
enum { A, B, C, D, E, F, N };
const int num_vertices = N;
// const char* name = "ABCDE";
// writing out the edges in the graph
typedef std::pair<int, int> Edge;
Edge edge_array[] =
{ Edge(A,B), Edge(B,C), Edge(C,D), Edge(D,E), Edge(E,F) };
// const int num_edges = sizeof(edge_array)/sizeof(edge_array[0]);
// declare a graph object and add the edges
Graph g(edge_array, edge_array + sizeof(edge_array) / sizeof(Edge), num_vertices);
// get the property map for vertex indices
// property_map<Graph, property type>
typedef property_map<Graph, vertex_index_t>::type IndexMap;
IndexMap index = get(vertex_index, g);
// Create an iterator for vertices
typedef graph_traits<Graph>::vertex_iterator vertex_iter;
std::cout << "vertices(g) = ";
// Vertices returns a pair of vertex iterators
// The first iter points to the beginning of the vertices
// The second points past the end
std::pair<vertex_iter, vertex_iter> vp;
// vertices() returns the vertices in graph g
for (vp = vertices(g); vp.first != vp.second; ++vp.first)
std::cout << index[*vp.first] << " ";
std::cout << std::endl;
graph_traits<Graph>::edge_iterator ei, ei_end;
std::cout << "edges(g) = ";
// For each tuple of vertices (an edge), till the end of the edge list ...
for (tie(ei, ei_end) = edges(g); ei != ei_end; ++ei)
// ... print out the source and target vertices in the edge
std::cout << "(" << index[source(*ei, g)] << "," << index[target(*ei, g)] << ") ";
std::cout << std::endl;
// Return the set of edges that form a maximum matching in graph g
return 0;
}
All you need to do is create read-write property map and pass it as second argument to
template <typename Graph, typename MateMap>
bool checked_edmonds_maximum_cardinality_matching(const Graph& g, MateMap mate);
You can create ordinary std::map where key and value are vertex descriptors and adapts it by associative_property_map in order to use it with checked_edmonds_maximum_cardinality_matching. From this map you can read all edges which create maximum cardinality of your graph.
// Return the set of edges that form a maximum matching in graph g
typedef graph_traits<Graph>::vertex_descriptor VD;
std::map<VD, VD> match;
boost::associative_property_map< std::map<VD,VD> > mapAdapter(match);
bool rc = checked_edmonds_maximum_cardinality_matching(g,mapAdapter);
if (rc)
{
std::set<graph_traits<Graph>::edge_descriptor> edges;
for (auto& i : match)
{
std::pair<Graph::edge_descriptor,bool> e = boost::edge(i.first,i.second,g);
if (e.second)
edges.insert(e.first);
std::cout << i.first << " is matched to " << i.second << std::endl;
}
// print edges
for (auto& e : edges)
std::cout << "edge: " << e << std::endl;
}
As output you can see:
0 is matched to 1
1 is matched to 0
2 is matched to 3
3 is matched to 2
4 is matched to 5
5 is matched to 4
edge: (0,1)
edge: (2,3)
edge: (4,5)
I am new to C++ and the boost graph library. I am trying to use iterators to access information already stored within my graph "lattuce", more specifically, the weight an edge between two specific nodes.
This data will then be used by a A* algorithm (not the one in Boost graph). I am not sure if iterators are the solution to this either, so any guidance or criticism would be appreciated.
struct Point {//struct point with vertex properties
int x, y;
int parentx, parenty;
double g;
double h;
friend std::ostream& operator<<(std::ostream& os, Point p) {
return os << "[" << p.x << "," << p.y << "]";
}
};
int main() {
//declarations
typedef property < edge_weight_t, double >Weight;
using std::vector;//?
using Graph = adjacency_list<setS, vecS, undirectedS, Point, Weight>;//graph includes our created point struct property<edge_weight_t
using vertex_descriptor = Graph::vertex_descriptor;
Graph lattuce;
//lattuce graph is created with weighted edges value 1 or 1,41 if diagonal. The functions used on a loop are:
//add_edge(nodes[p.x][p.y],nodes[neighbour.x][neighbour.y], Weight(1.0), lattuce);
//add_edge(nodes[p.x][p.y],nodes[neighbour.x][neighbour.y], Weight(1.4), lattuce);
If more information about the code that generates the graph is needed I'll provide it. Thanks
It is possible to obtain link edge weights in directed and undirected graphs by means of the boost::property_map:
boost::property_map<UndirectedGraph, boost::edge_weight_t>::type EdgeWeightMap = get(boost::edge_weight_t(), g);
Example implementation given below, that first builds the following simple graph (specifically a tree with no cycles):
... then uses the boost::property_map to obtain the weight of each edge, and prints it out:
#include <iostream>
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/adjacency_list.hpp>
typedef boost::property<boost::edge_weight_t, double> EdgeWeight;
typedef boost::adjacency_list<boost::listS, boost::vecS, boost::undirectedS, boost::no_property, EdgeWeight> UndirectedGraph;
typedef boost::graph_traits<UndirectedGraph>::edge_iterator edge_iterator;
int main(int, char*[])
{
// 1. Undirected graph - print out the edge weights
UndirectedGraph g;
boost::add_edge(0, 1, 8, g);
boost::add_edge(0, 5, 2, g);
boost::add_edge(5, 6, 1, g);
boost::add_edge(4, 5, 5, g);
boost::add_edge(3, 5, 7, g);
boost::property_map<UndirectedGraph, boost::edge_weight_t>::type EdgeWeightMap = get(boost::edge_weight_t(), g);
std::pair<edge_iterator, edge_iterator> edgePair;
for (edgePair = edges(g); edgePair.first != edgePair.second; ++edgePair.first)
{
std::cout << *edgePair.first << " " << EdgeWeightMap[*edgePair.first] << std::endl;
}
return 0;
}
Giving the following console output, showing the edges as (start,end) node pairs plus their respective weights:
I am working with a graphing using Boost library.
The graph is defined as follows.
typedef boost::adjacency_list<boost::setS,boost::setS,boost::undirectedS, uint32_t, float> AdjacencyList;
After creating the graph using appropriate data, in a separate function I want to print the adjacent_vertices of each vertex with their appropriate edge weight as computed in the beginning.
The creating part works well but when I want to extract adjacent vertices I dont get the values.
typedef boost::graph_traits<AdjacencyList>::adjacency_iterator AdjacencyIterator;
AdjacencyList::vertex_iterator i, end;
for (boost::tie(i, end) = boost::vertices(adjacency_list); i != end; i++) {
AdjacencyIterator ai, a_end;
boost::tie(ai, a_end) = boost::adjacent_vertices( *i, adjacency_list);
for (; ai != a_end; ai++) {
std::cout << *ai << "\t";
}
}
The Output I get are memory address in Hexademial number.
How can I get vertex indices and the edge weight?
You should access the property bundles, either using the graph's operator[] with the vertex/edge descriptor, or using the property map:
Using the operator[]
Live On Coliru
#include <boost/graph/adjacency_list.hpp>
#include <iostream>
typedef boost::adjacency_list<boost::setS,boost::setS,boost::undirectedS, uint32_t, float> AdjacencyList;
typedef boost::graph_traits<AdjacencyList>::adjacency_iterator AdjacencyIterator;
int main() {
AdjacencyList adjacency_list;;
boost::add_edge(
boost::add_vertex(10, adjacency_list),
boost::add_vertex(20, adjacency_list),
1.5f,
adjacency_list
);
boost::add_edge(
boost::add_vertex(30, adjacency_list),
boost::add_vertex(40, adjacency_list),
2.5f,
adjacency_list
);
AdjacencyList::vertex_iterator i, end;
for (boost::tie(i, end) = boost::vertices(adjacency_list); i != end; i++) {
AdjacencyIterator ai, a_end;
boost::tie(ai, a_end) = boost::adjacent_vertices(*i, adjacency_list);
for (; ai != a_end; ai++) {
std::cout << adjacency_list[*ai] << "\t";
}
}
}
Output:
10 20 30 40
Using the property map:
boost::property_map<AdjacencyList, boost::vertex_bundle_t>::type pmap = boost::get(boost::vertex_bundle, adjacency_list);
Now you can use boost::get(pmap, vertex_descriptor1) to access the vertex property bundle
I am trying to write an algorithm to (greedily) find the chromatic number of a graph. For this I need to be able to query the adjacent vertices of a given vertex.
My function is the following:
int Network::greedy_colouring() {
// create an undirected graph with the vertices and edges of the first one
UndirectedGraph g;
copy_graph(network, g);
int vertices_amount = num_vertices(g);
// Assign the first color to first vertex
std::map<std::string, int> vertex_colouring;
vertex_pair_iterators vp = vertices(g);
vertex_colouring[g[*vp.first].name] = 0;
++vp.first; // start from second vertex
for (; vp.first != vp.second; ++vp.first)
vertex_colouring[g[*vp.first].name] = -1;
// A temporary array to store the available colors. True
// value of available[cr] would mean that the color cr is
// assigned to one of its adjacent vertices
bool available[vertices_amount];
for (int cr = 0; cr < vertices_amount; cr++)
available[cr] = false;
// Assign colors to remaining V-1 vertices
vp = vertices(g); // reset to beginning
++vp.first; // start from second vertex
for (; vp.first != vp.second; ++vp.first) {
// Process all adjacent vertices and flag their colors
// as unavailable
for (std::pair<adjacency_it, adjacency_it> neighbours = boost::adjacent_vertices(g[*vp.first], g);
neighbours.first != neighbours.second; ++neighbours.first)
if (vertex_colouring[g[*neighbours.first].name] != -1)
available[vertex_colouring[g[*neighbours.first].name]] = true;
// Find the first available color
int cr;
for (cr = 0; cr < vertices_amount; cr++)
if (available[cr] == false)
break;
vertex_colouring[g[*vp.first].name] = cr; // Assign the found color
// Reset the values back to false for the next iteration
neighbours = boost::adjacent_vertices(g[*vp.first], g); // reset to beginning
for (; neighbours.first != neighbours.second; ++neighbours.first)
if (vertex_colouring[g[*neighbours.first].name] != -1)
available[vertex_colouring[g[*neighbours.first].name]] = false;
}
// print the result and find colour number
unsigned colour_number = 0;
for (std::map<std::string, int>::iterator it = vertex_colouring.begin(); it != vertex_colouring.end(); ++it) {
std::cout << "Vertex " << it->first << " ---> Color " << it->second << std::endl;
if (it->second > colour_number)
colour_number = it->second;
}
return colour_number;
}
The error I get is related to the call to:
std::pair<adjacency_it, adjacency_it> neighbours = boost::adjacent_vertices(g[*vp.first],g)
Which gives the following compile error: "error: no matching function for call to ‘boost::adjacency_iterator ... " (partial copy).
Commenting out the code related to the function adjacency lets it compile, so I am sure that this is the problem code.
Some typedefs that are being used in the function:
typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::bidirectionalS, Vertex, Edge > Graph;
typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS, Vertex, Edge > UndirectedGraph;
typedef std::pair<Vertex ,Vertex > vert_p;
typedef boost::graph_traits<Graph>::vertex_descriptor vertex_t;
typedef std::pair<boost::graph_traits<Graph>::edge_descriptor, bool> edge_t;
typedef boost::graph_traits<Graph>::in_edge_iterator in_edge_it;
typedef boost::graph_traits<Graph>::vertex_iterator vertex_iter;
typedef boost::graph_traits<Graph>::edge_iterator edge_iter;
typedef boost::property_map<Graph, boost::vertex_index_t>::type IndexMap;
typedef std::pair<vertex_iter, vertex_iter> vertex_pair_iterators;
typedef std::pair<in_edge_it, in_edge_it> edge_pair_iterators;
typedef boost::graph_traits<Graph>::adjacency_iterator adjacency_it;
Can anyone give me a clue what I am doing wrong?
Two issues:
the first argument needs to be a vertex descriptor, not the property bundle. Change
boost::adjacent_vertices(g[*vp.first], g)
into
boost::adjacent_vertices(*vp.first, g)
the return type is std::pair<adjacency_iterator, adjacency_iterator>. However, you defined adjacency_iterator as
typedef boost::graph_traits<Graph>::adjacency_iterator adjacency_it;
when it needs to be
typedef boost::graph_traits<UndirectedGraph>::adjacency_iterator adjacency_it;
Further notes:
It's easier to work with separate iterators instead of vp.first and vp.second (use boost::tie to assign both at once)
You have a "poisonous" unsigned value in your comparison, write it explicitly as
if(it->second > static_cast<int>(colour_number))
Or review the logic with possible -1 values in the map.
it's likely very inefficient to keep the colour map indexed by Vertex::name (which is a string). You should consider indexing by vertex_descriptor.
Now, since your vertex model uses vecS for the VertexContainer, you could actually use the fact that this descriptor is an integral index in the range [0, num_vertices(g)).
Therefore you can replace the map<> (which has bad memory locality) with a vector<int> (where the vertex descriptor is the vector index).
If you want to support other graph models, you can let the caller pass in an IndexMap that maps vertex-descriptor to similar consecutive indices. Lots of algorithms in the BGL use this approach.
Obviously, bool[] could (should) be std::bitset or even std::vector<bool>. Boost has the dynamic_bitset which is probably best here.
(I'd need to understand your algorithm a lot better. Perhaps a set of "taken" colour would be even better. And implemented as an unsorted contiguous collection for speed, unless you anticipate the number of colour to get big enough that an ordered/hash lookup would be faster (?!).
Always make your code selfcontained:
Live On Coliru
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/copy.hpp>
#include <iostream>
struct Vertex {
std::string name;
};
struct Edge {
};
typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::bidirectionalS, Vertex, Edge > Graph;
typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS, Vertex, Edge > UndirectedGraph;
Graph network;
int greedy_colouring() {
using namespace boost;
typedef boost::graph_traits<UndirectedGraph>::vertex_descriptor vertex_descriptor;
static_assert(is_integral<vertex_descriptor>::value, "IndexMap not provided yet TODO");
typedef boost::graph_traits<UndirectedGraph>::vertex_iterator vertex_iter;
typedef boost::graph_traits<UndirectedGraph>::adjacency_iterator adjacency_it;
// create an undirected graph with the vertices and edges of the first one
UndirectedGraph g;
copy_graph(network, g);
vertex_iter vit, vend;
tie(vit, vend) = vertices(g);
size_t const vertices_amount = num_vertices(g);
std::vector<int> vertex_colouring(vertices_amount, -1);
vertex_colouring[*vit] = 0; // Assign the first color to first vertex
// A temporary array to store the available colors.
// - available[cr]: assigned to one of its adjacent vertices
std::vector<bool> available(vertices_amount, false);
for (++vit; vit!=vend; ++vit)
{
// Process all adjacent vertices and flag their colors as unavailable
adjacency_it neighbour, neighbour_end;
for (tie(neighbour, neighbour_end) = adjacent_vertices(*vit, g); neighbour != neighbour_end; ++neighbour)
if (vertex_colouring[*neighbour] != -1)
available[vertex_colouring[*neighbour]] = true;
// Find the first available color
vertex_colouring[*vit] = distance(available.begin(), std::find(available.begin(), available.end(), false));
// Reset the values back to false for the next iteration
for (tie(neighbour, neighbour_end) = adjacent_vertices(*vit, g); neighbour != neighbour_end; ++neighbour)
if (vertex_colouring[*neighbour] != -1)
available[vertex_colouring[*neighbour]] = false;
}
// print the result and find colour number
for (vertex_descriptor v = 0; v < vertices_amount; ++v)
std::cout << "Vertex " << v << " ---> Color " << vertex_colouring[v] << std::endl;
return *std::max_element(vertex_colouring.begin(), vertex_colouring.end());
}
int main() { }
I have a set of edges E, and I want to know if I can safely remove edge i in E, meaning if I remove it from the graph, the graph should still be connected.
In my understanding that implies that edge i, has to lie on a circle.
The output should be a list of indices of all edges I can't remove.
The problem:
My different solutions seem to do the right thing, but are far too slow (inefficient).
One of my solutions was:
1. Loop through all edges i in E
2. Loop through all edges x in V
3. Add edge x to the graph (excluding edge i) until nodes of i are connected or end reached
4. If no connection possible, edge is not removable and added to the list
This way was way too slow.
I then decided to rewrite my code and use breadth-first-search to look if another path is possible without edge i.
I thought it would be performant enough, but it seems it's not. Either I have implemented in a very bad way or that's also the wrong algorithm for this task.
Here is the algorithm in the C++ code I have (removed some not important parts):
struct connection {
int a, b;
};
void expand(int x, connection *&arr, std::set<int> &exp, int size) {
for (int i = 0; i < size; i++) {
if (x == arr[i].a) {
exp.insert(arr[i].b);
}
else if (x == arr[i].b) {
exp.insert(arr[i].a);
}
}
return;
}
// recursive breadth-first-seach
bool BFSr(std::set<int> &group, std::set<int> &visited, int goal, connection *&arr, int size) {
if (group.empty()) return false;
if (group.find(goal) != group.end()) return true;
std::set<int> tempa;
for (std::set<int>::iterator it = group.begin(); it != group.end(); ++it) {
expand(*it, arr, tempa size);
}
for (std::set<int>::iterator it = visited.begin(); it != visited.end(); ++it) {
tempa.erase(*it);
}
tempb = visited;
tempb.insert(group.begin(), group.end());
return BFSr(tempa, tempb, goal, arr, size);
}
bool BFS(int start, int goal, connection *&arr, int size) {
std::set<int> tempa;
std::set<int> tempb;
tempa.insert(start);
return BFSr(tempa, tempb, goal, arr, size);
}
int main()
{
connection *arr = new connection[m];
connection *con = new connection[m - 1];
// fill arr with connections arr.a < arr.b ....
for (int j = 0; j < (m - 1); j++) {
con[j] = arr[j + 1];
}
// 1. edge for performance reasons extra
if (!BFS(arr[0].a, arr[0].b, con, (m - 1))) {
// connection is important therefore add to list
printf(" %d", 1);
}
// Look if nodes still connected after removing connection
for (int s = 1; s < m; s++) {
con[s - 1] = arr[s - 1];
if (!BFS(arr[s].a, arr[s].b, con, (m-1))) {
// connection is important therefore add to list
printf(" %d", s + 1);
}
}
printf("\n");
free(arr);
free(con);
return 0;
}
Do you know any solutions for me to make it faster, or do you know a better algorithm for my problem?
An edge whose deletion disconnects two connected components is called a bridge and there are linear-time algorithms for finding all the bridges in a graph (usually based on depth-first search). The Wikipedia article lists one of them (due to Tarjan) as an example. This paper also gives a simple algorithm for listing all the bridges in a graph and seems to be a bit simpler than Tarjan's algorithm.
Hope this helps!
Here is another version of your algorithm (I guess you will get industry standard optimization implementation of graph and various algorithm for free):
I am referring to this image as graph model.
Gist (from this post)
find articulation points
all points with single out-edge are
articulation points (boost graph won't return these)- these edges
are automatically bridging edges
for each articulation point-
loop over each outedge, if outedge already not bridging edge- then
remove the edge and check for graph components and add the edge back
again
At the end it will print Edge(a,g) connects components in graph
#include <iostream>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/biconnected_components.hpp>
#include <boost/graph/connected_components.hpp>
#include <functional>
#include <string>
#include <vector>
#include <unordered_map>
#include <unordered_set>
typedef boost::adjacency_list <boost::vecS, boost::vecS, boost::undirectedS> Graph;
typedef boost::graph_traits<Graph>::vertex_descriptor vertex_t;
typedef boost::graph_traits<Graph>::edge_descriptor edge_t;
// reference:
// http://lists.boost.org/boost-users/2005/08/13098.php
//
struct edge_t_hasher
{
std::size_t operator()(const edge_t& e) const
{
auto prop = e.get_property();
std::hash<decltype(prop)> hasher;
return hasher(prop);
}
};
typedef std::unordered_set<edge_t, edge_t_hasher> UnorderedBoostEdgeSet;
Graph getGraph()
{
Graph g;
vertex_t aVtx = boost::add_vertex(g);
vertex_t bVtx = boost::add_vertex(g);
vertex_t cVtx = boost::add_vertex(g);
vertex_t dVtx = boost::add_vertex(g);
vertex_t eVtx = boost::add_vertex(g);
vertex_t fVtx = boost::add_vertex(g);
vertex_t gVtx = boost::add_vertex(g);
vertex_t hVtx = boost::add_vertex(g);
vertex_t iVtx = boost::add_vertex(g);
boost::add_edge(dVtx, cVtx, g);
boost::add_edge(dVtx, bVtx, g);
boost::add_edge(cVtx, bVtx, g);
boost::add_edge(aVtx, bVtx, g);
boost::add_edge(bVtx, eVtx, g);
boost::add_edge(eVtx, fVtx, g);
boost::add_edge(aVtx, fVtx, g);
boost::add_edge(aVtx, gVtx, g);// edge connecting components
boost::add_edge(gVtx, iVtx, g);
boost::add_edge(gVtx, hVtx, g);
boost::add_edge(hVtx, iVtx, g);
return g;
}
UnorderedBoostEdgeSet bridgingEdgesForGraph(const Graph& graph)
{
UnorderedBoostEdgeSet bridgeEdges;
std::unordered_set<vertex_t> articulationVertices;
boost::articulation_points(graph, std::inserter(articulationVertices, articulationVertices.end()));
// add all the single connected vertices to the articulation vertices
auto vtxIters = boost::vertices(graph);
for (auto it = vtxIters.first, end = vtxIters.second; it != end; ++it)
{
if (boost::out_degree(*it, graph) == 1)
bridgeEdges.insert(*(boost::out_edges(*it, graph).first));
}
std::vector<vertex_t> componentsInGraph(boost::num_vertices(graph));
int numComponentsInGraph = boost::connected_components(graph, &componentsInGraph[0]);
// for each articulation vertex now get edges and check if removing that
// edge causes graph change in connected components
//
// copy the graph- so we can iterate over the outeges of vertices
// we will be fiddling with the copy- since the vtx descriptors are
// ints- they stay same across copy and removing edge operation
auto graph2 = graph;
for (auto vtx : articulationVertices)
{
auto outEdges = boost::out_edges(vtx, graph);
for (auto it = outEdges.first, end = outEdges.second; it != end; ++it)
{
auto edge = *it;
if (bridgeEdges.find(edge) != bridgeEdges.end())
continue;
// map this edge to graph2 edge- for removal and eventual addition
auto src = boost::source(edge, graph);
auto tgt = boost::target(edge, graph);
auto edge2 = boost::edge(src, tgt, graph2).first;
boost::remove_edge(edge2, graph2);
std::vector<vertex_t> componentsInGraph2(boost::num_vertices(graph2));
int numComponentsInGraph2 = boost::connected_components(graph2, &componentsInGraph2[0]);
// bridging edge- graph #components changed
if (numComponentsInGraph != numComponentsInGraph2)
bridgeEdges.insert(edge);
// add the edge back to graph2
boost::add_edge(src, tgt, graph2);
}
}
return bridgeEdges;
}
int main()
{
const Graph& graph = getGraph();
const auto& bridgingEdges = bridgingEdgesForGraph(graph);
const char* array = {"ABCDEFGHI"};
for (auto edge : bridgingEdges)
{
std::cout << "Edge(" << array[boost::source(edge, graph)] << ", " << array[boost::target(edge, graph)]
<< ") is a bridging edge" << std::endl;
}
return 0;
}
Meanwhile I found out how those special edges are called: Bridges.
And therefore I found a site giving an DFS algorithm for finding all bridges.
It's fast enough for my purposes.
DFS algorithm for finding Bridges in a undirected Graph
Thank you Sarang, your post made me find the correct search words for the site.