Boost Graph Library: Deleting an edge while using an iterator - c++

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.

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;

How to create a boost property map between rdkit molecular bond graph edges and BGL biconnected component identifiers?

The rdkit library provides a molecule class ROMol that provides a member function getTopology that returns a BGL graph of type adjacency_list<vecS, vecS, undirectedS, Atom *, Bond *>. I understand that the rdkit type Bond defines the edge properties of the graph. I know that Bond provides a member function getIdx that returns a unique integer identifying the bond, so the graph must have a notion of edge numbering.
To make use of the BGL algorithm for biconnected_components one requires a component property map that maps objects of the edge descriptor type of the graph (I understand this type is Bond?) to component identifying integers. As rdkit does not deal in biconnected components, I conclude this property map must be exterior to the graph. In this case, the BGL documentation suggests to use the iterator_property_map adaptor class (see section "Exterior Properties"). I struggle to determine the correct template arguments for iterator_property_map to get the required property map. If this is the correct approach what are the missing template arguments?
Unfortunately I did not get very far with my code before getting lost in the BGL documentation:
void my_function(const RDKit::ROMol& molecule) {
const RDKit::MolGraph& molecule_graph{molecule.getTopology()};
using EdgesSize =
boost::graph_traits<RDKit::MolGraph>::edges_size_type; // map value type
using Edge =
boost::graph_traits<RDKit::MolGraph>::edge_descriptor; // map key type
std::vector<EdgesSize> component_map(boost::num_edges(molecule_graph)
); // range, begin() yields random access iterator
// boost::iterator_property_map<?>;
// boost::biconnected_components(molecule_graph, ?);
}
one requires a component property map that maps objects of the edge descriptor type of the graph (I understand this type is Bond?)
Edge descriptors are internal descriptors, sort of like stable iterators. E.g.
edge_descriptor e = *edges(g).begin();
Edge properties are entirely separate notions. You get the edge properties from an edge descriptor like so:
Bond* e_props = g[e]; // equivalent to:
e_props = get(boost::edge_bundle, g, e);
Unsurprisingly the same goes for vertex descriptors vs. properties:
Atom* first_a_prop = g[vertex(0, g)];
The Complication
A notable difference between the two descriptor types is that - only because the graph type is using vecS as the vertex container selector - the vertex descriptor is guaranteed to be integral, where edge descriptor is opaque (similar to a void*).
Hence, the edge-decriptor cannot be the key type to a vector-based property map (because it would require an integral indexer).
Instead make an associative property-map:
// OUT: ComponentMap c
// must be a model of Writable Property Map. The value type should be
// an integer type, preferably the same as the edges_size_type of the
// graph. The key type must be the graph's edge descriptor type.
std::map<edge_descriptor, int> component_map;
auto c = boost::make_assoc_property_map(component_map);
Sample
Live On Coliru
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/biconnected_components.hpp>
#include <iostream>
struct Atom {
};
struct Bond {
int idx_ = 42;
int getIdx() const { return idx_; }
};
using G = boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS, Atom*, Bond*>;
using vertex_descriptor = G::vertex_descriptor;
using edge_descriptor = G::edge_descriptor;
int main() {
std::array<Atom, 4> atoms;
std::array<Bond, 2> bonds{Bond{111}, Bond{222}};
G g;
add_vertex(atoms.data()+0, g);
add_vertex(atoms.data()+1, g);
add_vertex(atoms.data()+2, g);
add_vertex(atoms.data()+3, g);
// using the fact that vertex_descriptor is 0..3 here:
add_edge(0, 2, bonds.data()+0, g);
add_edge(2, 3, bonds.data()+1, g);
{
// OUT: ComponentMap c
// must be a model of Writable Property Map. The value type should be
// an integer type, preferably the same as the edges_size_type of the
// graph. The key type must be the graph's edge descriptor type.
std::map<edge_descriptor, int> component_map;
auto c = boost::make_assoc_property_map(component_map);
// Now use it:
size_t n = boost::biconnected_components(g, c);
for (auto [e, component_id] : component_map) {
std::cout << "Edge " << e << " (idx:" << g[e]->getIdx()
<< ") mapped to component " << component_id << " out of "
<< n << "\n";
}
}
{
std::map<edge_descriptor, int> component_map;
auto c = boost::make_assoc_property_map(component_map);
// also writing articulation points:
[[maybe_unused]] auto [n, out] = boost::biconnected_components(
g, c,
std::ostream_iterator<vertex_descriptor>(
std::cout << "articulation points: ", " "));
std::cout << "\n";
}
}
Prints
Edge (0,2) (idx:111) mapped to component 1 out of 2
Edge (2,3) (idx:222) mapped to component 0 out of 2
articulation points: 2
Advanced Example
You can force a vector as mapping storage, but that requires you to map the edges (bonds) to a contiguous integral range [0..num_edges(g)).
I couldn't assume that getIdx() satisfies the criterion, but if it did:
// first map edges to 0..num_edges using getIdx
auto edge_index = boost::make_function_property_map<edge_descriptor>(
[&g](edge_descriptor e) { return g[e]->getIdx(); });
// provide num_edges storage for component-ids:
std::vector<int> component_ids(num_edges(g));
// project the vector through edge_index to make a Writable Property
// Map indexed by edge_descriptor;
auto c = boost::make_safe_iterator_property_map(component_ids.begin(),
component_ids.size(), edge_index);
Let's apply it to the graph from the documentation:
Live On Coliru
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/biconnected_components.hpp>
#include <boost/property_map/function_property_map.hpp>
#include <iostream>
enum letter : char { A, B, C, D, E, F, G, H, I };
struct Atom {
Atom(letter) {}
};
struct Bond {
int idx_;
int getIdx() const { return idx_; }
};
using Graph = boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS, Atom*, Bond*>;
using vertex_descriptor = Graph::vertex_descriptor;
using edge_descriptor = Graph::edge_descriptor;
int main() {
std::array<Atom, 9> atoms{A, B, C, D, E, F, G, H, I};
std::array<Bond, 11> bonds{
{{0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}, {10}}};
Graph g;
for (auto& atom : atoms)
add_vertex(&atom, g);
// using the fact that vertex_descriptor is vertex index:
add_edge(A, B, &bonds.at(0), g);
add_edge(A, F, &bonds.at(1), g);
add_edge(A, G, &bonds.at(2), g);
add_edge(B, C, &bonds.at(3), g);
add_edge(B, D, &bonds.at(4), g);
add_edge(B, E, &bonds.at(5), g);
add_edge(C, D, &bonds.at(6), g);
add_edge(E, F, &bonds.at(7), g);
add_edge(G, H, &bonds.at(8), g);
add_edge(G, I, &bonds.at(9), g);
add_edge(H, I, &bonds.at(10), g);
// OUT: ComponentMap c
// must be a model of Writable Property Map. The value type should be
// an integer type, preferably the same as the edges_size_type of the
// graph. The key type must be the graph's edge descriptor type.
// first map edges to 0..10 using getIdx
auto edge_index = boost::make_function_property_map<edge_descriptor>(
[&g](edge_descriptor e) { return g[e]->getIdx(); });
// provide num_edges storage for component-ids:
std::vector<int> component_ids(num_edges(g));
// project the vector through edge_index to make a Writable Property
// Map indexed by edge_descriptor;
auto c = boost::make_safe_iterator_property_map(component_ids.begin(),
component_ids.size(), edge_index);
// Now use it:
size_t n = boost::biconnected_components(g, c);
for (auto e : boost::make_iterator_range(edges(g))) {
// edge_index or getIdx, equivalent here:
assert(edge_index[e] == g[e]->getIdx());
auto idx =edge_index[e];
auto cid = component_ids.at(idx);
std::cout << "Edge " << e << " (idx:" << idx << ") mapped to component "
<< cid << " out of " << n << "\n";
}
}
Which prints prints the expected mapping
Edge (0,1) (idx:0) mapped to component 1 out of 4
Edge (0,5) (idx:1) mapped to component 1 out of 4
Edge (0,6) (idx:2) mapped to component 3 out of 4
Edge (1,2) (idx:3) mapped to component 0 out of 4
Edge (1,3) (idx:4) mapped to component 0 out of 4
Edge (1,4) (idx:5) mapped to component 1 out of 4
Edge (2,3) (idx:6) mapped to component 0 out of 4
Edge (4,5) (idx:7) mapped to component 1 out of 4
Edge (6,7) (idx:8) mapped to component 2 out of 4
Edge (6,8) (idx:9) mapped to component 2 out of 4
Edge (7,8) (idx:10) mapped to component 2 out of 4
In fact, if we add a little bit of bonus wizardry, we can render that:

