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}