Dijkstra Shortest Path with Vertexlist = Lists in Boost Graph

Dijkstra Shortest Path with VertexList = ListS in boost graph

BGL actually has an example of using dijkstra_shortest_paths with listS/listS, but it's not linked to from the HTML documentation: http://www.boost.org/doc/libs/release/libs/graph/example/dijkstra-example-listS.cpp

What the error message is trying to tell you (error: no match for ‘operator=’ in ‘index.boost::adj_list_vertex_property_map...ValueType = boost::detail::error_property_not_found...) is that there is no per-vertex storage for the vertex_index_t property, which is what adj_list_vertex_property_map needs. To fix the problem you can either change your Graph typedef to include per-vertex storage for the vertex_index_t property or use an "external" property map such as associative_property_map.

The dijkstra-example-listS.cpp example uses the approach of changing the graph typedef. To use this approach in your code, you could define:

typedef boost::adjacency_list <boost::listS, boost::listS, boost::directedS,
boost::property<boost::vertex_name_t, std::string, boost::property<boost::vertex_index_t, int> >,
boost::property<boost::edge_weight_t, Weight> > Graph;

Finding the shortest path in a boost graph using Dijkstra

I couldn't get your idea to work and had to make some other changes now, too which is why i changed my predecessor map now to:

typedef boost::property_map < tGraph, boost::vertex_index_t >::type IndexMap;
typedef boost::iterator_property_map < tVertex*, IndexMap, tVertex, tVertex& > PredecessorMap;
tVertex s = start;
IndexMap indexMap = get(vertex_index, graph);
vector<tVertex> p(num_vertices(graph));
PredecessorMap predecessorMap(&p[0], indexMap);
vector<double> d(num_vertices(graph));

// call dijkstra algorithm from boost to find shortest path
dijkstra_shortest_paths(graph, s, predecessor_map(predecessorMap).distance_map(boost::make_iterator_property_map(d.begin(), get(boost::vertex_index, graph))));
//find path from start to end vertex
list<tVertex> pathVertices;
tVertex current = goal;
while (current != start)
{
pathVertices.push_front(current);
current = predecessorMap[current];
}

and this actually works now :)

How to visualize the boost graph and perform dijkstra's shortest path?

As a first step I reviewed your graph creation code. It seems unnecessarily complicated.

Getting On The Grid

Let's simplify, based on the observation that vecS vertex container selector implies contiguous integral vertex descriptors, starting at 0:

Graph make_grid(size_t size_x, int size_y)
{
Graph g(size_x * size_y);

using boost::make_iterator_range;
for (auto v : boost::make_iterator_range(vertices(g)))
g[v].index = v;

auto col = [=](vertex_t v) { return v % size_x; };
auto row = [=](vertex_t v) { return v / size_x; };
auto lastcol = [=](vertex_t v) { return col(v) == size_x - 1; };
auto lastrow = [=](vertex_t v) { return row(v) == size_y - 1; };

// Add edges
for (auto v : boost::make_iterator_range(vertices(g))) {
if (not lastrow(v))
add_edge(v, v + size_x, g); // vertical
if (not lastcol(v))
add_edge(v, v + 1, g); // horizontal
if (not(lastrow(v) || lastcol(v)))
add_edge(v, v + 1 + size_x, g); // down-right
if (not(lastrow(v) || 0 == col(v)))
add_edge(v, v - 1 + size_x, g); // down-left
}
return g;
}

That's all. It makes the graph.

Publish It!

Now you want to print it. Let's do that:

std::ofstream dot_file("grid.dot");
boost::write_graphviz(dot_file, g);
if (auto code = system("neato -T png grid.dot -o grid.png"))
return code;

Results in

Sample Image

The Editor Wants A Word

You can spice things up with graphviz node/edge attributes. Let's add colors and save those as well:

struct Edge { int weight; std::string color; };

