Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Which algorithm from petgraph will find the shortest path from A to B?

Tags:

rust

petgraph

I have a directional graph and want to find the shortest path from node A to node B. I searched on crates.io and found petgraph which looks like the most popular crate. It implements a number of algorithms, but none of them solve my task. Did I miss something?

For example, Dijkstra's algorithm returns path costs, but which path has the minimum cost? The Bellman-Ford algorithm returns path costs and nodes, but no paths.

This is the simplest way I found to print a path from the graph:

extern crate petgraph;
use petgraph::prelude::*;
use petgraph::algo::dijkstra;

fn main() {
    let mut graph = Graph::<&str, i32>::new();
    let a = graph.add_node("a");
    let b = graph.add_node("b");
    let c = graph.add_node("c");
    let d = graph.add_node("d");

    graph.extend_with_edges(&[(a, b, 1), (b, c, 1), (c, d, 1), (a, b, 1), (b, d, 1)]);
    let paths_cost = dijkstra(&graph, a, Some(d), |e| *e.weight());
    println!("dijkstra {:?}", paths_cost);

    let mut path = vec![d.index()];
    let mut cur_node = d;

    while cur_node != a {
        let m = graph
            .edges_directed(cur_node, Direction::Incoming)
            .map(|edge| paths_cost.get(&edge.source()).unwrap())
            .min()
            .unwrap();
        let v = *m as usize;
        path.push(v);
        cur_node = NodeIndex::new(v);
    }

    for i in path.iter().rev().map(|v| graph[NodeIndex::new(*v)]) {
        println!("path: {}", i);
    }
}

As far as I can see, I need to calculate the path myself based on result of dijkstra.

I believe that if I implement dijkstra by myself (basing my implementation on dijkstra.rs), and uncomment the lines with predecessor, and return predecessor, the final variant will be faster because the answer will be something like predecessor[predecessor[d]].

like image 993
user1244932 Avatar asked Apr 14 '17 23:04

user1244932


1 Answers

As mentioned in the comments (by the library's primary author, no less), you can use the A* (astar) algorithm:

use petgraph::{algo, prelude::*}; // 0.5.1

fn main() {
    let mut graph = Graph::new();

    let a = graph.add_node("a");
    let b = graph.add_node("b");
    let c = graph.add_node("c");
    let d = graph.add_node("d");

    graph.extend_with_edges(&[(a, b, 1), (b, c, 1), (c, d, 1), (a, b, 1), (b, d, 1)]);

    let path = algo::astar(
        &graph,
        a,               // start
        |n| n == d,      // is_goal
        |e| *e.weight(), // edge_cost
        |_| 0,           // estimate_cost
    );

    match path {
        Some((cost, path)) => {
            println!("The total cost was {}: {:?}", cost, path);
        }
        None => println!("There was no path"),
    }
}
The total cost was 2: [NodeIndex(0), NodeIndex(1), NodeIndex(3)]
like image 153
Shepmaster Avatar answered Sep 29 '22 08:09

Shepmaster