Boost graph: speeding up add_edge - c++

I have a boost graph application where I need to call the function add_edge( ) [documentation available here].
Profiling this application with KCachegrind reveals the following breakup of time spent:
As can be seen, the add_edge function call takes up roughly 21% of the time of the parent caller. Out of this 21%, 14.49% is simply some std::vector's reallocation.
The suggested way to prevent such vector reallocations seems to be to upfront reserve some amount of space. See for e.g., thread: How to prevent memory reallocation using std::vector
What is the equivalent way to do this reservation of some sufficient amount of space in boost graph?
The underlying graph object over which this repeated calls to add_edge are made is thus:
typedef adjacency_list<
vecS, vecS, directedS,
property<
vertex_name_t, std::string,
property<vertex_index_t, int,
property<vertex_color_t, boost::default_color_type,
property<vertex_distance_t, double,
property<vertex_predecessor_t, Traits::edge_descriptor>
> > > >,
property<
edge_index_t, int,
property<edge_capacity_t, double,
property<edge_weight_t, double,
property<edge_residual_capacity_t, double,
property<edge_reverse_t, Traits::edge_descriptor>
> > > > >
Graph;
Edited to add: Similar questions here and here.
Unfortunately for me, g.m_edges does NOT have function reserve.
Edited to add link to a minimal example (that is difficult to get to work fully) but compiles fine except for an undefined external reference that is not the main issue.

Unfortunately for me, g.m_edges does NOT have function reserve
But m_vertices does.
#include <boost/graph/adjacency_list.hpp>
int main() {
boost::adjacency_list<> g;
g.m_vertices.reserve(1024);
}
Also, since you're using vecS you could almost equivalently just construct with a pre-allocated number of vertices:
boost::adjacency_list<> g(1024);
The difference is, of course, that this doesn't just reserve space for, but resizes the graph to contain 1024 vertices.

Related

How Can I Know Which Boost Graph Properties are Available in a Template Adjacency List?

Consider the following two adjacency lists:
#include <boost/graph/adjacency_list.hpp>
using Graph_1 = typename boost::adjacency_list<boost::vecS,
boost::vecS,
boost::directedS,
boost::no_property,
boost::property<boost::edge_weight_t, double> >;
Graph_1 graph_1(0);
struct VertexBundle { int some_int; };
struct EdgeBundle { double weight; };
struct GraphBundle { std::string name; };
using Graph_2 = typename boost::adjacency_list<boost::listS,
boost::listS,
boost::undirectedS,
VertexBundle,
boost::property<boost::edge_index_t, double, EdgeBundle>,
GraphBundle>;
Graph_2 graph_2(0);
They differ in their containers, directed category, and the properties defined for vertex, edge, and graph.
Now, for example with boost::detail::is_directed( boost::graph_traits<Graph>::directed_category ) I hold a boolean whether the graph of type Graph is directed or not, as taken from this SO question.
But how can I read out other information about the Graph Type's properties, especially which internal/bundled properties the graph has defined?
I found this boost docs page on graph_traits, but I'm not quite getting the hang of it. To beginners, the boost graph docs are a bit hard to read...
I'm thankful about any hints!
I already showed most of this here.
However I do notice that I made a mistake: I specialized boost::graph_property<Graph, Tag> where it should have been boost::property_map<Graph, Tag>. See https://www.boost.org/doc/libs/1_64_0/libs/graph/doc/property_map.html
You can either SFINAE on that or the result of get(property_tag_t{}, graph).
It looks like property_map might not even be very sfinae friendly - perhaps it hard-fails on eager instantiation of details. Mmm. That would mean you "can't" - you'll just have to always take parameters for any optional property-maps and default them to the library implementation. That way, if people fail to provide one when they should, it will croak with a hard compiler error
I'm happy to be proven wrong here, but I fear that BGL is showing its age here. I'm not sure whether c++20 std::detected<> has anything new/better here. Intuitively, I think, no, because it would require a core language change surrounding the extent of Sfinae.

boost graph copy a filtered_graph

