boost graph metric_tsp_approx solution not following graph edges - c++

I am trying to solve the traveling salesmen problem on a graph created using boost::adjacency_list. I am using the metric_tsp_approx to solve the tsp.
The problem I am facing is that the solution does not follow the graph edges. The solution connects vertices in the graph which are not directly connected. I wanted to know if this is how the library works or if I am doing something wrong. The solution does not look correct either. I had 4 vertices forming a square, the solution should have been going along the perimeter, but it was going along the diagonal instead. There was no edge along the diagonal.
This is my adjacency_list:
boost::adjacency_list<boost::setS, boost::listS, boost::undirectedS,
boost::property<boost::vertex_index_t, int>,
boost::property<boost::edge_weight_t, double>,
boost::no_property>
Add vertex and add edge functions:
boost::add_vertex(id, graph);
boost::add_edge(id1, id2, weight, graph); // weight is euclidean distance
TSP solver:
std::vector<VertexDescriptor> tsp_path; //VertexDescriptor is adjacency_list::vertex_descriptor
metric_tsp_approx_tour(graph, back_inserter(tsp_path));
I also tried passing the weightmap to the metric_tsp_approx_tour but the same problem persists.
Can someone help me solve this? If the boost metric_tsp_approx_tour does not consider the edges of the graph, is there a way to make it consider them?

The docs: https://www.boost.org/doc/libs/1_74_0/libs/graph/doc/metric_tsp_approx.html
This is a traveling salesperson heuristic for generating a tour of vertices for a fully connected undirected graph with weighted edges that obey the triangle inequality.
(emphasis mine)
The "fully connected graph" clause does state that all vertices are assumed to be connected.
Note as well, the vertex index is assumed to map to [0,num_vertices(graph)).
BONUS
As a bonus I tried to work out a minimal working example for the algorithm. It does seem to work as advertised:
Live On Coliru
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graph_utility.hpp>
#include <boost/graph/metric_tsp_approx.hpp>
using Graph =
boost::adjacency_list<boost::setS, boost::listS, boost::undirectedS,
boost::property<boost::vertex_index_t, int>,
boost::property<boost::edge_weight_t, double>,
boost::no_property>;
using VertexDescriptor = Graph::vertex_descriptor;
int main() {
std::vector const points { std::pair
{ 4., 9. }, // these can be randomized
{ 2., 6. },
{ 4., 1. },
{ 1., 1. },
};
Graph graph;
for (auto i = 0u; i < points.size(); ++i) {
add_vertex(i, graph);
}
for (auto i = 0u; i < points.size(); ++i) {
auto va = vertex(i, graph);
// undirected, so only need traverse higher vertices for connections
for (auto j = i+1; j < points.size(); ++j) {
auto vb = vertex(j, graph);
auto const [ax, ay] = points.at(i);
auto const [bx, by] = points.at(j);
auto const dx = bx - ax;
auto const dy = by - ay;
add_edge(va, vb, sqrt(dx*dx + dy*dy), graph); // weight is euclidean distance
}
}
print_graph(graph);
std::vector<VertexDescriptor> tsp_path(num_vertices(graph)); //VertexDescriptor is adjacency_list::vertex_descriptor
metric_tsp_approx_tour(graph, back_inserter(tsp_path));
auto idmap = get(boost::vertex_index, graph);
for (auto vd : tsp_path) {
if (vd != graph.null_vertex()) {
auto [x,y] = points.at(idmap[vd]);
std::cout << " {" << x << "," << y << "}";
}
}
}
Prints
0 <--> 1 2 3
1 <--> 0 2 3
2 <--> 0 1 3
3 <--> 0 1 2
{4,9} {2,6} {1,1} {4,1} {4,9}

Related

How to use Boost's Kruskal MST algorithm on CGAL's Triangulation_3?