...
// generating different color edges per direction
if (not lastrow(v))
add_edge(v, v + size_x, Edge{weightgen(), "red"}, g); // vertical
if (not lastcol(v))
add_edge(v, v + 1, Edge{weightgen(), "green"}, g); // horizontal
if (not(lastrow(v) || lastcol(v)))
add_edge(v, v + 1 + size_x, Edge{weightgen(), "blue"}, g); // down-right
if (not(lastrow(v) || 0 == col(v)))
add_edge(v, v - 1 + size_x, Edge{weightgen(), "brown"}, g); // down-left

Let's extract saving to a function as well:

void save(std::string const name, Graph const& g) {
std::ofstream dot_file(name + ".dot");

boost::dynamic_properties dp;
dp.property("node_id", get(boost::vertex_index, g));
dp.property("label", get(&Edge::weight, g));
dp.property("color", get(&Edge::color, g));
dp.property("fontcolor", get(&Edge::color, g));
boost::write_graphviz_dp(dot_file, g, dp);

std::ostringstream cmd;
cmd << "neato -T png " //
<< std::quoted(name + ".dot", '\'') << " -o "
<< std::quoted(name + ".png", '\'');

if (auto code = system(cmd.str().c_str())) {
if (code == -1)
::perror(name.data());
::exit(code);
}
}

Now the result is: Sample Image

Enter Dijkstra

you have compilation failures. They're due to the use of raw pointers (&d[0] e.g.) as property maps. This has been deprecated and removed long time ago. So, make explicit property maps instead, e.g.:

auto vidx = get(boost::vertex_index, g);
auto wmap = get(&Edge::weight, g);
auto dmap = boost::make_iterator_property_map(d.begin(), vidx);
auto pmap = boost::make_iterator_property_map(p.begin(), vidx);

dijkstra_shortest_paths( //
g, //
V{0}, //
boost::predecessor_map(pmap) //
.distance_map(dmap) //
.weight_map(wmap));

That compiles. I'm not too sure what exactly about the output you'd like to have visualized. Perhaps we can grey out any edges that are not in the path?

for (auto e : make_iterator_range(edges(g))) {
auto v = source(e,g), u = target(e,g);
if (p.at(u) != v)
g[e].color = "lightgrey";
}

And the result is:

Sample Image

FULL LISTING

"Live" On Coliru

#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <boost/graph/graphviz.hpp>
#include <iostream>

struct Vertex { int index; };
struct Edge { int weight; std::string color; };

using Graph = boost::adjacency_list< //
boost::vecS, boost::vecS, boost::bidirectionalS, Vertex, Edge>;

using Traits = boost::graph_traits<Graph>;
using V = Traits::vertex_descriptor;
using E = Traits::edge_descriptor;

Graph make_grid(size_t size_x, size_t size_y)
{
Graph g(size_x * size_y);

using boost::make_iterator_range;
for (auto v : boost::make_iterator_range(vertices(g)))
g[v].index = v;

auto col = [=](V v) { return v % size_x; };
auto row = [=](V v) { return v / size_x; };
auto lastcol = [=](V v) { return col(v) == size_x - 1; };
auto lastrow = [=](V v) { return row(v) == size_y - 1; };

// Add edges
auto weightgen = [] { return rand() % 5 + 1; };
for (auto v : boost::make_iterator_range(vertices(g))) {
if (not lastrow(v))
add_edge(v, v + size_x, Edge{weightgen(), "red"}, g); // vertical
if (not lastcol(v))
add_edge(v, v + 1, Edge{weightgen(), "green"}, g); // horizontal
if (not(lastrow(v) || lastcol(v)))
add_edge(v, v + 1 + size_x, Edge{weightgen(), "blue"}, g); // down-right
if (not(lastrow(v) || 0 == col(v)))
add_edge(v, v - 1 + size_x, Edge{weightgen(), "brown"}, g); // down-left
}
return g;
}