My actual intention is to use mcgregor_common_subgraphs to find some induced partial subgraphs. But it takes too long to compare small graphs. So instead of comparing the entire graphs I want to filter out a subset of comparable vertices and edges. Then find subgraphs between them.
So I use filtered_graph with vertex and edge predicates for both of these graphs. And pass the filtered graph to mcgregor_common_subgraphs. But it complains
error: use of deleted function ‘boost::iterators::filter_iterator<bya::util::isomorphism::directed_graph::vertex_filter_predicate, boost::range_detail::integer_iterator<long unsigned int> >& boost::iterators::filter_iterator<bya::util::isomorphism::directed_graph::vertex_filter_predicate, boost::range_detail::integer_iterator<long unsigned int> >::operator=(const boost::iterators::filter_iterator<bya::util::isomorphism::directed_graph::vertex_filter_predicate, boost::range_detail::integer_iterator<long unsigned int> >&)’
So I planned to copy the filtered graph into a new graph with copy_graph. But it complains that there is no default constructor for the vertex predicate vertex_filter_predicate.
error: no matching function for call to ‘bya::util::isomorphism::directed_graph::vertex_filter_predicate::vertex_filter_predicate()’
However my vertex and edge predicate takes a const reference to the original graph. So I cannot add an empty default constructor. I have searched in boost documentation, but didn't find any example of copying a filtered_graph. What is the solution ? Also why would copy_graph require the predicates to copy ?
struct directed_graph{
typedef boost::adjacency_list<boost::setS, boost::vecS, boost::bidirectionalS, vertex_data, edge_data> graph_type;
graph_type _graph;
// ...
};
I have a directed graph struct inside which I have the predicates and equivalance comparators. Both small and large are instances of directed_graph.
directed_graph::edge_filter_predicate edge_filter_small (small._graph, allowed_vertex_ids), edge_filter_large (large._graph, allowed_vertex_ids);
directed_graph::vertex_filter_predicate vertex_filter_small(small._graph, allowed_vertex_ids), vertex_filter_large(large._graph, allowed_vertex_ids);
boost::filtered_graph<directed_graph::graph_type, directed_graph::edge_filter_predicate, directed_graph::vertex_filter_predicate> filtered_small_view(small._graph, edge_filter_small, vertex_filter_small);
boost::filtered_graph<directed_graph::graph_type, directed_graph::edge_filter_predicate, directed_graph::vertex_filter_predicate> filtered_large_view(large._graph, edge_filter_large, vertex_filter_large);
directed_graph::graph_type filtered_small;
boost::copy_graph(filtered_small_view, filtered_small);

When analyzing boost::graph, does one operate on vertex and edge descriptor or their iterators?