I've been trying to puzzle out how to form edge descriptors for a CGAL Triangulation_3 such that I can use Boost's implementation of Kruskal's Minimum Spanning Tree on that Triangulation.
I have been reading through the reference documentation for a Triangulation_2 (provided here), but have observed that no implementation exists for boost::graph_traits<Triangulation_3>. While puzzling it out, I found that I could possibly provide my own implementation for edge descriptors through an adjacency list as shown in Boost's example for a Kruskal MST, but got lost and confused at this step, and didn't know if that would be a sufficient approach.
Ultimately, it seems that what I need to do is create a boost Graph implementation, but am lost at what resources I need to accomplish this step. From there, the desired use is to be able to traverse this MST to perform graph-based min-cuts at specific edges matching a predicate.
// EDIT :>
My current attempt revolves around creating the EMST via pushing simplex edges defined as a pair of vertex iterate indices, with weights defined as euclidean distance between vertices (a Point_3 with data attached), using the Graph construction shown in the Boost example.
My hope is to have BGL vertices (as a Point_3 with color information attached) be connected by BGL edges (as a simplex[!] edge after the triangulation). My ultimate use just requires that I traverse some sort of contiguous spatial ordering of my Point_3's (with RGB info), and split estimated planes into "patches" which meet a max-distance (complete linkage?) threshold, or a within-patch distance variance. It's not exactly segmentation, but similar.
// some defns...
using RGBA = std::array<uint16_t, 3>;
using PointData = boost::tuple<
Point_3, // Point location; Easting-Altitude-Northing
Vector_3, // Estimated Normal Vector at Point
RGBA, // Photo Color
RGBA, // RANSAC Shape Colorization
size_t, // Estimated Patch ID
RGBA>; // Estimated Patch Colorization
//
// Some operations on the points and RANSAC estimation occurs here
//
// iterate through shapes
while (it != shapes.end()) {
boost::shared_ptr<EfficientRANSAC::Shape> shape = *it;
std::cout << (*it)->info() << std::endl;
// generate a random color code for this shape
RGBA rgb;
for (int i=0; i<3; i++) {
rgb[i] = rand()%256;
}
// Form triangulation to later convert into Graph representation
using VertexInfoBase = CGAL::Triangulation_vertex_base_with_info_3<
PointData,
Kernel
>;
using TriTraits = CGAL::Triangulation_data_structure_3<
VertexInfoBase,
CGAL::Delaunay_triangulation_cell_base_3<Kernel>,
CGAL::Parallel_tag
>;
using Triangulation_3 = CGAL::Delaunay_triangulation_3<Kernel, TriTraits>;
Triangulation_3 tr;
// Iterate through point indices assigned to each detected shape.
std::vector<std::size_t>::const_iterator
index_it = (*it)->indices_of_assigned_points().begin();
while (index_it != (*it)->indices_of_assigned_points().end()) {
PointData& p = *(points.begin() + (*index_it));
// assign shape diagnostic color info
boost::get<3>(p) = rgb;
// insert Point_3 data for triangulation and attach PointData info
auto vertex = tr.insert(boost::get<0>(p));
vertex->info() = p;
index_it++; // next assigned point
}
std::cout << "Found triangulation with: \n\t" <<
tr.number_of_vertices() << "\tvertices\n\t" <<
tr.number_of_edges() << "\tedges\n\t" <<
tr.number_of_facets() << "\tfacets" << std::endl;
// build a Graph out of the triangulation that we can do a Minimum-Spanning-Tree on
using Graph = boost::adjacency_list<
boost::vecS, // OutEdgeList
boost::vecS, // VertexList
boost::undirectedS, // Directed
boost::no_property, // VertexProperties
boost::property< boost::edge_weight_t, int >, // EdgeProperties
boost::no_property, // GraphProperties
boost::listS // EdgeList
>;
using Edge = boost::graph_traits<Graph>::edge_descriptor;
using E = std::pair< size_t, size_t >; // <: TODO - should be iterator index of vertex in Triangulation_3 instead of size_t?
std::vector<E> edge_array; // edges should be between Point_3's with attached RGBA photocolor info.
// It is necessary to later access both the Point_3 and RGBA info for vertices after operations are performed on the EMST
std::vector<float> weights; // weights are `std::sqrt(CGAL::squared_distance(...))` between these Point_3's
// Question(?) :> Should be iterating over "finite" edges here?
for (auto edge : tr.all_edges()) {
// insert simplex (!!) edge (between-vertices) here
edge_array.push_back(...);
// generate weight using std::sqrt(CGAL::squared_distance(...))
weights.push_back(...);
}
// build Graph from `edge_array` and `weights`
Graph g(...);
// build Euclidean-Minimum-Spanning-Tree (EMST) as list of simplex edges between vertices
std::list<E> emst;
boost::kruskal_minimum_spanning_tree(...);
// - traverse EMST from start of list, performing "cuts" into "patches" when we have hit
// max patch distance (euclidean) from current "first" vertex of "patch".
// - have to be able to access Triangulation_3 vertex info (via `locate`?) here
// - foreach collection of PointData in patch, assign `patch_id` and diagnostic color info,
// then commit individual serialized "patches" collections of Point_3 and RGBA photocolor to database
todo!();
it++; // next shape
}
The end goal of traversing each of the shapes using a Minimum Spanning Tree via Triangulation is to break each of the RANSAC estimated shapes into chunks, for other purposes. Picture example:
Do you want the graph of vertices and edges, or the graph of the dual, that is the tetrahedra would be BGL vertices and the faces between tetrahedra would be BGL edges?
For both it is not that hard to write the specialization of the graph traits class and the some free functions to navigate. Get inspired by the code for the 2D version for the graph_traits
Ultimately, it seems that what I need to do is create a boost Graph implementation, but am lost at what resources I need to accomplish this step.
The algorithm documents the concept requirements:
You can zoom in on the implications here: VertexListGraph and EdgeListGraph.
I found that I could possibly provide my own implementation for edge descriptors through an adjacency list as shown in Boost's example for a Kruskal MST, but got lost and confused at this step, and didn't know if that would be a sufficient approach.
It would be fine to show your attempt as a question, because it would help us know where you are stuck. Right now there is really no code to "go at", so I'll happily await a newer, more concrete question.
I was able to find an attempt at an answer. I added another property to my Point collection implementation (that included the index of that point in an array), and used this to iterate over edges in the triangulation to build the Graph, before running the EMST algorithm on it.
However, the real answer is don't do this -- it still is not working correctly (incorrect number of edges, including infinite vertices in the edge list, and other problems).
// Form triangulation to later convert into Graph representation
using VertexInfoBase = CGAL::Triangulation_vertex_base_with_info_3<
PointData,
Kernel
>;
using TriTraits = CGAL::Triangulation_data_structure_3<
VertexInfoBase,
CGAL::Delaunay_triangulation_cell_base_3<Kernel>,
CGAL::Parallel_tag
>;
using Triangulation_3 = CGAL::Delaunay_triangulation_3<Kernel, TriTraits>;
Triangulation_3 tr;
// Iterate through point indices assigned to each detected shape.
std::vector<std::size_t>::const_iterator
index_it = (*it)->indices_of_assigned_points().begin();
while (index_it != (*it)->indices_of_assigned_points().end()) {
PointData& p = *(points.begin() + (*index_it));
// assign shape diagnostic color info
boost::get<3>(p) = rgb;
// insert Point_3 data for triangulation and attach PointData info
TriTraits::Vertex_handle vertex = tr.insert(boost::get<0>(p));
vertex->info() = p;
index_it++; // next assigned point
}
std::cout << "Found triangulation with: \n\t" <<
tr.number_of_vertices() << "\tvertices\n\t" <<
tr.number_of_edges() << "\tedges\n\t" <<
tr.number_of_facets() << "\tfacets" << std::endl;
// build a Graph out of the triangulation that we can do a Minimum-Spanning-Tree on
// examples taken from https://www.boost.org/doc/libs/1_80_0/libs/graph/example/kruskal-example.cpp
using Graph = boost::adjacency_list<
boost::vecS, // OutEdgeList
boost::vecS, // VertexList
boost::undirectedS, // Directed
boost::no_property, // VertexProperties
boost::property< boost::edge_weight_t, double > // EdgeProperties
>;
using Edge = boost::graph_traits<Graph>::edge_descriptor;
using E = std::pair< size_t, size_t >; // <: TODO - should be iterator index of vertex in Triangulation_3 instead of size_t?
Graph g(tr.number_of_vertices());
boost::property_map< Graph, boost::edge_weight_t >::type weightmap = boost::get(boost::edge_weight, g);
// iterate over finite edges in the triangle, and add these
for (
Triangulation_3::Finite_edges_iterator eit = tr.finite_edges_begin();
eit != tr.finite_edges_end();
eit++
)
{
Triangulation_3::Segment s = tr.segment(*eit);
Point_3 vtx = s.point(0);
Point_3 n_vtx = s.point(1);
// locate the (*eit), get vertex handles?
// from https://www.appsloveworld.com/cplus/100/204/how-to-get-the-source-and-target-points-from-edge-iterator-in-cgal
Triangulation_3::Vertex_handle vh1 = eit->first->vertex((eit->second + 1) % 3);
Triangulation_3::Vertex_handle vh2 = eit->first->vertex((eit->second + 2) % 3);
double weight = std::sqrt(CGAL::squared_distance(vtx, n_vtx));
Edge e;
bool inserted;
boost::tie(e, inserted)
= boost::add_edge(
boost::get<6>(vh1->info()),
boost::get<6>(vh2->info()),
g
);
weightmap[e] = weight;
}
// build Euclidean-Minimum-Spanning-Tree (EMST) as list of simplex edges between vertices
//boost::property_map<Graph, boost::edge_weight_t>::type weight = boost::get(boost::edge_weight, g);
std::vector<Edge> spanning_tree;
boost::kruskal_minimum_spanning_tree(g, std::back_inserter(spanning_tree));
// we can use something like a hash table to go from source -> target
// for each of the edges, making traversal easier.
// from there, we can keep track or eventually find a source "key" which
// does not correspond to any target "key" within the table
std::unordered_map< size_t, std::vector<size_t> > map = {};
// iterate minimum spanning tree to build unordered_map (hashtable)
std::cout << "Found minimum spanning tree of " << spanning_tree.size() << " edges for #vertices " << tr.number_of_vertices() << std::endl;
for (std::vector< Edge >::iterator ei = spanning_tree.begin();
ei != spanning_tree.end(); ++ei)
{
size_t source = boost::source(*ei, g);
size_t target = boost::target(*ei, g);
// << " with weight of " << weightmap[*ei] << std::endl;
if ( map.find(source) == map.end() ) {
map.insert(
{
source,
std::vector({target})
}
);
} else {
std::vector<size_t> target_vec = map[source];
target_vec.push_back(target);
map[source] = target_vec;
}
}
// iterate over map to find an "origin" node
size_t origin = 0;
for (const auto& it : map) {
bool exit_flag = false;
std::vector<size_t> check_targets = it.second;
for (size_t target : check_targets) {
if (map.find(target) == map.end()) {
origin = target;
exit_flag = true;
break;
}
}
if (exit_flag) {
break;
}
}
std::cout << "Found origin of tree with value: " << origin << std::endl;