boost graph metric_tsp_approx solution not following graph edges

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}

Augment custom weights to edge descriptors in boost::grid_graph

I am using BGL for a custom AStar search. Basically, the nodes of the graph correspond to the cells of a map, and each cell has an elevation.
I've created an cell traversal score function stepTime which takes in the elevation of two cells, and outputs a cost function. I wanna add this cost function to the edge weights in my boost graph.
How do I go about this? I've seen functions using
auto weightmap = make_transform_value_property_map
to create a weight map, but how do I update the weights according to the output of:
double stepTime(const vertex_descriptor& source, const vertex_descriptor& target, const std::vector<uint8_t>& elevation)
but how do I update the weights according to the output of:
double stepTime(const vertex_descriptor& source, const vertex_descriptor& target, const std::vector<uint8_t>& elevation)
I have no clue where you get the elevation vector from, but I guess that's your problem.
The source and target vertices are easily gotten from the graph itself, so here goes:
auto custom = boost::make_function_property_map<Graph::edge_descriptor>(
[&g,&elevation](Graph::edge_descriptor e) {
return stepTime(boost::source(e, g), boost::target(e, g), elevation);
});
Demo
Live On Coliru
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/astar_search.hpp>
#include <boost/property_map/function_property_map.hpp>
#include <iostream>
using Graph = boost::adjacency_list<>;
double stepTime(const Graph::vertex_descriptor& source, const Graph::vertex_descriptor& target, const std::vector<uint8_t>& elevation) {
std::cout << __FUNCTION__ << "(" << source << ", " << target << ", {" << elevation.size() << " elements})\n";
return 42;
}
int main() {
Graph g(10);
add_edge(4, 5, g);
add_edge(2, 8, g);
add_edge(5, 1, g);
add_edge(1, 3, g);
std::vector<uint8_t> const elevation { 1,2,3,4,5,6 }; // or whatevs
// custom weight map
auto custom = boost::make_function_property_map<Graph::edge_descriptor>(
[&g,&elevation](Graph::edge_descriptor e) {
return stepTime(boost::source(e, g), boost::target(e, g), elevation);
});
// pass it to an algorithm directly, or wrap it in a named-parameter object:
auto param = boost::weight_map(custom);
param.weight_map2(custom); // or as the alternative weight map
}

