rs_graph/shortestpath/
dijkstra.rs

1/*
2 * Copyright (c) 2017, 2018, 2021, 2022 Frank Fischer <frank-fischer@shadow-soft.de>
3 *
4 * This program is free software: you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation, either version 3 of the
7 * License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program.  If not, see  <http://www.gnu.org/licenses/>
16 */
17
18//! Dijkstra's shortest path algorithm.
19//!
20//! Dijkstra's algorithm computes the shortest path from some start node $s \in
21//! V$ to all other nodes in (directed or undirected) graph. Each edge is
22//! assigned a non-negative weight (or length) $w \colon E \to \mathbb{R}_+$.
23//!
24//! Dijkstra's algorithm is essentially an [A*-search][crate::search::astar]
25//! using the zero potential (heuristic) for all nodes.
26//!
27//! # Example
28//!
29//! ```
30//! use rs_graph::{LinkedListGraph, Builder, traits::*};
31//! use rs_graph::adjacencies::Neighbors;
32//! use rs_graph::shortestpath::dijkstra;
33//! use rs_graph::string::from_ascii;
34//!
35//! let data = from_ascii::<LinkedListGraph>(r"
36//!     a-----9-----b
37//!    / \           \
38//!   |   2           6
39//!   |    \           \
40//!  14     c-----8-----d
41//!   |    / \         /
42//!   |   9  10      15
43//!    \ /     \     /
44//!     e----7--f----
45//! ").unwrap();
46//! let g = data.graph;
47//! let weights = data.weights;
48//! let nodes = data.nodes;
49//! let a = nodes[&'a'];
50//! let b = nodes[&'b'];
51//! let c = nodes[&'c'];
52//! let d = nodes[&'d'];
53//! let e = nodes[&'e'];
54//! let f = nodes[&'f'];
55//!
56//! let mut preds: Vec<_> = dijkstra::start(&Neighbors(&g), g.id2node(e), |e| weights[e.index()])
57//!     .map(|(u, e, d)| {
58//!         let uv = g.enodes(e);
59//!         if uv.0 == u { (uv.1, u, d) } else { (uv.0, u, d) }
60//!     })
61//!     .map(|(u, v, d)| (g.node_id(u), g.node_id(v), d))
62//!     .collect();
63//!
64//! assert_eq!(preds, vec![(e,f,7), (e,c,9), (c,a,11), (c,d,17), (a,b,20)]);
65//!
66//! let (path, dist) = dijkstra::find_undirected_path(&g, g.id2node(e), g.id2node(b), |e| weights[e.index()]).unwrap();
67//!
68//! assert_eq!(dist, 20);
69//!
70//! let path = path
71//!     .into_iter()
72//!     .map(|e| g.enodes(e))
73//!     .map(|(u,v)| (g.node_id(u), g.node_id(v)))
74//!     .map(|(u,v)| if u < v { (u,v) } else { (v,u) })
75//!     .collect::<Vec<_>>();
76//! assert_eq!(path, vec![(c,e), (a,c), (a,b)]);
77//! ```
78
79use crate::adjacencies::{Adjacencies, Neighbors, OutEdges};
80use crate::collections::{ItemMap, ItemPriQueue};
81use crate::search::astar::{
82    self, AStar, AStarHeuristic, Accumulator, Data, DefaultMap, DefaultPriQueue, SumAccumulator,
83};
84use crate::traits::{Digraph, Graph};
85
86use crate::num::traits::Zero;
87use std::hash::Hash;
88use std::ops::{Add, Neg, Sub};
89
90/// Internal type used when no heuristic is required.
91///
92/// Used for standard Dijkstra.
93#[derive(Clone, Copy, Default)]
94pub struct NoHeur;
95
96impl<N> AStarHeuristic<N> for NoHeur {
97    type Result = NoHeur;
98
99    fn call(&self, _u: N) -> NoHeur {
100        NoHeur
101    }
102}
103
104impl<T> Add<T> for NoHeur {
105    type Output = T;
106    fn add(self, x: T) -> T {
107        x
108    }
109}
110
111impl Neg for NoHeur {
112    type Output = NoHeur;
113    fn neg(self) -> Self {
114        self
115    }
116}
117
118/// Dijkstra search iterator.
119pub type Dijkstra<'a, A, D, W, M, P, Accum> = AStar<'a, A, D, W, M, P, NoHeur, Accum>;
120
121/// The Dijkstra-iterator with default types.
122pub type DijkstraDefault<'a, A, D, W> =
123    Dijkstra<'a, A, D, W, DefaultMap<'a, A, D, NoHeur>, DefaultPriQueue<'a, A, D, NoHeur>, SumAccumulator>;
124
125/// Start and return an Dijkstra-iterator using default data structures.
126///
127/// This is a convenience wrapper around [`start_with_data`] using the default
128/// data structures [`DefaultData`][crate::search::astar::DefaultData].
129///
130/// # Parameters
131///
132/// - `adj`: adjacency information for the graph
133/// - `src`: the source node at which the search should start.
134/// - `weights`: the weight function for each edge
135pub fn start<'a, A, D, W>(adj: A, src: A::Node, weights: W) -> DijkstraDefault<'a, A, D, W>
136where
137    A: Adjacencies<'a>,
138    A::Node: Hash,
139    D: Copy + PartialOrd + Zero,
140    W: Fn(A::Edge) -> D,
141{
142    start_with_data(adj, src, weights, astar::DefaultData::default())
143}
144
145/// Start and return an Dijkstra-iterator with custom data structures.
146///
147/// The returned iterator traverses the edges in the order of an Dijkstra-search. The
148/// iterator returns the next node, its incoming edge and the distance to the
149/// start node.
150///
151/// Note that the start node is *not* returned by the iterator.
152///
153/// The algorithm requires a pair `(M, P)` with `M` implementing [`ItemMap<Node,
154/// Item>`][crate::collections::ItemMap], and `P` implementing
155/// [`ItemPriQueue<Node, D>`][crate::collections::ItemStack] as internal data
156/// structures. The map is used to store information about the last edge on a
157/// shortest path for each reachable node. The priority queue is used the handle
158/// the nodes in the correct order. The data structures can be reused for
159/// multiple searches.
160///
161/// # Parameters
162///
163/// - `adj`: adjacency information for the graph
164/// - `src`: the source node at which the search should start.
165/// - `weights`: the weight function for each edge
166/// - `data`: the custom data structures
167pub fn start_with_data<'a, A, D, W, M, P>(
168    adj: A,
169    src: A::Node,
170    weights: W,
171    data: (M, P),
172) -> Dijkstra<'a, A, D, W, M, P, SumAccumulator>
173where
174    A: Adjacencies<'a>,
175    D: Copy + PartialOrd + Zero,
176    W: Fn(A::Edge) -> D,
177    M: ItemMap<A::Node, Option<P::Item>>,
178    P: ItemPriQueue<A::Node, Data<A::Edge, D, NoHeur>>,
179{
180    start_generic(adj, src, weights, data)
181}
182
183/// Start and return an Dijkstra-iterator with a custom accumulator and custom
184/// data structures.
185///
186/// This function differs from [`start_with_data`] in the additional type
187/// parameter `Accum`. The type parameter is the accumulation function for
188/// combining the length to the previous node with the weight of the current
189/// edge. It is usually just the sum ([`SumAccumulator`]). One possible use is
190/// the Prim's algorithm for the minimum spanning tree problem (see
191/// [`mst::prim`](crate::mst::prim())).
192pub fn start_generic<'a, A, D, W, M, P, Accum>(
193    adj: A,
194    src: A::Node,
195    weights: W,
196    data: (M, P),
197) -> Dijkstra<'a, A, D, W, M, P, Accum>
198where
199    A: Adjacencies<'a>,
200    D: Copy + PartialOrd + Zero,
201    W: Fn(A::Edge) -> D,
202    M: ItemMap<A::Node, Option<P::Item>>,
203    P: ItemPriQueue<A::Node, Data<A::Edge, D, NoHeur>>,
204    Accum: Accumulator<D>,
205{
206    astar::start_generic(adj, src, weights, NoHeur, data)
207}
208
209/// Start a Dijkstra-search on a undirected graph.
210///
211/// Each edge can be traversed in both directions with the same weight.
212///
213/// This is a convenience wrapper to start the search on an undirected graph
214/// with the default data structures.
215///
216/// # Parameter
217/// - `g`: the graph
218/// - `weights`: the (non-negative) edge weights
219/// - `src`: the source node
220/// - `heur`: the lower bound heuristic
221pub fn start_undirected<'a, G, D, W>(
222    g: &'a G,
223    src: G::Node<'a>,
224    weights: W,
225) -> DijkstraDefault<'a, Neighbors<'a, G>, D, W>
226where
227    G: Graph,
228    G::Node<'a>: Hash,
229    D: Copy + PartialOrd + Zero,
230    W: Fn(G::Edge<'a>) -> D,
231{
232    start(Neighbors(g), src, weights)
233}
234
235/// Run a Dijkstra-search on an undirected graph and return the path.
236///
237/// Each edge can be traversed in both directions with the same weight.
238///
239/// This is a convenience wrapper to run the search on an undirected graph with
240/// the default data structures and return the resulting path from `src` to
241/// `snk`.
242///
243/// # Parameter
244/// - `g`: the graph
245/// - `weights`: the (non-negative) edge weights
246/// - `src`: the source node
247/// - `snk`: the sink node
248///
249/// The function returns the edges on the path and its length.
250pub fn find_undirected_path<'a, G, D, W>(
251    g: &'a G,
252    src: G::Node<'a>,
253    snk: G::Node<'a>,
254    weights: W,
255) -> Option<(Vec<G::Edge<'a>>, D)>
256where
257    G: Graph,
258    G::Node<'a>: Hash,
259    D: 'a + Copy + PartialOrd + Zero + Add<D, Output = D> + Sub<D, Output = D>,
260    W: Fn(G::Edge<'a>) -> D,
261{
262    astar::find_undirected_path(g, src, snk, weights, NoHeur)
263}
264
265/// Start a Dijkstra-search on a directed graph.
266///
267/// This is a convenience wrapper to start the search on an directed graph
268/// with the default data structures.
269///
270/// # Parameter
271/// - `g`: the graph
272/// - `weights`: the (non-negative) edge weights
273/// - `src`: the source node
274pub fn start_directed<'a, G, D, W>(g: &'a G, src: G::Node<'a>, weights: W) -> DijkstraDefault<'a, OutEdges<'a, G>, D, W>
275where
276    G: Digraph,
277    G::Node<'a>: Hash,
278    D: Copy + PartialOrd + Zero,
279    W: Fn(G::Edge<'a>) -> D,
280{
281    start(OutEdges(g), src, weights)
282}
283
284/// Run a Dijkstra-search on a directed graph and return the path.
285///
286/// This is a convenience wrapper to run the search on an directed graph with
287/// the default data structures and return the resulting path from `src` to
288/// `snk`.
289///
290/// # Parameter
291/// - `g`: the graph
292/// - `weights`: the (non-negative) edge weights
293/// - `src`: the source node
294/// - `snk`: the sink node
295///
296/// The function returns the edges on the path and its length.
297pub fn find_directed_path<'a, G, D, W>(
298    g: &'a G,
299    src: G::Node<'a>,
300    snk: G::Node<'a>,
301    weights: W,
302) -> Option<(Vec<G::Edge<'a>>, D)>
303where
304    G: Digraph,
305    G::Node<'a>: Hash,
306    D: 'a + Copy + PartialOrd + Zero + Add<D, Output = D> + Sub<D, Output = D>,
307    W: Fn(G::Edge<'a>) -> D,
308{
309    astar::find_directed_path(g, src, snk, weights, NoHeur)
310}