1

Can anyone show, with a working example, how one might determine the actual edges used by path obtained from an astar_search() on a graph of type: adjacency_list<multisetS,vecS,directedS,location,route> when parallel edges (multiple routes between the same adjacent source and target vertex) are likely to be present (with different "costs")?

location and route are custom structures that I have as bundled properties for vertices and edges.

I originally was going to use a listS (specifically a std::list) as the type for the outEdgesList but I understand that if I wanted to use out_edge_range(source, target, graph) to retrieve all the edges linking source and target, it needs to be a multisetS (an "ordered set" which permits duplicate values?) - in the worst case I would have to step back through the vertexes of the found path from destination to start, and use the current and previous vertexes with that to recall all the possible edges involved and then pick the one with the lowest "cost" - but that seems a bit non-optimal if the search has already done just that to find the path...!

I am led to believe an edge_predecessor_recorder visitor might be a way to note down the particular edge selected but I have not been able to find a code sample that shows it in use - can that particular visitor even be used on the predecessor map from an A* search?

I should say that I am not totally familiar with the boost libraries - and I'm not that strong on C++ (C: yes, C++: gulp !) The way that the BGL typedefs things and provides some data structures automagically may, indeed, maximise the flexibility to utilise it - but it is a little confusing for the inexperienced (me, for example) to pin down the actual types of elements used or needed for a particular use IMVHO.

SlySven
  • 326
  • 3
  • 15

1 Answers1

2

I think you're on the right track. This worked for me:

struct location_t {     // vertex properties
    std::string name;
};
struct route_t {       // edge properties
    std::size_t distance;
};
typedef adjacency_list<listS,vecS,directedS,location_t,route_t> graph_t;

typedef graph_traits<graph_t>::edge_descriptor   edge_t;
typedef graph_traits<graph_t>::vertex_descriptor vertex_t;

struct heuristic {
    heuristic(vertex_t dest) : dest_(dest) {}
    std::size_t operator()(vertex_t src) {
        // only needs to be "optimistic", so:
        return (src == dest_) ? 0 : 1 ;
    }
private:
    vertex_t dest_;
};

typedef std::map<vertex_t, edge_t> pred_edge_map_t;
typedef associative_property_map<pred_edge_map_t> pred_edge_pmap_t;

int main() {
    graph_t g;
    // insert four vertices and a mix of singular and parallel edges
    vertex_t zero  = add_vertex(location_t{"A"}, g);    // source
    vertex_t one   = add_vertex(location_t{"B"}, g);
    vertex_t two   = add_vertex(location_t{"C"}, g);
    vertex_t three = add_vertex(location_t{"D"}, g);    // sink

    // optimal path: 0->2->3 (cost 6)
    add_edge(zero, one, route_t{3}, g);
    add_edge(zero, one, route_t{5}, g);  // parallel to previous edge
    add_edge(zero, two, route_t{4}, g);
    add_edge(one, three, route_t{4}, g);
    add_edge(two, three, route_t{2}, g);
    add_edge(two, three, route_t{4}, g); // parallel to previous edge

    // construct predecessor map
    pred_edge_map_t pred;
    pred_edge_pmap_t pred_pmap(pred);
    // construct visitor that uses it
    auto recorder = record_edge_predecessors(pred_pmap, on_edge_relaxed());
    astar_visitor<decltype(recorder)> visitor(recorder);

    astar_search(g, zero, heuristic(three),
                 weight_map(get(&route_t::distance, g)).
                 visitor(visitor));

    // extract route (in reverse order)
    for (vertex_t v = three; v != zero; v = source(pred_pmap[v], g)) {
        auto e = pred_pmap[v];
        std::cout << g[source(e, g)].name << "->" << g[target(e, g)].name << " with weight " << g[pred_pmap[v]].distance << std::endl;
    }
}
Jeff Trull
  • 1,236
  • 11
  • 16
  • I note that your adjacency_list typedef uses listS as the [outer]edgeList type, is there going to be a problem using a multisetS instead - if you note I'm dealing with a graph with parallel edges which isn't going to work if I use a std::list type structure to hold the edgeList...? – SlySven Aug 17 '15 at 13:18
  • SlySven, I believe that the opposite is true - using a set structure (setS, for example) would be a problem for parallel edges, because it would not permit duplicates. The list should be fine. In fact, you can see that my example contains two pairs of parallel edges. – Jeff Trull Aug 18 '15 at 03:26
  • Ah "@Jeff TrullI" I did say **[multisetS](http://www.cplusplus.com/reference/set/multiset/)** for precisely that reason - so that _duplicates are permitted_, and indeed, they will be _grouped_ together! – SlySven Aug 20 '15 at 15:25
  • OK, I must not understand your question properly :) So you would prefer to use a multiset over a list? Looking over your question again, I don't understand the comment about `out_edge_range`... but the code produces the same result when `multiset` is used instead of `list` – Jeff Trull Aug 20 '15 at 16:18
  • For sequence edge container types I think add_edge(, , , ) will reject the addition of subsequent parallel edges - it returns a std::tie(, ) {though your example doesn't use either :) }, the second, a boolean indicates successful insertion into the graph which I am lead to think will be false for std::list OR std::vector types - I do not know if the code actually replaces "worse" edges with better ones within the add_edge() call, it might be possible depending on whether the container members are mutable... – SlySven Aug 21 '15 at 02:07
  • See this SO question about [out_edge_range()](http://stackoverflow.com/questions/19223692/boost-graph-library-directed-multigraph-edge-range-bug) relating to its use - I would hope the bug it mentions has been fixed by now. Importantly, it needs something like a multisetS edge type as that holds parallel edges in adjacent locations in the container. – SlySven Aug 21 '15 at 02:13
  • Looks like it's actually `edge_range` :) but that's OK, I see what they are doing. Fortunately `edge_range` is not necessary for tracing predecessors if you use the edge predecessor as I did. As for rejecting parallel edges, it is only the set and hash_set variants that do this, according to the docs (see "edge_parallel_category"), and indeed, the "inserted" values returned in the list case are both *true* for the second, parallel edge. – Jeff Trull Aug 22 '15 at 06:44
  • 1
    I am minded to accept this answer - but I will refrain from doing so until I have actually put it into use in the project concerned (with which I am getting bogged down with other things at the moment... *sigh* !) – SlySven Dec 07 '15 at 12:01