#include <boost/graph/adjacency_list.hpp>
typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS,
boost::no_property,
boost::property<boost::edge_weight_t, double>>
DiGraph;
typedef boost::graph_traits<DiGraph>::vertex_descriptor vertex_descriptor;
int main () {
std::vector<std::size_t> vertices = { 1, 5, 10};
std::vector<std::pair<std::size_t, std::size_t>> edges = {std::make_pair(1, 5),
std::make_pair(5, 10)};
std::vector<double> weights = {2., 2.};
DiGraph di_graph (edges.begin(), edges.end(), weights.begin(), vertices.size());
DiGraph::vertex_descriptor v_start = boost::vertex(1, di_graph);
std::vector<vertex_descriptor> parents(boost::num_vertices(di_graph));
boost::dijkstra_shortest_paths(di_graph, v_start,
boost::predecessor_map(boost::make_iterator_property_map(parents.begin(), boost::get(boost::vertex_index, di_graph))));
}
This allocates a vector parents of size 11, since boost uses contiguous vertex indices.
I want the non-contiguous vertices (1, 5, 10..) but don't want the unnecessary memory space for the vector parents.
How can I make a mapping from my vertex indices to the vertex indices 1, 2, 3 and pass it to boost::dijkstra_shortest_paths?
On top of that it would be even more convenient to receive the result of dijkstra in a struct parents and access the predecessor of an element with my index, e.g.
parents[10]
but without a vector of length 11 or just have an easy conversion function f I could use
parents[f(10)]
I did take a look at the documentation of boost graph and thought the IndexMap could make this possible, but I don't understand the concept and can't make it work.
With the boost::vecS vertex container selection, the vertex index is implicit, and the call
DiGraph di_graph(
edges.begin(), edges.end(), weights.begin(), vertices.size());
is a lie: you tell it that there are 3 vertices, but then you index out of bounds (5, 10 are outside [0,1,2]).
Note also that
V v_start = boost::vertex(1, di_graph);
selects the second vertex, not vertex 1.
Internal Names
I'd probably suggest a more recent addition: internal vertex names. If we add a vertex property bundle, like simply int:
using DiGraph = boost::adjacency_list<
boost::vecS,
boost::vecS,
boost::directedS,
int,
boost::property<boost::edge_weight_t, double>>;
And then also teach BGL that we can use it as the vertex internal name:
template<> struct boost::graph::internal_vertex_name<int> {
struct type : std::identity {
using result_type = int;
};
};
Now creating the equivalent graph is simply:
DiGraph g;
add_edge(1, 5, 2., g);
add_edge(5, 10, 2., g);
That's all. You can see that it created vertices with implicit indices as the descriptors:
for (auto e : make_iterator_range(edges(g))) {
std::cout << "edge: " << e << "\n";
}
Prints:
edge: (0,2)
edge: (1,0)
To get the names, use property maps like so:
for (auto v : make_iterator_range(vertices(g))) {
std::cout << "vertex at index " << v << " named " << g[v] << "\n";
}
Printing
vertex at index 0 named 5
vertex at index 1 named 1
vertex at index 2 named 10
Using internal vertex names, you can query vertices by property bundles:
boost::optional<V> v_start = g.vertex_by_property(1);
Now, all I can suggest is using safe iterator maps:
dijkstra_shortest_paths(
g,
v_start.value(),
boost::predecessor_map(boost::make_safe_iterator_property_map(
parents.begin(), parents.size(), get(boost::vertex_index, g))));
for (size_t i = 0; i < parents.size(); ++i) {
std::cout << "Parent for '" << g[i] << "' is '" << g[parents[i]] << "'\n";
}
Live Demo
Live On Coliru
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <iostream>
template<> struct boost::graph::internal_vertex_name<int> {
struct type : std::identity {
using result_type = int;
};
};
using DiGraph = boost::adjacency_list<
boost::vecS,
boost::vecS,
boost::directedS,
int,
boost::property<boost::edge_weight_t, double>>;
using V = DiGraph::vertex_descriptor;
using boost::make_iterator_range;
int main()
{
DiGraph g;
add_edge(1, 5, 2., g);
add_edge(5, 10, 2., g);
for(auto e : make_iterator_range(edges(g)))
std::cout << "edge: " << e << "\n";
for(auto v : make_iterator_range(vertices(g)))
std::cout << "vertex at index " << v << " named " << g[v] << "\n";
boost::optional<V> v_start = g.vertex_by_property(1);
std::vector<V> parents(num_vertices(g));
dijkstra_shortest_paths(
g,
v_start.value(),
boost::predecessor_map(boost::make_safe_iterator_property_map(
parents.begin(), parents.size(), get(boost::vertex_index, g))));
for (size_t i = 0; i < parents.size(); ++i) {
std::cout << "Parent for '" << g[i] << "' is '" << g[parents[i]] << "'\n";
}
}
Prints
edge: (0,2)
edge: (1,0)
vertex at index 0 named 5
vertex at index 1 named 1
vertex at index 2 named 10
Parent for '5' is '1'
Parent for '1' is '1'
Parent for '10' is '5'
First step: take a look at bundled properties https://www.boost.org/doc/libs/1_79_0/libs/graph/doc/bundles.html
Second:
the non-contiguous vertices (1, 5, 10..) " these should be regarded as properties of the vertex. e.g "1" is a property of vertex 0.
Third: create a vertex class with 1, 5, 10.. as public attributes
Four: Create a boost graph using the your vertex class, setting and getting 1, 5, 10.. as described in the bundled properties page.
Related
The simplest boost::adjacency_list uses std::size_t as the underlying vertex_descriptor (index).
boost::adjacency_list<
boost::vecS,
boost::vecS,
boost::directedS,
boost::no_property,
boost::no_property
> graph;
Once I know the vertex descriptor, I can quickly access the desired vertex.
auto vertex = graph[idx]; // idx is the veretx descriptor
However, when the graph is mutated, there is no guarantee that the vertex_decriptor will be stable.
auto v0 = boost::add_vertex(graph);
auto v1 = boost::add_vertex(graph);
auto v2 = boost::add_vertex(graph);
boost::remove_vertex(v0, graph); // v1 and v2 are no longer valid
I would like to be able to find a specific vertex quickly - meaning I wish to avoid traversing the graph structure in search of a vertex I know exists.
My thinking is that I can somehow tweak boost::adjacency_list with a different selector to the VertexListS template parameter, that will allow me to use my own provided vertex_descripor (index).
I explored the possibility of using an associative container selector such as the builtin boost::hash_mapS but it seems I can't control the exact id it returns when calling add_vertex.
I would like to be able to use my own id (vertex_descriptor) for each vertex.
I'll try to be a bit more clear, with the code I wish would work:
// the following type will serve as the vertex_descriptor:
using my_id_type = std::size_t;
struct MyVertex
{
my_id_type id;
// some other fields
}
// add vertices with unique identifiers that will also serve as the vertex_descriptors
boost::add_vertex(MyVertex{1111}, graph); // I expect this to return 1111
boost::add_vertex(MyVertex{2222}, graph);
boost::add_vertex(MyVertex{1234}, graph);
boost::remove_vertex(1111, graph); // I expect this to remove the first vertex
// access a specific vertex using its unique, stable descriptor:
auto vertex = graph[1234];
Can this be achieved using boost::graph?
Can this be achieved using boost::graph?
The answer is nearly always "yes" with BGL. It's one of the most profoundly generic library designs in existence.
To my surprise, something new appeared in the type-hierarchy for adjacency_list. Apparently these days there's a named_graph mixin (actually maybe_name_graph) which uses traits on the vertex bundle to detect an "internal name".
This means you can get close. Though the vertex descriptor will not become your id, you can have O(1) lookup. And the interface has some conveniences, so you can write:
add_edge(1111, 2222, g);
add_edge(2222, 1111, g);
Notes:
the lookup is amortized O(1) because the name-vertex lookup is based on an unordered (hash) index
you run into problems (e.g. ambiguous overloads for add_edge) if you make the id type accidentally the same as the vertex_descriptor type (or if your argument have equally "far" conversions to either).
as far as I can tell, the internal name property is not automatically picked up as the vertex_index_t nor vertex_name_t property.
Step #1: The Graph
struct Vertex {
size_t id;
std::string other = "default-constructed";
};
using Graph =
boost::adjacency_list<boost::vecS, boost::listS, boost::directedS, Vertex>;
So far no surprises. I
opted to add a second member other just to show when/how it gets default constructed
opted listS because it
a. offers reference/descriptor stability (except for removed vertices)
b. leads to opaque vertex_descriptor (void*) which does not conflict with the id type (size_t) in overload resolution
Step #2: Name Traits
Next we teach BGL about our Internal Vertex Name.
This is purely parameterized by Vertex bundle, so keep in mind that different graphs using the same bundle would use the same name traits.
// traits
template <> struct boost::graph::internal_vertex_name<Vertex> {
struct type {
using result_type = size_t;
result_type const& operator()(Vertex const& bundle) const {
return bundle.id;
}
};
};
template <> struct boost::graph::internal_vertex_constructor<Vertex> {
struct type {
private:
using extract_name_type = typename internal_vertex_name<Vertex>::type;
using vertex_name_type = typename remove_cv<typename remove_reference<
typename extract_name_type::result_type>::type>::type;
public:
using argument_type = vertex_name_type;
using result_type = Vertex;
result_type operator()(const vertex_name_type& id) const {
return {id};
}
};
};
Note
We could of course hard-code the knowns in the second specialization:
template <> struct boost::graph::internal_vertex_constructor<Vertex> {
struct type {
Vertex operator()(size_t id) const { return {id}; }
};
};
It is very important to return the id by reference. Failing to do so leads to UB with no diagnostics from the library/compiler
Step #3 Adding/Finding Vertices
Now you can add vertices. Either by "name" (your id):
auto x = add_vertex(1111, g); // by id
Or the old-fashioned way you anticipated in the question:
add_vertex(Vertex{2222, "twotwotwotwo"}, g); // or with full bundle
Duplicate additions have no effect:
assert(add_vertex(1111, g) == x);
Different lookups exist. The vertex_by_property returns a optional<vertex_descriptor> given a vertex bundle.
assert(x == *g.vertex_by_property(g[x]));
It does so by extracting the "internal name" from the bundle passed, so there is no need for the bundle to contain any other state outside the id:
assert(x == *g.vertex_by_property(Vertex{1111}));
Although it feels like an implementation detail, the actual multi_index_container implementing the name -> descriptor index is also exposed:
assert(x == *g.named_vertices.find(1111));
Step #4 Adding Edges
add_edge(1111, 2222, g);
add_edge(2222, 1111, g);
That borrows a page from your previous question :)
Obviously, you can still add edges by vertex descriptors.
Step #5 Other Operations
Borrowing more pages from that previous answer:
print_graph(g, get(&Vertex::id, g), std::cout << "---\n");
print_graph(g, get(&Vertex::other, g), std::cout << "---\n");
std::cout << "---\n";
for (auto *v : boost::make_iterator_range(vertices(g))) {
auto const& [id, other] = g[v];
std::cout << id << " " << std::quoted(other) << "\n";
}
if (auto v = g.vertex_by_property({1111})) {
std::cout << "==== removing " << g[*v].id << "\n";
clear_vertex(*v, g); // clear edges
remove_vertex(*v, g); // remove the vertex
}
print_graph(g, get(&Vertex::id, g), std::cout << "---\n");
print_graph(g, get(&Vertex::other, g), std::cout << "---\n");
Full Demo
Live On Coliru
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graph_utility.hpp>
#include <iostream>
#include <iomanip>
struct Vertex {
size_t id;
std::string other = "default-constructed";
};
using Graph =
boost::adjacency_list<boost::vecS, boost::listS, boost::directedS, Vertex>;
// traits
template <> struct boost::graph::internal_vertex_name<Vertex> {
struct type {
using result_type = size_t;
result_type const& operator()(Vertex const& bundle) const {
return bundle.id;
}
};
};
template <> struct boost::graph::internal_vertex_constructor<Vertex> {
struct type {
private:
using extractor = typename internal_vertex_name<Vertex>::type;
using name_t = std::decay_t<typename extractor::result_type>;
public:
using argument_type = name_t;
using result_type = Vertex;
result_type operator()(const name_t& id) const { return {id}; }
};
};
int main() {
Graph g;
{
auto x = add_vertex(1111, g); // by id
add_vertex(Vertex{2222, "twotwotwotwo"}, g); // or with full bundle
// duplicate additions have no effect
assert(add_vertex(1111, g) == x);
// different lookups exist
assert(x == *g.named_vertices.find(1111));
assert(x == *g.vertex_by_property(Vertex{1111}));
assert(x == *g.vertex_by_property(g[x]));
}
add_edge(1111, 2222, g);
add_edge(2222, 1111, g);
print_graph(g, get(&Vertex::id, g), std::cout << "---\n");
print_graph(g, get(&Vertex::other, g), std::cout << "---\n");
std::cout << "---\n";
for (auto *v : boost::make_iterator_range(vertices(g))) {
auto const& [id, other] = g[v];
std::cout << id << " " << std::quoted(other) << "\n";
}
if (auto v = g.vertex_by_property({1111})) {
std::cout << "==== removing " << g[*v].id << "\n";
clear_vertex(*v, g); // clear edges
remove_vertex(*v, g); // remove the vertex
}
print_graph(g, get(&Vertex::id, g), std::cout << "---\n");
print_graph(g, get(&Vertex::other, g), std::cout << "---\n");
}
Prints
---
1111 --> 2222
2222 --> 1111
---
default-constructed --> twotwotwotwo
twotwotwotwo --> default-constructed
---
1111 "default-constructed"
2222 "twotwotwotwo"
==== removing 1111
---
2222 -->
---
twotwotwotwo -->
Sidenote:
you cannot use an associative container selector for vertices. Specifying hash_mapS leads to unordered_set<void*, hash<void*>, equal_to<void*>, allocator<void*>> as the m_vertices type.
I am a Boost (and C++) newbie, going through the graph library tutorial. I can create a graph and give it vertices and edges. I would like to use maximum cardinality matching in Boost to return a set of edges that form the maximum matching in the graph.
I have looked through max_cardinality_matching.hpp, but am not quite sure how to use it, or which functions to use, to return the maximum matching set of edges.
Here is my code so far:
#include <iostream>
#include <boost/graph/max_cardinality_matching.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graph_traits.hpp>
using namespace boost;
int main(int argc, const char * argv[]) {
// create a typedef for the Graph type
typedef adjacency_list<vecS, vecS, bidirectionalS> Graph;
// Make convenient labels for the vertices
enum { A, B, C, D, E, F, N };
const int num_vertices = N;
// const char* name = "ABCDE";
// writing out the edges in the graph
typedef std::pair<int, int> Edge;
Edge edge_array[] =
{ Edge(A,B), Edge(B,C), Edge(C,D), Edge(D,E), Edge(E,F) };
// const int num_edges = sizeof(edge_array)/sizeof(edge_array[0]);
// declare a graph object and add the edges
Graph g(edge_array, edge_array + sizeof(edge_array) / sizeof(Edge), num_vertices);
// get the property map for vertex indices
// property_map<Graph, property type>
typedef property_map<Graph, vertex_index_t>::type IndexMap;
IndexMap index = get(vertex_index, g);
// Create an iterator for vertices
typedef graph_traits<Graph>::vertex_iterator vertex_iter;
std::cout << "vertices(g) = ";
// Vertices returns a pair of vertex iterators
// The first iter points to the beginning of the vertices
// The second points past the end
std::pair<vertex_iter, vertex_iter> vp;
// vertices() returns the vertices in graph g
for (vp = vertices(g); vp.first != vp.second; ++vp.first)
std::cout << index[*vp.first] << " ";
std::cout << std::endl;
graph_traits<Graph>::edge_iterator ei, ei_end;
std::cout << "edges(g) = ";
// For each tuple of vertices (an edge), till the end of the edge list ...
for (tie(ei, ei_end) = edges(g); ei != ei_end; ++ei)
// ... print out the source and target vertices in the edge
std::cout << "(" << index[source(*ei, g)] << "," << index[target(*ei, g)] << ") ";
std::cout << std::endl;
// Return the set of edges that form a maximum matching in graph g
return 0;
}
All you need to do is create read-write property map and pass it as second argument to
template <typename Graph, typename MateMap>
bool checked_edmonds_maximum_cardinality_matching(const Graph& g, MateMap mate);
You can create ordinary std::map where key and value are vertex descriptors and adapts it by associative_property_map in order to use it with checked_edmonds_maximum_cardinality_matching. From this map you can read all edges which create maximum cardinality of your graph.
// Return the set of edges that form a maximum matching in graph g
typedef graph_traits<Graph>::vertex_descriptor VD;
std::map<VD, VD> match;
boost::associative_property_map< std::map<VD,VD> > mapAdapter(match);
bool rc = checked_edmonds_maximum_cardinality_matching(g,mapAdapter);
if (rc)
{
std::set<graph_traits<Graph>::edge_descriptor> edges;
for (auto& i : match)
{
std::pair<Graph::edge_descriptor,bool> e = boost::edge(i.first,i.second,g);
if (e.second)
edges.insert(e.first);
std::cout << i.first << " is matched to " << i.second << std::endl;
}
// print edges
for (auto& e : edges)
std::cout << "edge: " << e << std::endl;
}
As output you can see:
0 is matched to 1
1 is matched to 0
2 is matched to 3
3 is matched to 2
4 is matched to 5
5 is matched to 4
edge: (0,1)
edge: (2,3)
edge: (4,5)
I try to implement a graph class based on https://stackoverflow.com/a/950173/7558038. When adding an edge I return the edge descriptor of the added edge, but if the edge already exists, it shouldn't be added. What shall I return then? Unfortunately, null_edge() does not exist (unlike null_vertex()). It could be an std::pair<e_it_t,bool> with an appropriate edge iterator type e_it_t, but how can I get an iterator to the new edge?
Don't use that class that is almost 10 years old. It is obsolete.
Bundled properties have come to BGL as long as I know, which is... probably since at least 2010. Nothing there is fundamentally easier than straight boost.
Another weird property is that somehow only complementary edges can be inserted in that graph. This might be what you want, but it doesn't warrant having the complete class, IMO.
In fact, having the custom type removes ADL, which makes things more tedious unless you go and add each other operation (like, you know, out_edges or in_edges, which presumably is what you wanted a bidirectional graph for in the first place; maybe you actually wish to have iterable ranges instead of pair<iterator, iterator> which requires you to write old-fashioned for loops).
Now that I've warmed up a bit, lets demonstrate:
Using The Obsolete Wrapper class
The linked wrapper affords usage like this:
struct VertexProperties { int i; };
struct EdgeProperties { double weight; };
int main() {
using MyGraph = Graph<VertexProperties, EdgeProperties>;
MyGraph g;
VertexProperties vp;
vp.i = 42;
MyGraph::Vertex v1 = g.AddVertex(vp);
g.properties(v1).i = 23;
MyGraph::Vertex v2 = g.AddVertex(vp);
g.properties(v2).i = 67;
g.AddEdge(v1, v2, EdgeProperties{1.0}, EdgeProperties{0.0});
for (auto vr = g.getVertices(); vr.first!=vr.second; ++vr.first) {
auto& vp = g.properties(*vr.first);
std::cout << "Vertex " << vp.i << "\n";
for (auto er = g.getAdjacentVertices(*vr.first); er.first!=er.second; ++er.first) {
auto s = *vr.first;
auto t = *er.first;
// erm how to get edge properties now?
std::cout << "Edge " << g.properties(s).i << " -> " << g.properties(t).i << " (weight?!?)\n";
}
}
}
Which prints:
Vertex 23
Edge 23 -> 67 (weight?!?)
Vertex 67
Edge 67 -> 23 (weight?!?)
Note I didn't exactly bother to solve the problem of getting the edge-weight (we don't readily get edge descriptors from the interface at all).
The for loops throw us back in time at least 6 years. And that's not nearly the worst problem. Presumably, you need your graph for something. Let's assume you want minimum cut, or a shortest path. This means you want to invoke an algorithm that requires the edge weight. This would look like so:
// let's find a shortest path:
// build the vertex index map
boost::property_map<MyGraph::GraphContainer, vertex_properties_t>::const_type vpmap =
boost::get(vertex_properties, g.getGraph());
// oops we need the id from it. No problem, it takes only rocket science:
struct GetId {
int operator()(VertexProperties const& vp) const {
return vp.i;
}
};
GetId get_id;
boost::transform_value_property_map<GetId,
boost::property_map<MyGraph::GraphContainer, vertex_properties_t>::const_type,
int> id_map
= boost::make_transform_value_property_map<int>(get_id, vpmap);
// build the weight map
boost::property_map<MyGraph::GraphContainer, edge_properties_t>::const_type epmap =
boost::get(edge_properties, g.getGraph());
// oops we need the weight from it. No problem, it takes only rocket science:
struct GetWeight {
double operator()(EdgeProperties const& ep) const {
return ep.weight;
}
};
GetWeight get_weight;
boost::transform_value_property_map<GetWeight,
boost::property_map<MyGraph::GraphContainer, edge_properties_t>::const_type,
double> weight_map
= boost::make_transform_value_property_map<double>(get_weight, epmap);
// and now we "simply" use Dijkstra:
MyGraph::vertex_range_t vertices = g.getVertices();
//size_t n_vertices = g.getVertexCount();
MyGraph::Vertex source = *vertices.first;
std::map<MyGraph::Vertex, MyGraph::Vertex> predecessors;
std::map<MyGraph::Vertex, double> distance;
boost::dijkstra_shortest_paths(g.getGraph(), source,
boost::predecessor_map(boost::make_assoc_property_map(predecessors))
.distance_map(boost::make_assoc_property_map(distance))
.weight_map(weight_map)
.vertex_index_map(id_map));
This is not my idea of usability. Just to show it all compiles and runs:
Live On Coliru
Replace The Wrapper In 2 Lines Of C++11
Let's replace the whole Graph class template in modern BGL style:
template <typename VertexProperties, typename EdgeProperties>
using Graph = adjacency_list<setS, listS, bidirectionalS, VertexProperties, EdgeProperties>;
Really. That is a solid replacement, I'll demonstrate it right away.
In fact, let's not do using namespace boost; because it pollutes our namespace with all manner of names we might find really useful (like, you know source or num_vertices) and invites ambiguous symbols:
template <typename VertexProperties, typename EdgeProperties>
using Graph = boost::adjacency_list<boost::setS, boost::listS, boost::bidirectionalS, VertexProperties, EdgeProperties>;
The Same Use-Cases - creation and dijkstra
They are still as simple, or in fact simpler. The full code goes down from 249 lines of code to just 57:
Live On Coliru
#include <boost/graph/adjacency_list.hpp>
namespace MyLib {
template <typename VertexProperties, typename EdgeProperties>
using Graph = boost::adjacency_list<boost::setS, boost::listS, boost::bidirectionalS, VertexProperties, EdgeProperties>;
}
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <iostream>
struct VertexProperties { int i; };
struct EdgeProperties { double weight; };
int main() {
using boost::make_iterator_range;
using MyGraph = MyLib::Graph<VertexProperties, EdgeProperties>;
MyGraph g;
auto v1 = add_vertex({42}, g);
auto v2 = add_vertex({42}, g);
g[v1].i = 23;
g[v2].i = 67;
add_edge(v1, v2, EdgeProperties{ 1.0 }, g);
add_edge(v2, v1, EdgeProperties{ 0.0 }, g);
for (auto v : make_iterator_range(vertices(g))) {
std::cout << "Vertex " << g[v].i << "\n";
}
for (auto e : make_iterator_range(boost::edges(g))) {
auto s = source(e, g);
auto t = target(e, g);
std::cout << "Edge " << g[s].i << " -> " << g[t].i << " (weight = " << g[e].weight << ")\n";
}
// let's find a shortest path:
auto id_map = get(&VertexProperties::i, g);
auto weight_map = get(&EdgeProperties::weight, g);
auto source = *vertices(g).first;
using Vertex = MyGraph::vertex_descriptor;
std::map<Vertex, Vertex> predecessors;
std::map<Vertex, double> distance;
std::map<Vertex, boost::default_color_type> colors;
boost::dijkstra_shortest_paths(
g, source,
boost::vertex_color_map(boost::make_assoc_property_map(colors))
.predecessor_map(boost::make_assoc_property_map(predecessors))
.distance_map(boost::make_assoc_property_map(distance))
.weight_map(weight_map)
.vertex_index_map(id_map));
}
I'd say
that is superior.
it's just as elegant despite not relying on using namespace boost (ADL is the key here)
and we actually printed the edge weight!
And It Can Be Cleaner Still
If you switch to a vertex container selector that has implicit vertex index (like vecS):
Live On Coliru
#include <boost/graph/adjacency_list.hpp>
namespace MyLib {
template <typename VertexProperties, typename EdgeProperties>
using Graph = boost::adjacency_list<boost::setS, boost::vecS, boost::bidirectionalS, VertexProperties, EdgeProperties>;
}
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <iostream>
struct VertexProperties { int i; };
struct EdgeProperties { double weight; };
int main() {
using boost::make_iterator_range;
using MyGraph = MyLib::Graph<VertexProperties, EdgeProperties>;
MyGraph g;
add_vertex({23}, g);
add_vertex({67}, g);
add_edge(0, 1, EdgeProperties{ 1.0 }, g);
add_edge(1, 0, EdgeProperties{ 0.0 }, g);
for (auto v : make_iterator_range(vertices(g))) {
std::cout << "Vertex " << g[v].i << "\n";
}
for (auto e : make_iterator_range(boost::edges(g))) {
auto s = source(e, g);
auto t = target(e, g);
std::cout << "Edge " << g[s].i << " -> " << g[t].i << " (weight = " << g[e].weight << ")\n";
}
// let's find a shortest path:
std::vector<size_t> predecessors(num_vertices(g));
std::vector<double> distance(num_vertices(g));
boost::dijkstra_shortest_paths(g, *vertices(g).first,
boost::predecessor_map(predecessors.data()).distance_map(distance.data())
.weight_map(get(&EdgeProperties::weight, g)));
}
Output:
Vertex 23
Vertex 67
Edge 23 -> 67 (weight = 1)
Edge 67 -> 23 (weight = 0)
WAIT - Don't Forget About The Question!
I won't! I think the above shows the problem was an X/Y problem.
If you hadn't had the handicap of custom class wrapping, detecting duplicate edges was a given (see if add_vertex in BGL checks for the existence of the vertex for background):
struct { size_t from, to; double weight; } edge_data[] = {
{0, 1, 1.0},
{1, 0, 0.0},
{0, 1, 99.999} // oops, a duplicate
};
for(auto request : edge_data) {
auto addition = add_edge(request.from, request.to, { request.weight }, g);
if (!addition.second) {
auto& weight = g[addition.first].weight;
std::cout << "Edge already existed, changing weight from " << weight << " to " << request.weight << "\n";
weight = request.weight;
}
}
This will print Live On Coliru:
Edge already existed, changing weight from 1 to 99.999
If you prefer you can of course write things more expressively:
Graph::edge_descriptor e;
bool inserted;
boost::tie(e, inserted) = add_edge(request.from, request.to, { request.weight }, g);
Or, with some c++17 flair:
auto [e, inserted] = add_edge(request.from, request.to, { request.weight }, g);
More From Here
Also, in all likelihood you need to do uniqueness checks on the vertices too, so you end up with graph creation code like you can see in this answer: Boost BGL BFS Find all unique paths from Source to Target
Graph read_graph() {
std::istringstream iss(R"(
0 1 0.001
0 2 0.1
0 3 0.001
1 5 0.001
2 3 0.001
3 4 0.1
1 482 0.1
482 635 0.001
4 705 0.1
705 5 0.1
1 1491 0.01
1 1727 0.01
1 1765 0.01)");
Graph g;
std::map<int,Vertex> idx; // temporary lookup of existing vertices
auto vertex = [&](int id) mutable {
auto it = idx.find(id);
if (it != idx.end())
return it->second;
return idx.emplace(id, add_vertex(id, g)).first->second;
};
for (std::string line; getline(iss, line);) {
std::istringstream ls(line);
int s,t; double w;
if (ls >> s >> t >> w) {
add_edge(vertex(s), vertex(t), w, g);
} else {
std::cerr << "Skipped invalid line '" << line << "'\n";
}
}
return g;
}
Other examples show how you can insert both a -> b and b -> a while maintaining a mapping between the forward and back edges: Accessing specific edges in boost::graph with integer index
Summary
Coming full circle, I recommend getting acquainted with the newer, more elegant Boost Graph features. In the end, it's perfectly normal to encapsulate your graph, and you might end up with an even more polished interface.
I am new to C++ and the boost graph library. I am trying to use iterators to access information already stored within my graph "lattuce", more specifically, the weight an edge between two specific nodes.
This data will then be used by a A* algorithm (not the one in Boost graph). I am not sure if iterators are the solution to this either, so any guidance or criticism would be appreciated.
struct Point {//struct point with vertex properties
int x, y;
int parentx, parenty;
double g;
double h;
friend std::ostream& operator<<(std::ostream& os, Point p) {
return os << "[" << p.x << "," << p.y << "]";
}
};
int main() {
//declarations
typedef property < edge_weight_t, double >Weight;
using std::vector;//?
using Graph = adjacency_list<setS, vecS, undirectedS, Point, Weight>;//graph includes our created point struct property<edge_weight_t
using vertex_descriptor = Graph::vertex_descriptor;
Graph lattuce;
//lattuce graph is created with weighted edges value 1 or 1,41 if diagonal. The functions used on a loop are:
//add_edge(nodes[p.x][p.y],nodes[neighbour.x][neighbour.y], Weight(1.0), lattuce);
//add_edge(nodes[p.x][p.y],nodes[neighbour.x][neighbour.y], Weight(1.4), lattuce);
If more information about the code that generates the graph is needed I'll provide it. Thanks
It is possible to obtain link edge weights in directed and undirected graphs by means of the boost::property_map:
boost::property_map<UndirectedGraph, boost::edge_weight_t>::type EdgeWeightMap = get(boost::edge_weight_t(), g);
Example implementation given below, that first builds the following simple graph (specifically a tree with no cycles):
... then uses the boost::property_map to obtain the weight of each edge, and prints it out:
#include <iostream>
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/adjacency_list.hpp>
typedef boost::property<boost::edge_weight_t, double> EdgeWeight;
typedef boost::adjacency_list<boost::listS, boost::vecS, boost::undirectedS, boost::no_property, EdgeWeight> UndirectedGraph;
typedef boost::graph_traits<UndirectedGraph>::edge_iterator edge_iterator;
int main(int, char*[])
{
// 1. Undirected graph - print out the edge weights
UndirectedGraph g;
boost::add_edge(0, 1, 8, g);
boost::add_edge(0, 5, 2, g);
boost::add_edge(5, 6, 1, g);
boost::add_edge(4, 5, 5, g);
boost::add_edge(3, 5, 7, g);
boost::property_map<UndirectedGraph, boost::edge_weight_t>::type EdgeWeightMap = get(boost::edge_weight_t(), g);
std::pair<edge_iterator, edge_iterator> edgePair;
for (edgePair = edges(g); edgePair.first != edgePair.second; ++edgePair.first)
{
std::cout << *edgePair.first << " " << EdgeWeightMap[*edgePair.first] << std::endl;
}
return 0;
}
Giving the following console output, showing the edges as (start,end) node pairs plus their respective weights:
I have a graph stored as an adjacency list. The vertex property holds an int ID, and the edge property holds a 4x4 matrix and a weight;
In a test case, the graph is a 3-vertex graph with an edge connecting each pair of vertices (a complete graph).
I have a vector of edges descriptors PathType, representing a path, and I am iterating through it and accessing each edge and its property, RelationshipEdge, as follows.
for(PathType::iterator pathIterator = path.begin(); pathIterator != path.end(); ++pathIterator){
edge_t edge = *pathIterator;
RelationshipEdge rEdge = m_graph[edge];
int sourceID = m_graph[boost::source(edge, m_graph)].id;
int destID = m_graph[boost::target(edge, m_graph)].id;
However sometimes when this is performed, the RelationshipEdge returned contains the data for the wrong edge.
As an example, inspecting edge shows an m_source of 1 and m_target of 2. If I inspect the graph and find the edge with source 1 and target 2, the weight is 3 and the matrix is as entered. However rEdge has a weight of 1, and a different matrix. Those values actually correspond with the edge with source 0 and target 1.
Am I accessing the edge properties correctly?
The definition of my graph type is:
typedef boost::adjacency_list<
boost::vecS, boost::vecS, boost::undirectedS, MarkerVertex, RelationshipEdge>
CorrelationAdjacencyList;
I believe your error is coming from somewhere else in the code base.
I put together this simple code to test out edge access and order on a similar graph and everything works as expected.
Culprits could be manually maintained edge_descriptors or vertex_descriptors as sehe mentioned. Or perhaps your path vector initialization or construction.
#include <iostream>
#include <algorithm>
#include <vector>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graph_traits.hpp>
using namespace std;
enum edge_t {A,B};
struct MarkerVertex{
std::string id;
};
struct RelationshipEdge{
std::string id;
};
typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS,
MarkerVertex, RelationshipEdge> CorrelationAdjacencyList;
typedef boost::graph_traits<CorrelationAdjacencyList>::edge_descriptor Edge;
typedef boost::graph_traits<CorrelationAdjacencyList>::vertex_descriptor Vertex;
typedef vector<Edge> PathType;
void printPath(PathType &p, CorrelationAdjacencyList &g){
cout << "print path" << endl;
for(PathType::iterator pi = p.begin(); pi != p.end(); ++pi){
Edge e = *pi;
Vertex s = boost::source(e,g);
Vertex t = boost::target(e,g);
cout << g[s].id << "\t"
<< g[e].id << "\t"
<< g[t].id << endl;
bool isFound;
Edge eForward;
boost::tie(eForward,isFound) = boost::edge(s,t,g);
cout << "forward " << g[eForward].id << "\t" << isFound << endl;
Edge eBackward;
boost::tie(eBackward,isFound) = boost::edge(t,s,g);
cout << "backward " << g[eBackward].id << "\t" << isFound << endl;
}
}
int main(int argc, char* argv[]){
CorrelationAdjacencyList graph;
Vertex a = boost::add_vertex(graph); graph[a].id = "a";
Vertex b = boost::add_vertex(graph); graph[b].id = "b";
Vertex c = boost::add_vertex(graph); graph[c].id = "c";
Edge e1 = boost::add_edge(a,b,graph).first; graph[e1].id = "e1";
Edge e2 = boost::add_edge(b,c,graph).first; graph[e2].id = "e2";
Edge e3 = boost::add_edge(c,a,graph).first; graph[e3].id = "e3";
PathType path1,path2,path3;
path1.push_back(e1);
path1.push_back(e2);
path1.push_back(e3);
path2.push_back(e2);
path2.push_back(e3);
path2.push_back(e1);
path3.push_back(e3);
path3.push_back(e2);
path3.push_back(e1);
printPath(path1,graph);
cout << endl;
printPath(path2,graph);
cout << endl;
printPath(path3,graph);
cin.get();
}
Without further information, I can make the educated guess that you use vecS containers and the iterators/references have become invalidated.
This would happen on insertion/deletion of edges/vertices.
Here's the solution I found, though I'm not convinced I fully understand why the original method does not.
I have replaced the above code with
for(PathType::iterator pathIterator = path.begin(); pathIterator != path.end(); ++pathIterator){
edge_t edge = *pathIterator;
int sourceID = m_graph[boost::source(edge, m_graph)].id;
int destID = m_graph[boost::target(edge, m_graph)].id;
int lowerID, higherID;
if (sourceID < destID){
lowerID = sourceID;
higherID = destID;
} else {
lowerID = destID;
higherID = sourceID;
}
edge_t edge2 = m_edgeDescriptorsByEndpoints.at(make_pair(lowerID, higherID));
RelationshipEdge rEdge = m_graph[edge2];
m_edgeDescriptorsByEndpoints is a map of pairs of vertex IDs to edge descriptors.
So I'm taking the edge descriptor, getting the IDs of the source and target vertices, finding the corresponding edge descriptor from the map and then getting the properties of that edge.
Not exactly a satisfying solution but it works, as far as I've been able to test so far.
EDIT
I substituted the call to my map with a call to boost::edge(u,v,g) as suggested by #sehe, so the code is now as follows:
for(PathType::iterator pathIterator = path.begin(); pathIterator != path.end(); ++pathIterator){
edge_t edge = *pathIterator;
vert_t sourceV = boost::source(edge, m_graph);
vert_t targetV = boost::target(edge, m_graph);
pair<edge_t, bool> edgePair = boost::edge(sourceV, targetV, m_graph);
if (!edgePair.second){
cerr << "Edge does not exist" << endl;
}
RelationshipEdge rEdge = m_graph[edgePair.first];
This code still results in rEdge containing the correct properties, which seems odd, since I'd think that querying the graph for the vertices of an edge, and then querying the graph for the edge that connects those vertices would always give you that exact same edge back.