When working with the BOOST graph library, I have a graph instance fully initialized -- the structure is now static. I need to do some processing based on the graph.
I'm not clear if I should be working with the iterator types for vertices and edges, or the vertex and edge types themselves?
typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::bidirectionalS, VertexProperty, EdgeProperty > GraphType;
typedef typename boost::graph_traits< GraphType >::vertex_descriptor VertexType;
typedef typename boost::graph_traits<GraphType>::vertex_iterator VertexIterator;
typedef typename boost::graph_traits< GraphType >::edge_descriptor EdgeType;
typedef typename boost::graph_traits<GraphType>::out_edge_iterator EdgeIterator;
I have an algorithm where I need to check if two edges are "the same". (in the strongest sense. Suppose the graph has two parallel edges connecting E1(S1,T2) & E2(S1,T2). An edge can only be "the same" with at most one of them.
What's the difference between (edge_descriptor == edge_descriptor) and (edge_iterator == edge_iterator)? Same question for vertices.
Most of the graph functions return iterators rather than the edge/vertex types themselves.
I also have the need to store a set of edges. Not sure whether I should be storing EdgeType or EdgeIterator?
std::vector<EdgeType> processedEdges;
std::vector<EdgeIterator> processedEdges;
vit = std::find( processedEdges.begin(), processedEdges.end(), anotherEdgeRef )
if ( vit == processedEdges.end() )
doSomethingBasedOnEdgeProperty(*vit);
Reference:
http://www.boost.org/doc/libs/1_64_0/libs/graph/doc/adjacency_list.html
You should be storing descriptors, not iterators.
Iterators relate to a logical range, not the graph. Iterators may not be valid between different ranges of the same graph:
auto range1 = out_edges(vertex1, g);
auto range2 = out_edges(vertex2, g);
assert(range1.first != range2.first); // unspecified or undefined
Instead, descriptors are graph-wide. Depending on graph model, descriptors may be more stable: if an operation invalidates iterators, it doesn't necessarily invalidate the descriptors corresponding to the same graph elements.
In other words, this makes descriptors more usable as vertex or edge "ID" - or, as Boost Graph would call it, vertex_index or edge_index properties.
I think that is very close to your question.
One caveat: even so, descriptors may not always be stable!
E.g.:
adjacency_list<vecS, vecS, directedS>
leads to vertex descriptors that are stable on append, but not on
deletion.
adjacency_list<setS, listS, directedS>
on the other hand, leads to vertex descriptors that are stable on both
insertion and removal.
See documentation section "Iterator and Descriptor
Stability/Invalidation"
If you need a completely stable identity for your graph elements, you may need to add one as a (bundled) property.

vector inside a map is clear safe?

Just a quick question, if you have say:
using namespace std;
map< int, vector< string > > map1;
or maybe even:
map< int, map< int, vector< string > > > map2;
and just so you get the right idea:
map< int, map< int, map< int, vector< string > > > > map3;
if I do just:
map1.clear();
map2.clear();
map3.clear();
Is it safe in that it will empty everything in the map and its nested maps, vectors, lists, etc.?
Note:
I know if you use pointers you need to manually go through and delete or if the map goes out of scope it should be fine too I'm only particularly interested in this case when in scope and on the stack.
Yes, a map will destruct all components.
If its components are STL containers, their destructors will clear the containers.
Read more about STL containers notably about destructor of std::map
Yes this is perfectly safe. STL containers take care of memory management.
However, if you store pointers to objects which you allocated youself, you also have to delete them yourself:
std::vector<MyClass*> vec;
vec.push_back(new MyClass());
vec.clear(); // you get a memory leak here because you did not delete the object you allocated.

how to access BGL's vertex_descriptor as an int

I have an adjacency list defined as shown below. At this point I need to access vertex_descriptor as an int type. How can I do that tvertex source = ...; int source_as_int = ???source??? I remember bumping into this same question before and solved it but don't remember how and the BGL documentation is useless using it as reference, they should try to take a look at and learn from Javadocs.
Another possibility is to use a possible member function of the vertex_descriptor type or otherwise some of the global BGL functions for this purpose ... one never knows where to look for this and they seem to randomly choose between making global functions or member functions, a total fail of an intuitive design if you ask me.
typedef adjacency_list_traits<setS, setS, bidirectionalS> ttraits;
typedef adjacency_list<setS, setS, bidirectionalS,
// vertex properties
property<vertex_color_t, default_color_type>,
// edge properties
property<edge_capacity_t, int,
property<edge_residual_capacity_t, int,
property<edge_reverse_t, ttraits::edge_descriptor> > >, no_property, vecS> tbgl_adjlist_bidir;
typedef graph_traits<tbgl_adjlist_bidir>::vertex_descriptor tvertex;
typedef graph_traits<tbgl_adjlist_bidir>::edge_descriptor tedge;
typedef property_map<tbgl_adjlist_bidir, edge_capacity_t>::type tedge_capacity_map;
typedef property_map<tbgl_adjlist_bidir, edge_reverse_t>::type treverse_edge_map;
typedef property_map<tbgl_adjlist_bidir, vertex_color_t>::type tvertex_color_map;
typedef graph_traits<tbgl_adjlist_bidir>::out_edge_iterator tout_edge_iterator;
typedef graph_traits<tbgl_adjlist_bidir>::in_edge_iterator tin_edge_iterator;
OK I figured it out. Adding the vertex property vertex_index_t solves the problem. I can then access the int index of the vertex like this:
typedef adjacency_list_traits<setS, vecS, bidirectionalS> ttraits;
typedef adjacency_list<setS, vecS, bidirectionalS,
// vertex properties
property<vertex_index_t, int,
property<vertex_color_t, default_color_type> >,
// edge properties
property<edge_capacity_t, int,
property<edge_residual_capacity_t, int,
property<edge_reverse_t, ttraits::edge_descriptor> > >, no_property, vecS> tbgl_adjlist_bidir;
typedef graph_traits<tbgl_adjlist_bidir>::vertex_descriptor tvertex;
typedef graph_traits<tbgl_adjlist_bidir>::edge_descriptor tedge;
typedef property_map<tbgl_adjlist_bidir, edge_capacity_t>::type tedge_capacity_map;
typedef property_map<tbgl_adjlist_bidir, edge_reverse_t>::type treverse_edge_map;
typedef property_map<tbgl_adjlist_bidir, vertex_color_t>::type tvertex_color_map;
typedef property_map<tbgl_adjlist_bidir, vertex_index_t>::type tvertex_index_map;
typedef graph_traits<tbgl_adjlist_bidir>::vertex_iterator tvertex_iterator;
typedef graph_traits<tbgl_adjlist_bidir>::edge_iterator tedge_iterator;
typedef graph_traits<tbgl_adjlist_bidir>::out_edge_iterator tout_edge_iterator;
typedef graph_traits<tbgl_adjlist_bidir>::in_edge_iterator tin_edge_iterator;
then I use it like this:
tbgl_adjlist_bidir bgl_adjlist_bidir;
// ...
tvertex_index_map indices = get(vertex_index, bgl_adjlist_bidir);
// ...
tvertex source;
// ...
int source_as_int = indices[source];
The type of a vertex_descriptor depends on the underlying structure of the VertexListS-template parameter of adjacency_list. The only case i know of, where the descriptor is an int is, when the VertexList-Type is vecS. But keep in mind: If you choose vecS as your VertexList-Type all (stored) descriptors can become invalid, when you change the structure of your graph(as described in Iterator and Descriptor Stability/Invalidation).
Could I interest you in using custom vertex and edge types instead? They're much, much easier to work with in your own code. And as for calling on BGL algorithms, you can use Bundled Properties.