Boost Graph Library: Deleting an edge while using an iterator

Currently I am trying to implement a Girvan Newman Algorithm for a class project using boost graph library in c++.
At the moment, edges and vertices are being added fine, but I am having an issue removing edges. I am using an iterator to add my edges, and am now trying to remove them. Everything functions as expected until I try to remove an edge, and then I hit a segfault. I have tried removing the edge using the conditional remove_edge_if() and tried some of the other boost options - still having the same issue.
enum {A, B, C, D, E};
num_vertices = 5;
//writing out edges in graph
typedef std::pair<int, int> Edge;
Edge edge_array[] = {
Edge(A,B),
Edge(A,D),
Edge(C,A),
Edge(D,C),
Edge(C,E),
Edge(D,E)
};
const int num_edges = sizeof(edge_array)/sizeof(edge_array[0]);
//declare a graph object
Graph g(num_vertices);
//add the edges to the graph object
for(int i=0;i<num_edges;i++){
add_edge(edge_array[i].first, edge_array[i].second, g);
}
//actual algorithm starts here
//get property map for vertex indices
index = get(boost::vertex_index, g);
//calculate all centrality values and store in vector betweenness
calcCentrality();
//set up iterators to run through all edges
boost::graph_traits<Graph>::edge_iterator ei, ei_end;
boost::graph_traits<Graph>::edge_iterator loc;
std::tie(ei, ei_end)=edges(g);
for(int i=0;i<num_edges;i++){
std::cout << "(" << index[source(*ei, g)] << "," << index[target(*ei, g)] << ") ";
ei++;
}
std::cout << std::endl;
std::tie(ei, ei_end)=edges(g);
int max_B = 0;
boost::graph_traits<Graph>::vertex_descriptor max_B_edge1, max_B_edge2;
//iterate through all edges-1
for(int i=0;i<num_edges-1;i++){
//check betweenness centrality of edge i
int BC = betweeness[i];
if(BC > max_B){
max_B = BC;
max_B_edge1 = source(*ei, g);
max_B_edge2 = target(*ei, g);
}
ei++;
}
boost::remove_edge(max_B_edge1, max_B_edge2, g);
std::cout << std::endl;
std::tie(ei, ei_end)=edges(g);
for(int i=0;i<num_edges;i++){
std::cout << "(" << index[source(*ei, g)] << "," << index[target(*ei, g)] << ") ";
ei++;
}
std::cout << std::endl;
}
Any guidance on how to resolve this, or a new approach would be extremely helpful. I think the issue is with my iterator
The bigger problem is that you are indexing betweenness by an integer [0, num_edges). This doesn't make a lot of sense unless betweenness is actually an edge centrality map (not the vertex centrality map, or just "centrality map").
But that is making things weirder, because that would suggest an associative container like map<edge_descriptor, double> instead of a vector<>, because edge_descriptor is not an integral type.
Of course, one can paper over this by creating an explicit edge_index property/map and projecting through that to a integral-indexed container, e.g.
std::vector<double> betweenness(num_edges(g));
auto edge_id_map = get(boost::edge_index, g); // or assumed externally supplied?
auto edge_centrality_map = boost::make_safe_iterator_property_map(
betweenness.begin(),
betweenness.size(),
edge_id_map);
Now, if that were the case, one has to wonder why the same map is not used to access the betweenness by descriptor... instead of implicitly recalculating an edge index again in a loop-variable (int i). That is unsafe especially since you're modifying the edges, so re-running the loop will calculate different i for each edge! Oops.
Instead, I'd make betweenness associative, indexed by edge-descriptor. Of course, I can't show the relevant changes, because calcCentrality is missing. Even betweenness is missing (which by the way suggests that it must be a global variable? Not a very robust idea if so).
Let's assume for a moment that you have edge_centrality_map defined as above, or, in my preferred approach, like this:
// more stable idea:
std::map<Graph::edge_descriptor, double> betweenness;
auto edge_centrality_map = boost::make_assoc_property_map(betweenness);
Now the whole... mess with the loop could be replace with:
std::map<Graph::edge_descriptor, double> betweenness;
auto edge_centrality_map = boost::make_assoc_property_map(betweenness);
while (num_edges(g)) {
auto ee = edges(g);
auto [min, max] = std::minmax_element(
ee.first, ee.second, [ecm = edge_centrality_map](auto a, auto b) {
return ecm[a] < ecm[b];
});
remove_edge(*max, g);
print_edges();
}
For good measure I used minmax_element to show off the convenience of using standard algorithms here. We use the lambda to "simply" project through the centrality map. No manual juggling with loop variables and translating to vector indexes. This is less work, but more importantly less room for error.
Here's the code presented modernized and showing the suggested approach:
Live On Compiler Explorer
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/betweenness_centrality.hpp>
#include <iostream>
using Graph = boost::adjacency_list<>;
int main() {
enum { A, B, C, D, E, N /*==5*/ };
Graph g(N);
auto print_edges = [&g] { // helper to avoid repeating code
for (auto e : boost::make_iterator_range(edges(g)))
std::cout << e << " ";
std::cout << std::endl;
};
// add the edges to the graph object
for (auto [s, t] :
std::array{std::pair{A, B}, std::pair{A, D}, std::pair{C, A},
std::pair{D, C}, std::pair{C, E}, std::pair{D, E}}) //
{
add_edge(s, t, g);
}
print_edges();
// actual algorithm starts here
// get property map for vertex indices
auto index = get(boost::vertex_index, g);
// calculate all centrality values and store in vector betweenness
// calcCentrality();
//std::vector<int> betweenness(num_edges(g)); // JUST GUESSING
// more stable idea:
std::map<Graph::edge_descriptor, double> betweenness;
auto edge_centrality_map = boost::make_assoc_property_map(betweenness);
while (num_edges(g)) {
auto ee = edges(g);
auto [min, max] = std::minmax_element(
ee.first, ee.second, [ecm = edge_centrality_map](auto a, auto b) {
return ecm[a] < ecm[b];
});
boost::remove_edge(*max, g);
print_edges();
}
}
Prints dummy output (because the betweenness is defaulted to 0.0 for each edge):
(0,1) (0,3) (2,0) (2,4) (3,2) (3,4)
(0,1) (0,3) (2,0) (2,4) (3,2)
(0,1) (0,3) (2,0) (2,4)
(0,1) (0,3) (2,0)
(0,1) (0,3)
(0,1)
BONUS
In fact, the loop is not very optimal, because instead of repeatedly finding the maximum element of a container, just to remove them can be thought of as sorting, and then removing edges in order.
Live On Compiler Explorer
auto [e_begin, e_end] = edges(g);
std::vector ascending(e_begin, e_end);
std::stable_sort(begin(ascending), end(ascending), edge_compare);
while (not ascending.empty()) {
remove_edge(ascending.back(), g);
ascending.pop_back();
print_edges();
}
Which gives the same output, just much quicker.
Also note that if your chosen edge_centrality_map suffers from
index/iterator/descriptor or reference invalidation in some way on the
remove_edge operation, this is safer, because it only depends on
the stability of edge descriptors in your chosen Graph model.
In fact, you might not even need/want to remove the edges "physically" to save time and work. After all, you know which edges to leave out when printing.