void save(std::string const name, Graph& g) {
std::ofstream dot_file(name + ".dot");

boost::dynamic_properties dp;
dp.property("node_id", get(boost::vertex_index, g));
dp.property("label", get(&Edge::weight, g));
dp.property("color", get(&Edge::color, g));
dp.property("fontcolor", get(&Edge::color, g));

boost::write_graphviz_dp(dot_file, g, dp);

std::ostringstream cmd;
cmd << "neato -T png " //
<< std::quoted(name + ".dot", '\'') << " -o "
<< std::quoted(name + ".png", '\'');

if (auto code = system(cmd.str().c_str())) {
if (code == -1)
::perror(name.data());
::exit(code);
}
}

int main() {
Graph g = make_grid(3, 3);

save("grid", g);

std::vector<int> d(num_vertices(g));
std::vector<V> p(num_vertices(g));

auto vidx = get(boost::vertex_index, g);
auto wmap = get(&Edge::weight, g);
auto dmap = boost::make_iterator_property_map(d.begin(), vidx);
auto pmap = boost::make_iterator_property_map(p.begin(), vidx);

dijkstra_shortest_paths( //
g, //
V{0}, //
boost::predecessor_map(pmap) //
.distance_map(dmap) //
.weight_map(wmap));

for (auto e : make_iterator_range(edges(g))) {
auto v = source(e,g), u = target(e,g);
if (p.at(u) != v)
g[e].color = "lightgrey";
}

save("path", g);
}

How to Pass Direceted Graph (adjacency list) into Dijkstra Algorithm Boost to find shortest path?

I don't see what the question is. Your code would simply compile, see Live On Coliru.

That said

  • you can probably do without copying the graph quite so many times
  • you should make the distance-map to number of vertices instead of edges:

    std::vector<double> d(num_edges(MyGraph));

    is supposed to be

    std::vector<double> d(num_vertices(MyGraph));

UPDATE

To the added code in the question:

  • Like I said, you probably should not be copying quite so much. In particular, why does MyAlgorithm own a copy of AnyGraph as a member MyGraph? It is never used by your own constructor...

  • Similarly, the added code has the same problem, specifically with

    for (auto v : make_iterator_range(vertices(MyGraph))) {
    std::vector<Vertex> p(num_vertices(MyGraph));
    std::vector<double> d(num_vertices(MyGraph));
    std::cout << "distance(" << v << ") = " << d[v] << ", ";
    std::cout << "parent(" << v << ") = " << p[v] << std::endl;
    }

    The d and p vectors are simply created with default-initialized values every iteration through the loop. What did you expect to find?

  • I can guess that you intended the result of dijkstra_shortest_paths to be used there, but you never did anything to make that happen. At the very least it looks like you should have made d and p member varlables

  • The setShortPath member functions are never used. By extension, the ShortPath member is never properly set. It seems you are aware of this because you also don't attempt to use it in PrintOut

  • There is a conceptual problem with printing "The Shortest Path" as it obviously depends on the target vertex... I'd write a getShortPath accessor that calculates a specific path:

    Path getShortPath(Vertex destination) {
    Path path;

    while (destination != MyGraph.null_vertex()) {
    path.push_front(destination);
    if (destination == src)
    return path;

    if (predecessors.at(destination) == destination)
    break;
    destination = predecessors.at(destination);
    }
    throw std::runtime_error("Unreachable");
    }
  • Now you can add a print function for any path:

    void PrintPath(MyAlgorithm::Path const& path, graph_t const& g) {
    std::cout << "Path: ";
    auto idmap = get(boost::vertex_name, g);
    auto wmap = get(boost::edge_weight, g);

    auto previous = g.null_vertex();
    for (auto v : path) {
    if (previous != g.null_vertex()) {
    for (auto e : make_iterator_range(out_edges(previous, g))) {
    if (target(e, g) == v) {
    std::cout << " -> (w:" << " << " << wmap[e] << ") ";
    }
    }
    }
    std::cout << "#" << v << " (id:" << idmap[v] << ") ";
    previous = v;
    }
    std::cout << "\n";
    }

    It also prints weights on every edge (you will see it matches the total distance)

Fixed Demo