Planar graph drawing C++

I have read many articles on drawing planar graphs on the plane, I tried a lot of libraries.
In the end, I need to specify the input graph, the output to obtain new coordinates of its vertices, so that the edges do not intersect.
The choice fell on the function chrobak_payne_straight_line_drawing from Boost Graph Library.
I tested it on the following graph: http://cboard.cprogramming.com/attachments/cplusplus-programming/11153d1322430048-graph-planarization-boost-library-1grb.jpg
But it did not work and calls System.AccessViolationException at the 125 line of file chrobak_payne_drawing.hpp:
delta_x[v3] = 1;
But on this graph it works: http://cboard.cprogramming.com/attachments/cplusplus-programming/11154d1322430090-graph-planarization-boost-library-2gr.jpg
Please help, I need to function to work with all planar graphs and does not cause critical errors. Below I present the code, which I tested the first graph and got a critical error, by the way, I'm using visual studio 2010.
#include <iostream>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/properties.hpp>
#include <boost/graph/graph_traits.hpp>
#include <boost/property_map/property_map.hpp>
#include <vector>
#include <stack>
#include <boost/graph/planar_canonical_ordering.hpp>
#include <boost/graph/is_straight_line_drawing.hpp>
#include <boost/graph/chrobak_payne_drawing.hpp>
#include <boost/graph/boyer_myrvold_planar_test.hpp>
using namespace boost;
//a class to hold the coordinates of the straight line embedding
struct coord_t
{
std::size_t x;
std::size_t y;
};
int main(int argc, char** argv)
{
typedef adjacency_list
< vecS,
vecS,
undirectedS,
property<vertex_index_t, int>
> graph;
//Define the storage type for the planar embedding
typedef std::vector< std::vector< graph_traits<graph>::edge_descriptor > >
embedding_storage_t;
typedef boost::iterator_property_map
< embedding_storage_t::iterator,
property_map<graph, vertex_index_t>::type
>
embedding_t;
// Create the graph - a maximal planar graph on 7 vertices. The functions
// planar_canonical_ordering and chrobak_payne_straight_line_drawing both
// require a maximal planar graph. If you start with a graph that isn't
// maximal planar (or you're not sure), you can use the functions
// make_connected, make_biconnected_planar, and make_maximal planar in
// sequence to add a set of edges to any undirected planar graph to make
// it maximal planar.
graph g(5);
add_edge(0,3, g);
add_edge(0,4, g);
add_edge(1,3, g);
add_edge(1,4, g);
add_edge(2,3, g);
add_edge(2,4, g);
// Create the planar embedding
embedding_storage_t embedding_storage(num_vertices(g));
embedding_t embedding(embedding_storage.begin(), get(vertex_index,g));
boyer_myrvold_planarity_test(boyer_myrvold_params::graph = g,
boyer_myrvold_params::embedding = embedding
);
// Find a canonical ordering
std::vector<graph_traits<graph>::vertex_descriptor> ordering;
planar_canonical_ordering(g, embedding, std::back_inserter(ordering));
//Set up a property map to hold the mapping from vertices to coord_t's
typedef std::vector< coord_t > straight_line_drawing_storage_t;
typedef boost::iterator_property_map
< straight_line_drawing_storage_t::iterator,
property_map<graph, vertex_index_t>::type
>
straight_line_drawing_t;
straight_line_drawing_storage_t straight_line_drawing_storage
(num_vertices(g));
straight_line_drawing_t straight_line_drawing
(straight_line_drawing_storage.begin(),
get(vertex_index,g)
);
// Compute the straight line drawing
chrobak_payne_straight_line_drawing(g,
embedding,
ordering.begin(),
ordering.end(),
straight_line_drawing
);
std::cout << "The straight line drawing is: " << std::endl;
graph_traits<graph>::vertex_iterator vi, vi_end;
for(tie(vi,vi_end) = vertices(g); vi != vi_end; ++vi)
{
coord_t coord(get(straight_line_drawing,*vi));
std::cout << *vi << " -> (" << coord.x << ", " << coord.y << ")"
<< std::endl;
}
// Verify that the drawing is actually a plane drawing
if (is_straight_line_drawing(g, straight_line_drawing))
std::cout << "Is a plane drawing." << std::endl;
else
std::cout << "Is not a plane drawing." << std::endl;
return 0;
}
Mayber i must first make graph maximal planar? But I did not succeed .. Please help me add to my code the necessary functions for this, I'm not quite sure what step to convert the graph.
Thanks in advance, you - my last hope!
This looks like a bug in boost::graph. I have a different access violation there, perhaps because of a different boost version, but there's definitely a bug.
left[v] = right[leftmost];
vertex_t next_to_rightmost;
for(vertex_t temp = leftmost; temp != rightmost;
temp = right[temp]
)
{
next_to_rightmost = temp;
}
right[next_to_rightmost] = graph_traits<Graph>::null_vertex();
Here when leftmost==rightmost, the for loop is not executed and next_to_rightmost remains uninitialized. Crash! That's what I'm seeing.
The graph you were using was not maximal planar. You need to use the functions make_connected, make_biconnected_planar and make_maximal_planar before you get to a canonical ordering or an embedding. Though the comment in the source explains this, their use is not exactly intuitive and I could not find a source using them and the function chrobak_payne_straight_line_drawing in combination. Here is an example I adapted from the original source and other examples.