Creating graph in loop from Boost library c++

I am trying to convert myself from R to C++ and am struggling with a particular graph problem. I have a String Matrix called "Gra" as below.
int main(){
string Gra[4][5] = {{"V0", "V1", "V2", "V3", "V4"},
{"V5", "V6", "NA", "NA", "V7"},
{"V8", "V9", "NA", "NA", "V10"},
{"V11", "V12", "V13", "V14", "V15"}};
Where "V0" represents a node and "NA" is not. This matrix came from the matrix called "base"
int base[4][5] = {{1, 1, 1, 1, 1},
{1, 1, 0, 0, 1},
{1, 1, 0, 0, 1},
{1, 1, 1, 1, 1}};
typedef float Weight;
typedef boost::property<boost::edge_weight_t, Weight> WeightProperty;
typedef boost::property<boost::vertex_name_t, std::string> NameProperty;
typedef boost::adjacency_list < boost::listS, boost::vecS, boost::directedS,
NameProperty, WeightProperty > Graph;
typedef boost::graph_traits < Graph >::vertex_descriptor Vertex;
typedef boost::property_map < Graph, boost::vertex_index_t >::type IndexMap;
typedef boost::property_map < Graph, boost::vertex_name_t >::type NameMap;
typedef boost::iterator_property_map < Vertex*, IndexMap, Vertex, Vertex& > PredecessorMap;
typedef boost::iterator_property_map < Weight*, IndexMap, Weight, Weight& > DistanceMap;
Graph g;
Where the problem is, trying to describe the graph in a loop. I would like to declare the nodes as
Vertex V0 = boost::add_vertex(std::string("V0"), g); // Struggling to implement this in a loop
Vertex V1 = boost::add_vertex(std::string("V1"), g);
Vertex V2 = boost::add_vertex(std::string("V2"), g);
Vertex V3 = boost::add_vertex(std::string("V3"), g);
Vertex V4 = boost::add_vertex(std::string("V4"), g);
Vertex V5 = boost::add_vertex(std::string("V5"), g);
Vertex V6 = boost::add_vertex(std::string("V6"), g);
Vertex V7 = boost::add_vertex(std::string("V7"), g);
Vertex V8 = boost::add_vertex(std::string("V8"), g);
Vertex V9 = boost::add_vertex(std::string("V9"), g);
Vertex V10 = boost::add_vertex(std::string("V10"), g);
Vertex V11 = boost::add_vertex(std::string("V11"), g);
Vertex V12 = boost::add_vertex(std::string("V12"), g);
Vertex V13 = boost::add_vertex(std::string("V13"), g);
Vertex V14 = boost::add_vertex(std::string("V14"), g);
Vertex V15 = boost::add_vertex(std::string("V15"), g);
How I have tried to replicate this is through a loop like this.
for ( int i=0; i < 4; i++) // So this will run along all elements of our base vector
{
for ( int j=0; j < 5; j++) // Length is the number of elements in our array
{
if( !(Gra[i][j] == "NA")) // Whilst going along each element inspecting whether it is a true node
{
Vertex Gra[i][j] = boost::add_vertex(std::Gra[i][j], g); // This is where the problem is
}
}
}
So the problem comes from using a string to define this object of class Vertex. Could any body help me? I'm pretty sure this is a naming convention issue that I'm struggling with. If this is solved then I can solve the rest of my problems in terms of creating the edges which I also have the same problem of trying to call an object of class "vertex" using a string.
Thanks in advance Cyrill
The expression !Gra[i][j] == "NA" will not do what you expect it to. It will first check that Gra[i][j] is not "false" and then compare the boolean result with the string "NA".
Instead either use parentheses around the equality check, or do a not-equal check. So either
!(Gra[i][j] == "NA")
or
Gra[i][j] != "NA"
There's also the problem that you declare a local array-of-arrays variable Gra inside the inner loop, which will cause clashes with your outer Gra variable. I guess that's why you use std::Gra there, but Gra is not declared in the standard namespace. Neither can you use ::Gra because Gra is not declared in the global namespace either.
Instead of declaring a new variable inside the inner loop, declare an array Vertexes outside the loop:
Vertex Vertexes[4][5];
Then use that variable to store the result of boost::add_vertex.

Optimize Network Graph creation

I have the following code that goes through a matrix of 188k x 188k rows of data and attempts to create a network graph out of it. The problem here is my algorithm is extremely slow (as expected since its quadratic). Is there a better way of doing this that I'm not seeing? I'm already thinking of using openMP to parallelize this but would be great if I don't have to.
Here's whats true about my matrix - its symmetric, its over 188 thousand by 188 thousand, each value in the matrix corresponds to the edge weight So for example, an element aij is the weight of the edge between i and j. Here's my code:
The graph creation:
typedef boost::adjacency_list
<
boost::vecS,
boost::vecS,
boost::undirectedS,
boost::property<boost::vertex_name_t, std::string>,
boost::property<boost::edge_weight_t, float>,
boost::property<boost::graph_name_t, std::string>
> UGraph;
typedef UGraph::vertex_descriptor vertex_t;
typedef UGraph::edge_descriptor edge_t;
Now the function creating the network:
vertex_t u;
vertex_t v;
edge_t e;
bool found=0;
int idx =0;
float cos_similarity;
for(int p =1;p<=adj_matrix.cols();p++){
//using a previously created vector to track already created nodes
if(std::find(created_nodes.begin(), created_nodes.end(), nodes[idx]) == created_nodes.end()){
u = add_vertex(nodes[idx], ug);
created_nodes.push_back(nodes[idx]);
}else{
u = vertex(p,ug);
}
int jdx = 0;
for(int q =1;q<=adj_matrix.cols();q++){
if(p!=q){//NO LOOPS IN THIS GRAPH
//using a previously created vector to track already created nodes
if(std::find(created_nodes.begin(), created_nodes.end(), nodes[jdx]) == created_nodes.end()){
v = add_vertex(nodes[jdx], ug);
created_nodes.push_back(nodes[jdx]);
}else{
u = vertex(q,ug);
}
tie(e, found) = edge(u, v, ug);
if(!found){//check that edge does not already exist
cos_similarity = adj_matrix(p,q);
fil<<cos_similarity<<endl;
fil.flush();
if(cos_similarity >= 0.2609){ //only add edge if value of cell is greater than this threshold
boost::add_edge(u,v,cos_similarity, ug);
edge_out<<p<<" "<<q<<" "<<cos_similarity<<endl; //creating an edge-weight list for later use
}
}
}
jdx++;
}
idx++;
}
A simple tip:
I think your algorithm is cubic rather than quadratic, because vector and std::find(vector.begin(), vector.end()) are used to avoid duplicates in the inside loop.
To avoid duplicates and keep the algorithm quadraic, you can just traverse the upper triangle of the matrix as it's symmetric, which means the graph is an undirected graph.

Limited depth search in BGL without O(number_of_vertices) used memory or time?

Is it possible to do a depth or breadth first search/visit up to some distance from a vertex in BGL without accessing, filtering, indexing, etc. all vertices in the graph?
The closest thing I've managed to write is (creates the graph 0<->1<->2<->3<->4<->5 but only visits vertices 0 to 3):
#include <iostream>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/depth_first_search.hpp>
using namespace std;
struct custom_dfs_visitor : public boost::default_dfs_visitor {
template < typename Vertex, typename Graph >
void discover_vertex(const Vertex& v, const Graph& g) const {
std::cout << v << std::endl;
}
};
struct Terminator {
template<class Vertex, class Graph>
bool operator()(const Vertex& v, const Graph& g) {
return v > 2;
}
};
int main()
{
typedef boost::adjacency_list<
boost::vecS,
boost::vecS,
boost::undirectedS
> Graph_T;
Graph_T g(6);
boost::add_edge(0, 1, g);
boost::add_edge(1, 2, g);
boost::add_edge(2, 3, g);
boost::add_edge(3, 4, g);
boost::add_edge(4, 5, g);
std::vector<boost::default_color_type> color_map(boost::num_vertices(g));
boost::depth_first_visit(
g,
boost::vertex(0, g),
custom_dfs_visitor(),
boost::make_iterator_property_map(
color_map.begin(),
boost::get(boost::vertex_index, g),
color_map[0]
),
Terminator()
);
return 0;
}
which does only print 0 1 2 3 instead of visiting all vertices, but the code still requires a color map as large as the whole graph (boost::num_vertices(g)). Is there a way to make the search complexity not be at all comparable to the total number of edges/vertices in the graph?
Using a bundled color would be acceptable because many searches would be done in different parts of the graph, but is it possible to reduce the complexity of each individual search in the same graph from O(number_of_vertices)?
Initial coloring of vertices will hopefully also stop when Terminator returns true but that seems to be taken care of already.
Maybe a related question: What about indexing if the graph uses something else than vecS? Can BFS/DFS do without indexing in that case?
Thanks for any help.
Turns out using bundled properties is the easiest way to accomplish this. The fact that the color property is included in every vertex is better than creating the color property for each vertex every time a dfs is done. The graph type should be
typedef boost::adjacency_list<
boost::vecS,
boost::vecS,
boost::undirectedS,
property<vertex_color_t, boost::default_color_type>
> Graph_T;
and the call to dfv is
depth_first_visit(
g,
vertex(0, g),
custom_dfs_visitor(),
get(vertex_color_t(), g),
Terminator()
);
With the above, doing a limited dfs in a graph with 100 M vertices doesn't increase memory consumption (76.2 % of total memory) while with an external vector of colors memory usage increases from 76.2 % to 78.5 % while searching.