Here's a version that fixes all of the above. I stopped generating random graphs as now the "test cases" make assumptions about which paths are reachable:

Live On Coliru

#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/dag_shortest_paths.hpp>
#include <boost/graph/graph_utility.hpp>
#include <iostream>

using boost::make_iterator_range;

using idType = unsigned long long;

typedef boost::adjacency_list<
boost::vecS,
boost::vecS,
boost::directedS,
boost::property<boost::vertex_name_t, idType>,
boost::property<boost::edge_weight_t, double>> graph_t;

struct MyGraphBuilder {
void generate();
void printGraph() const;

graph_t const& getGraph() const { return MyGraph; }
graph_t& getGraph() { return MyGraph; }
private:
graph_t MyGraph;
};

void MyGraphBuilder::printGraph() const {
std::cout << "Number of Vertices is:" << num_vertices(MyGraph) << "\n";
std::cout << "Number of Edges is:" << num_edges(MyGraph) << "\n";

boost::print_graph(MyGraph, boost::get(boost::vertex_name, MyGraph), std::cout);

// to print with edge weights:
for (auto v : make_iterator_range(vertices(MyGraph))) {
for (auto oe : make_iterator_range(out_edges(v, MyGraph))) {
std::cout << "Edge " << oe << " weight " << get(boost::edge_weight, MyGraph, oe) << "\n";
}
}
}

void MyGraphBuilder::generate() {
MyGraph = graph_t(5); // clear graph, 5 vertices

auto idmap = get(boost::vertex_name, MyGraph);
idmap[0] = 0ull;
idmap[1] = 100ull;
idmap[2] = 200ull;
idmap[3] = 300ull;
idmap[4] = 400ull;

add_edge(1, 3, { 1.52275 }, MyGraph);
add_edge(2, 0, { 8.79559 }, MyGraph);
add_edge(2, 0, { 6.41004 }, MyGraph);
add_edge(3, 2, { 7.37265 }, MyGraph);
add_edge(4, 0, { 1.18526 }, MyGraph);
}

struct MyAlgorithm {
using Vertex = graph_t::vertex_descriptor;

graph_t MyGraph;
Vertex src;
std::vector<Vertex> predecessors;
std::vector<double> distances;

MyAlgorithm(graph_t const& AnyGraph, Vertex VSource)
: MyGraph(AnyGraph),
src(VSource),
predecessors(num_vertices(MyGraph)),
distances(num_vertices(MyGraph))
{
dijkstra_shortest_paths(MyGraph, src,
predecessor_map(make_iterator_property_map(predecessors.begin(), get(boost::vertex_index, MyGraph)))
.distance_map(boost::make_iterator_property_map(distances.begin(), get(boost::vertex_index, MyGraph))));
}

using Path = std::deque<Vertex>;
Path getShortPath(Vertex destination) {
Path path;

while (destination != MyGraph.null_vertex()) {
path.push_front(destination);
if (destination == src)
return path;

if (predecessors.at(destination) == destination)
break;
destination = predecessors.at(destination);
}
throw std::runtime_error("Unreachable");
}

void PrintRawData() const {
std::cout << "distances and parents:" << std::endl;
for (auto v : make_iterator_range(vertices(MyGraph))) {
std::cout << "distance(" << v << ") = " << distances.at(v) << ", ";
std::cout << "parent(" << v << ") = " << predecessors.at(v) << std::endl;
}
}

graph_t const& getGraph() const { return MyGraph; }
graph_t& getGraph() { return MyGraph; }
};

void PrintPath(MyAlgorithm::Path const& path, graph_t const& g) {
std::cout << "Path: ";
auto idmap = get(boost::vertex_name, g);
auto wmap = get(boost::edge_weight, g);

auto previous = g.null_vertex();
for (auto v : path) {
if (previous != g.null_vertex()) {
for (auto e : make_iterator_range(out_edges(previous, g))) {
if (target(e, g) == v) {
std::cout << " -> (w:" << " << " << wmap[e] << ") ";
}
}
}
std::cout << "#" << v << " (id:" << idmap[v] << ") ";
previous = v;
}
std::cout << "\n";
}

