I have two structs containing some fields: struct MyNodeData, and struct MyEdgeData. When I create a graph with VertexList as vecS, there is no problem to access the descriptor of vertices etc. For example:
typedef adjacency_list<setS, vecS, undirectedS, MyNodeData, MyEdgeData> Graph;
typedef Graph::vertex_descriptor MyNodeDataID;
typedef Graph::edge_descriptor MyEdgeDataID;
typedef graph_traits < Graph >::vertex_iterator VertexIterator;
typedef graph_traits < Graph >::edge_iterator EdgeIterator;
typedef graph_traits < Graph >::adjacency_iterator AdjacencyIterator;
typedef property_map < Graph, vertex_index_t >::type IndexMap;
Graph g;
const IndexMap index = get(vertex_index, g);
/* Puis après avoir ajouté des vertex et edges, je peux accéder par exemple à la liste des vertex comme suite: */
pair<VertexIterator, VertexIterator> vi;
for(vi = vertices(g); vi.first != vi.second; ++vi.first)
{
cout << "vertex: " << index[*vi.first] << endl;
// or: cout << "vertex: " << *vi.first << endl;
}
But I usually need to add/delete edges and vertices from my graph. So I want to use setS or listS as a VertexList, instead of vecS, since with vecS the indexes are invalidated when we delete one of them !
The problem is that if I define VertexList as setS or listS, I can not browse the list of vertices/edges and access there descriptors like I did before !
To make it short, my question is: Since an adjacency_list that uses listS or setS as the vertex container does not automatically provide this vertex_id property, how can I add it to the code above ?
Currently, you just need to provide an associative property map.
<...>
typedef Graph::vertex_descriptor NodeID;
typedef map<NodeID, size_t> IndexMap;
IndexMap mapIndex;
associative_property_map<IndexMap> propmapIndex(mapIndex);
<...>
// indexing all vertices
int i=0;
BGL_FORALL_VERTICES(v, g, Graph)
{
put(propmapIndex, v, i++);
}
But I usually need to add/delete edges and vertices from my graph.
Removing vertices and edges is possible with vecS, setS and listS. Just call remove_vertex\remove_edge with the vertex\edge descriptor.
In all above containers, removing \ adding a vertex \ edge will invalidate the iterator. This means that after you had modified the graph, you'll have to call vertices(g) again. In most containers, modifying the container invalidates iterators to it.
In listS, adding a vertex may not invalidate the iterator, but this is implementation specific and should not be relied on.
You could add a vertex_id property to the graph, thus allowing you to get access to the vertex descriptors whenever.
Related
I usually work with vecS as container for boost::adjacency_list:
struct myVertexType { std::vector<Stuff> vec; /* and more */ };
struct myEdgeType { /* some data too */ };
using Graph = boost::adjacency_list<
boost::vecS,
boost::vecS,
boost::directedS,
myVertexType,
myEdgeType
>;
However, I encountered a situation were that raised an issue:
I was referencing some data stored as a bundled property of a vertex and when I created another vertex, that seemed to make my reference invalid (1).
At least that's what I understood from reading this page (section "Iterator and Descriptor Stability/Invalidation").
So I switched to listS, and all went fine:
using Graph = boost::adjacency_list<
boost::listS,
boost::listS,
boost::directedS,
myVertexType,
myEdgeType
>;
Until...
Until I noticed that with listS, boost::target( e1, g ) fails to compile! :
Graph g;
auto e1 = boost::add_edge(1, 0, g).first;
auto t = boost::target( e1, g );
This fails to build too: (see on coliru)
Graph g;
boost::add_edge(1, 0, g);
write_graphviz(std::cout, g );
So I searched a bit and found an answer by Sehe, stating that
vecS has an implicit vertex index.
listS doesn't. Therefore it uses the internal property vertex_index_t
However, the given answer uses Inner properties (?) (or is it dynamic properties?) and I am using my own datatypes for vertices and edges.
So my question is:
How can I build a list-based graph type that enables me to do all the "regular stuff" allowed by VecS?
(1) to be clear, I was referencing a vector that was in a vertex, and when I created another vertex, the vector suddenly became empty!
Edit: clarified what is inside my nodes.
Background
"when I created another vertex, that seemed to make my reference invalid (1)."
Yes, that's possible.
You have to realize that there's are much bigger performance trade-offs underlying your choice of container selectors. Many algorithms can get very different efficiency characteristics.
Also, some semantics subtly change (e.g. when using setS as the edge container selector, you naturally cannot have duplicate edges anymore; this is also why add_edge returns a pair<descriptor, bool>).
Also realize that often you don't need reference or even iterator stability. The typical coding pattern in BGL is not to pass/hold references to property (bundles), but instead pass property maps by value.
Property maps abstract aways access to (mutable) properties.
You can usually pass descriptors which usually are stable (unless you're removing vertices "in the middle" in vecS, as the implied vertex index is obviously changing for all following vertices).
That said, let's move on to your problems:
Questions
Until I noticed that with listS, boost::target( e1, g ) fails to compile!
Nope. That compiles fine.
What ISN'T fine is that you call add_edge with integral arguments. The vertex descriptor isn't integral with lists/setS (node based containers).
Worse, vertices don't get automatically added for non-vecS adjacency_list so you'd be referring to vertices out-of-range anyways.
The general way to refer to these is:
V v0 = add_vertex(g);
V v1 = add_vertex(g);
auto [e1, inserted] = boost::add_edge(v0, v1, g);
assert(inserted);
[[maybe_unused]] V t = boost::target(e1, g);
The graphviz call is also fine, but fails for the same reason on add_edge...
Also, you need to add a vertex index. Either as interior property or passing a property map to the algorithm function.
Here's a complete test demo that shows all three flavours:
Live On Coliru
#include <boost/algorithm/string.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graphviz.hpp>
#include <boost/core/demangle.hpp>
#include <iostream>
#include <numeric>
using boost::core::demangle;
using boost::algorithm::replace_all_copy;
struct myVertexType { /* some data */ };
struct myEdgeType { /* some data too */ };
template <typename containerS> void tests() {
using Graph = boost::adjacency_list<
containerS, containerS,
boost::directedS,
myVertexType,
myEdgeType>;
using V = typename Graph::vertex_descriptor;
std::cout << "\n"
<< std::boolalpha << "tests() with "
<< demangle(typeid(containerS).name()) << " - "
<< "vertex_descriptor integral? " << std::is_integral<V>()
<< "\n";
Graph g;
V v0 = add_vertex(g);
V v1 = add_vertex(g);
auto [e1, inserted] = boost::add_edge(v0, v1, g);
assert(inserted);
[[maybe_unused]] V t = boost::target(e1, g);
std::ostringstream dot;
if constexpr (std::is_same<boost::vecS, containerS>()) {
boost::write_graphviz(dot, g);
} else {
std::map<V, int> index;
for (auto v : boost::make_iterator_range(vertices(g)))
index.emplace(v, index.size());
auto index_map = boost::make_assoc_property_map(index);
boost::dynamic_properties dp;
dp.property("node_id", index_map); // get(boost::vertex_index, g)
boost::write_graphviz_dp(dot, g, dp);
}
std::cout << "dot: " << replace_all_copy(dot.str(), "\n", "") << "\n";
}
int main() {
tests<boost::vecS>();
tests<boost::setS>();
tests<boost::listS>();
}
Prints
tests() with boost::vecS - vertex_descriptor integral? true
dot: digraph G {0;1;0->1 ;}
tests() with boost::setS - vertex_descriptor integral? false
dot: digraph G {0;1;0->1 ;}
tests() with boost::listS - vertex_descriptor integral? false
dot: digraph G {0;1;0->1 ;}
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.
I am trying to use the Boost.Graph Library to run Goldberg's Max-Flow Algorithm. Boost.Graph calls it push_relabel_max_flow.
However, I have a very hard time understanding the library and its type-system. The documentation, which I linked above, gives an example code. But in that example, the graph is read from a file. I want to generate the graph at runtime. This is the code I have so far (mostly copied from the example):
typedef boost::adjacency_list_traits<boost::vecS, boost::vecS, boost::directedS> Traits;
typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS,
boost::property<boost::vertex_name_t, std::string>,
boost::property<boost::edge_capacity_t, long,
boost::property<boost::edge_residual_capacity_t, long,
boost::property<boost::edge_reverse_t, Traits::edge_descriptor>>>> DirectedGraph;
DirectedGraph g;
Traits::vertex_descriptor s, t;
s = boost::add_vertex(g);
t = boost::add_vertex(g);
boost::add_vertex(g);
boost::add_vertex(g);
After I added 4 vertices to the graph I should "connect" them, i.e., making edges with a capacity, residual capacity and the reverse value. The function for this task is boost::add_edge() but I have no clue how I can pass my arguments. The example code does not show it, because as I said, the data is read from a file and then directly parsed to a graph. Maybe someone with experience in the Boost.Graph library can show me how.
You can add an edge between vertices s and t like so:
boost::add_edge(s, t, {33, 44}, g);
Here setting edge_capacity to 33, and edge_residual_capacity to 44.
To actually access the edge properties, as far as I know you must do something like this:
std::cout << boost::get(boost::edge_capacity, g, boost::edge(s,t,g).first) << '\n';
which is annoying. It's easier if you use bundled properties instead, like so:
typedef boost::adjacency_list_traits<boost::vecS, boost::vecS, boost::directedS> Traits;
struct VertexProps {
std::string name;
};
struct EdgeProps {
long capacity;
long residual_capacity;
Traits::edge_descriptor reverse;
};
typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS,
VertexProps, EdgeProps > DirectedGraph;
Then you can add vertices and edges just the same way, but it's easier to access edge properties, e.g.
auto e = boost::edge(s,t,g).first; // get the edge descriptor for an edge from s to t, if any
std::cout << g[e].capacity << '\n';
To add an edge between the anonymous third and fourth vertices you added, you can get away with
boost::add_edge(2, 3, {17, 26}, g);
since the underlying storage is vector, and so vertex_descriptor is just the vector index (aka size_t, aka unsigned long around here). But to be more strictly correct you should do
boost:add_edge(boost::vertex(2, g), boost::vertex(3, g), {17, 26}, g);
in order to get the vertex_descriptor for the 3rd and 4th vertices.
I'm a BGL newbie with a (possibly) easy question: I have a directed graph and use bundled properties for edges, one of them being an index of type int. Knowing a unique index, I would like to get the corresponding edge_descriptor of that edge in order to perform operations on it. The following example summarizes my problem:
#include <boost/graph/adjacency_list.hpp>
struct EdgeProperties {
EdgeProperties(): distance(10), time_limit(5) {};
int index;
int distance;
int time_limit;
};
typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS, boost::no_property, EdgeProperties> Graph;
int main() {
Graph graph;
EdgeProperties edge_prop1, edge_prop2, edge_prop3, edge_prop4;
// Define edge properties
edge_prop1.index = 0;
edge_prop2.index = 1;
edge_prop3.index = 2;
edge_prop4.index = 3;
// Add edges to graph
boost::add_edge(0, 1, edge_prop1, graph);
boost::add_edge(0, 2, edge_prop2, graph);
boost::add_edge(1, 3, edge_prop3, graph);
boost::add_edge(2, 3, edge_prop4, graph);
// Get vertex_descriptor from an (int) index:
int vertex_index = 2;
boost::graph_traits<Graph>::vertex_descriptor v = boost::vertex(vertex_index, graph);
// I would like to get an edge_descriptor from an (int) index property:
// The following DOES NOT work:
boost::graph_traits<Graph>::edge_descriptor e = boost::edge(edge_prop1.index, graph);
}
I read about property maps as well, but could not find a solution my problem. I would prefer bundled properties over internal properties.
Is there a way of assigning unique int type indices via a bundle property to edges and access edges through these int type values?
Sadly, I don't think boost::graph is of immediate help here.
First, there is no mechanism to find an edge (or vertex, for that matter), based on a field of an edge property - BGL keeps any such mapping, and the 'index' field you have is entirely for your purposes.
Second, there is the boost::edges function that returns an iterator range for all edges of the graph. I though that you could pass vecS as edge container type to adjacency_list template, and then look inside this range, but per http://www.boost.org/doc/libs/1_61_0/libs/graph/doc/EdgeListGraph.html the iterators are only required to be multi-pass input iterators, and the implementation does exactly that -- even with vecS as edge type, you can't do random access.
Therefore, it seems that the only way to accomplish what you want is to keep your own unodered_map from index to edge descriptor.
Since my graph use setS for vertex, I have to either provide a vertex_index property map for my graph, or give an explicit vertex_id argument to write_graphviz, to be able to use write_graphviz.
My graph is defined as: typedef adjacency_list<setS, setS, undirectedS, NodeData, EdgeData> Graph;
Where NodeData and EdgeData are structures.
Can you please give me a very simple example of how to provide a vertex_index property map for my graph ? or how to give an explicit vertex_id argument to write_graphviz ?
Thanks
The solution is just to:
1) Say the vertex descriptor is defined as typedef Graph::vertex_descriptor NodeID; then you need to define an associative property map as following:
typedef map<NodeID, size_t> IndexMap;
IndexMap mapIndex;
associative_property_map<IndexMap> propmapIndex(mapIndex);
2) In the code, index all vertices as following:
int i=0;
BGL_FORALL_VERTICES(v, g, Graph)
{
put(propmapIndex, v, i++);
}
3) You can now use graphvize to drow/visualize your graph as following:
ofstream myfile;
myfile.open ("example.txt");
write_graphviz(myfile, g, default_writer(), default_writer(), default_writer(), propmapIndex);
myfile.close();
The graph will be described in example.txt, you can visualize it using graphviz.