int main() {
MyGraphBuilder builder;
builder.generate();
//builder.printGraph();

MyAlgorithm algo(builder.getGraph(), 1); // 1 is first vertex, not idmap

algo.PrintRawData();

auto p0 = algo.getShortPath(0);
auto p1 = algo.getShortPath(1);
auto p2 = algo.getShortPath(2);
auto p3 = algo.getShortPath(3);

for (auto path : {p0, p1, p2, p3}) {
PrintPath(path, algo.getGraph());
}

// vertex 4 is unreachable:
try {
auto p4 = algo.getShortPath(4);
} catch(std::exception const& e) {
std::cout << "Error getting path for vertex 4: " << e.what() << "\n";
}
}

Prints

distances and parents:
distance(0) = 15.3054, parent(0) = 2
distance(1) = 0, parent(1) = 1
distance(2) = 8.8954, parent(2) = 3
distance(3) = 1.52275, parent(3) = 1
distance(4) = 1.79769e+308, parent(4) = 4
Path: #1 (id:100) -> (w: << 1.52275) #3 (id:300) -> (w: << 7.37265) #2 (id:200) -> (w: << 8.79559) -> (w: << 6.41004) #0 (id:0)
Path: #1 (id:100)
Path: #1 (id:100) -> (w: << 1.52275) #3 (id:300) -> (w: << 7.37265) #2 (id:200)
Path: #1 (id:100) -> (w: << 1.52275) #3 (id:300)
Error getting path for vertex 4: Unreachable

Boost graph: dijkstra_shortest_paths: cannot form a reference to 'void'

If you look closely, or consult the docs, you'll find that Dijkstra wants edge weights.

You didn't supply them. That's an error.

In fact, if you want Dijkstra without weights, a BFS would do:

Use the Bellman-Ford algorithm for the case when some edge weights are negative. Use breadth-first search instead of Dijkstra's algorithm when all edge weights are equal to one.

Now to humour you, let's add a constant edge-weight of 1.0:

Live On Coliru

#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>

namespace octomap {
struct point3d { double x,y,z; };
}

struct OcTreeNode {};

struct Vertex {
octomap::point3d coord;
OcTreeNode *node;
};

typedef boost::adjacency_list<boost::listS, boost::vecS, boost::undirectedS, Vertex> Graph;
typedef boost::graph_traits<Graph>::vertex_descriptor VertexDesc;
typedef boost::graph_traits<Graph>::edge_descriptor EdgeDesc;

struct GraphContainer {
std::map<OcTreeNode*, VertexDesc> revmap;
Graph graph;
};

int main() {
GraphContainer gc;
add_vertex(gc.graph); // make sure we have a valid start vertex
VertexDesc vGoal = vertex(0, gc.graph);

std::vector<VertexDesc> pr(num_vertices(gc.graph));
std::vector<int> d(num_vertices(gc.graph));

auto idmap = get(boost::vertex_index, gc.graph);

dijkstra_shortest_paths(gc.graph, vGoal,
boost::predecessor_map(boost::make_iterator_property_map(pr.begin(), idmap))
.distance_map(boost::make_iterator_property_map(d.begin(), idmap))
.weight_map(boost::make_constant_property<EdgeDesc>(1.0))
);
}

Boost:: Dijkstra Shortest Path, how to get vertice index from path iterator?

With the typedefs from that question, you can use get(boost::vertex_index, dgraph, v) to get the index of v. You can also cache the property map using:

IndexMap vi = get(boost::vertex_index, dgraph);

then use get(vi, v) to get the index for v.

BGL Dijkstra Shortest Paths with Bundled Properties

So the question was "how to use a bundled property as weight map with Boost Graph Library?".

Good. You use property maps. The bundled property can be accessed with a little bit of funky syntax documented right on the "Bundled Properties" page: http://www.boost.org/doc/libs/1_58_0/libs/graph/doc/bundles.html, see heading "Property maps from bundled properties".

Now for a quick demo:

// set up a weight map:
auto weights = boost::get(&EdgeProperties::p1, g);

Passing the minimum amount of arguments to dijkstra:

// you can pass it to dijkstra using direct or named params. Let's do the simplest
boost::dijkstra_shortest_paths(g, v0, boost::no_named_parameters() .weight_map(weights));

You will want to add more parameters, but hey, this is your start :)

Live On Coliru

#include <iostream>
#include <vector>

#include <boost/config.hpp>
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <boost/property_map/property_map.hpp>
#include <boost/graph/graph_utility.hpp>

// Create a struct to hold properties for each vertex
struct VertexProperties { int p1; };

// Create a struct to hold properties for each edge
struct EdgeProperties { int p1; };

// Define the type of the graph
typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS, VertexProperties, EdgeProperties> Graph;

int main() {
// Create a graph object
Graph g;

// Add vertices
auto v0 = boost::add_vertex({1}, g),
v1 = boost::add_vertex({2}, g),
v2 = boost::add_vertex({3}, g);

// Add edges
boost::add_edge(v0, v1, EdgeProperties{1}, g);
boost::add_edge(v1, v2, EdgeProperties{2}, g);

boost::print_graph(g, boost::get(&VertexProperties::p1, g));

// set up a weight map:
auto weights = boost::get(&EdgeProperties::p1, g);

// you can pass itprint_graph`enter code here` to dijkstra using direct or named params. Let's do the simplest
boost::dijkstra_shortest_paths(g, v0, boost::no_named_parameters() .weight_map(weights));
}

You'll note that I simplified the initialization of the vertex/edge properties as well. The print_graph utility is neat if you want to have an idea of what the graph "looks" like (short of using Graphviz).

The output on Coliru is:

1 <--> 2 
2 <--> 1 3
3 <--> 2

Boost dijkstra shortest_path - how can you get the shortest path and not just the distance?

If you just want to get the path from the predecessor map you can do it like this.

//p[] is the predecessor map obtained through dijkstra
//name[] is a vector with the names of the vertices
//start and goal are vertex descriptors
std::vector< graph_traits< graph_t >::vertex_descriptor > path;
graph_traits< graph_t >::vertex_descriptor current=goal;

while(current!=start) {
path.push_back(current);
current=p[current];
}
path.push_back(start);

//This prints the path reversed use reverse_iterator and rbegin/rend
std::vector< graph_traits< graph_t >::vertex_descriptor >::iterator it;
for (it=path.begin(); it != path.end(); ++it) {

std::cout << name[*it] << " ";
}
std::cout << std::endl;

How to use boost::graph dijkstra's algorithm if vertex properties are pointers?

Q.

If I understant it right, I use make_iterator_property_map to create an external property map, but in order to create it I need to pass the vertex ID property map. But I can't access it through boost::get, because vertex property is a pointer. What type should I pass to boost::get(some_type, m_graph) to get such ID map?

You make /any type of propertymap that satisfies the requirement/. You don't need to associate it with the graph. You can simply pass it to the algorithms when you need to (which also makes it clear at which point you promise to have the graph data and the propertymap in synch).

It just occurred to me that in fact you might be able to get a pass on that last issue - the burden of maintaining the property map.
That is, if your index can be derived from the pointer value (perhaps retrieved from the struct it points to).

You can use the

  • transform_value_property_map
  • function_property_map
  • perhaps both combined with e.g. compose_property_map

Each of these types has a corresponding deducing factory method make_transform_value_property_map, make_function_property_map etc. so that you don't have to manually spell out the resulting types.

You can search my older answers for examples of what can be done with these.

Samples:

  • Dijkstra graph with a table of weights on each edge
  • Weight map as function in Boost Graph Dijkstra algorithm
  • General point about Property Map map set/get requests into C++ class/structure changes


Related Topics



Leave a reply



Submit