rs_graph/shortestpath/bidijkstra.rs
1// Copyright (c) 2016, 2017, 2018, 2021, 2022 Frank Fischer <frank-fischer@shadow-soft.de>
2//
3// This program is free software: you can redistribute it and/or
4// modify it under the terms of the GNU General Public License as
5// published by the Free Software Foundation, either version 3 of the
6// License, or (at your option) any later version.
7//
8// This program is distributed in the hope that it will be useful, but
9// WITHOUT ANY WARRANTY; without even the implied warranty of
10// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11// General Public License for more details.
12//
13// You should have received a copy of the GNU General Public License
14// along with this program. If not, see <http://www.gnu.org/licenses/>
15//
16
17#![allow(clippy::type_complexity)]
18
19//! Dijkstra's bidirectional shortest path algorithm.
20//!
21//! The bidirectional Dijkstra algorithm starts the search at the start and the
22//! end node simultaneously. The shortest path is found if the two searches
23//! meet.
24//!
25//! This is actually a [bidirectional A*-search][crate::search::biastar] with
26//! the all zero node potential.
27//!
28//! # Example
29//!
30//! ```
31//! use rs_graph::{LinkedListGraph, Builder, traits::*};
32//! use rs_graph::adjacencies::Neighbors;
33//! use rs_graph::shortestpath::bidijkstra;
34//! use rs_graph::string::from_ascii;
35//!
36//! let data = from_ascii::<LinkedListGraph>(r"
37//! a-----9-----b
38//! / \ \
39//! | 2 6
40//! | \ \
41//! 14 c-----8-----d
42//! | / \ /
43//! | 10 12 15
44//! \ / \ /
45//! e----7--f----
46//! ").unwrap();
47//! let g = data.graph;
48//! let weights = data.weights;
49//! let nodes = data.nodes;
50//! let a = nodes[&'a'];
51//! let b = nodes[&'b'];
52//! let c = nodes[&'c'];
53//! let d = nodes[&'d'];
54//! let e = nodes[&'e'];
55//! let f = nodes[&'f'];
56//!
57//! // ensure that the nodes are visited in the correct order
58//! let visited = bidijkstra::start(&Neighbors(&g), &Neighbors(&g), g.id2node(e), g.id2node(b), |e| weights[e.index()])
59//! .map(|(u, _ , d)| (g.node_id(u), d))
60//! .collect::<Vec<_>>();
61//! assert_eq!(visited, vec![(d, 6), (f, 7), (a, 9), (c, 10), (a, 12)]);
62//!
63//! // obtain the shortest path directly.
64//! let (path, dist) = bidijkstra::find_undirected_path(&g, g.id2node(e), g.id2node(b), |e| weights[e.index()]).unwrap();
65//!
66//! assert_eq!(dist, 21);
67//!
68//! let path = path.into_iter()
69//! .map(|e| g.enodes(e))
70//! .map(|(u,v)| (g.node_id(u), g.node_id(v)))
71//! .map(|(u,v)| if u > v { (v,u) } else { (u,v) })
72//! .collect::<Vec<_>>();
73//! assert_eq!(path, vec![(c,e), (a,c), (a,b)]);
74//! ```
75
76use crate::adjacencies::{Adjacencies, InEdges, Neighbors, OutEdges};
77use crate::collections::{ItemMap, ItemPriQueue};
78use crate::search::biastar::{self, BiAStar, BiData, DefaultMap, DefaultPriQueue, Direction};
79pub use crate::shortestpath::dijkstra::NoHeur;
80use crate::traits::{Digraph, Graph};
81
82use either::Either;
83use num_traits::Zero;
84use std::hash::Hash;
85use std::ops::{Add, Sub};
86
87/// Bi-Dijkstra search iterator.
88pub type BiDijkstra<'a, Aout, Ain, D, W, M, P> = BiAStar<'a, Aout, Ain, D, W, M, P, NoHeur>;
89
90/// Bi-Dijkstra search iterator with default data structures.
91pub type BiDijkstraDefault<'a, Aout, Ain, D, W> =
92 BiDijkstra<'a, Aout, Ain, D, W, DefaultMap<'a, Aout, D, NoHeur>, DefaultPriQueue<'a, Aout, D, NoHeur>>;
93
94/// Compute a shortest path with bidirectional Dijkstra algorithm using default data structures.
95///
96/// This is a convenience wrapper around [`start_with_data`] using the default
97/// data structures returned by
98/// [`DefaultData`][crate::search::biastar::DefaultData].
99///
100/// # Parameters
101/// - `adjout`: adjacency information for the forward search (i.e outgoing edges)
102/// - `adjin`: adjacency information for the backward search (i.e incoming edges)
103/// - `src`: the source node at which the path should start.
104/// - `snk`: the snk node at which the path should end.
105/// - `weights`: the (non-negative) weight function for each edge
106pub fn start<'a, Aout, Ain, D, W>(
107 adjout: Aout,
108 adjin: Ain,
109 src: Aout::Node,
110 snk: Aout::Node,
111 weights: W,
112) -> BiDijkstraDefault<'a, Aout, Ain, D, W>
113where
114 Aout: Adjacencies<'a>,
115 Aout::Node: Hash,
116 Ain: Adjacencies<'a, Node = Aout::Node, Edge = Aout::Edge>,
117 D: Copy + PartialOrd + Zero,
118 W: Fn(Aout::Edge) -> D,
119{
120 start_with_data(adjout, adjin, src, snk, weights, biastar::DefaultData::default())
121}
122
123/// Run bidirectional Dijkstra on a generic graph with custom data structures.
124///
125/// The returned iterator traverses the edges in the order of a bidirectional
126/// Dijkstra-search. The iterator returns the next node, its incoming edge with
127/// direction information and the distance to the start node or end node
128/// depending on the direction.
129///
130/// Note that the start and end nodes are *not* returned by the iterator.
131///
132/// The algorithm requires a pair `(M, P)` with `M` implementing
133/// [`ItemMap<Direction<Node>, Item>`][crate::collections::ItemMap], and `P`
134/// implementing [`ItemPriQueue<Direction<Node>,
135/// D>`][crate::collections::ItemStack] as internal data structures. The map is
136/// used to store information about the last edge on a shortest path for each
137/// reachable node. The priority queue is used the handle the nodes in the
138/// correct order. The data structures can be reused for multiple searches.
139///
140/// # Parameters
141/// - `adjout`: adjacency information for the forward search (i.e outgoing edges)
142/// - `adjin`: adjacency information for the backward search (i.e incoming edges)
143/// - `src`: the source node at which the path should start.
144/// - `snk`: the snk node at which the path should end.
145/// - `weights`: the (non-negative) weight function for each edge
146/// - `heur`: the heuristic used in the search
147/// - `data`: the data structures
148pub fn start_with_data<'a, Aout, Ain, D, W, M, P>(
149 adjout: Aout,
150 adjin: Ain,
151 src: Aout::Node,
152 snk: Aout::Node,
153 weights: W,
154 data: (M, P),
155) -> BiDijkstra<'a, Aout, Ain, D, W, M, P>
156where
157 Aout: Adjacencies<'a>,
158 Ain: Adjacencies<'a, Node = Aout::Node, Edge = Aout::Edge>,
159 D: Copy + PartialOrd + Zero,
160 W: Fn(Aout::Edge) -> D,
161 M: ItemMap<Direction<Aout::Node>, Either<P::Item, D>>,
162 P: ItemPriQueue<Direction<Aout::Node>, BiData<Aout::Edge, D, NoHeur>>,
163{
164 biastar::start_with_data(adjout, adjin, src, snk, weights, NoHeur, data)
165}
166
167/// Start a bidirectional Dijkstra-search on an undirected graph.
168///
169/// Each edge can be traversed in both directions with the same weight.
170///
171/// This is a convenience wrapper to start the search on an undirected graph
172/// with the default data structures.
173///
174/// # Parameters
175///
176/// - `g`: the undirected graph
177/// - `src`: the source node at which the path should start.
178/// - `snk`: the snk node at which the path should end.
179/// - `weights`: the weight function for each edge
180pub fn start_undirected<'a, G, D, W>(
181 g: &'a G,
182 src: G::Node<'a>,
183 snk: G::Node<'a>,
184 weights: W,
185) -> BiDijkstraDefault<'a, Neighbors<'a, G>, Neighbors<'a, G>, D, W>
186where
187 G: Graph,
188 G::Node<'a>: Hash,
189 D: Copy + PartialOrd + Zero,
190 W: Fn(G::Edge<'a>) -> D,
191{
192 start(Neighbors(g), Neighbors(g), src, snk, weights)
193}
194
195/// Run a bidirectional Dijkstra-search on an undirected graph and return the path.
196///
197/// Each edge can be traversed in both directions with the same weight.
198///
199/// This is a convenience wrapper to run the search on an undirected graph
200/// with the default data structures and obtain the shortest path.
201///
202/// # Parameters
203///
204/// - `g`: the undirected graph
205/// - `src`: the source node at which the path should start.
206/// - `snk`: the snk node at which the path should end.
207/// - `weights`: the weight function for each edge
208///
209/// The function returns the edges on the path and its length.
210pub fn find_undirected_path<'a, G, D, W>(
211 g: &'a G,
212 src: G::Node<'a>,
213 snk: G::Node<'a>,
214 weights: W,
215) -> Option<(Vec<G::Edge<'a>>, D)>
216where
217 G: Graph,
218 G::Node<'a>: Hash,
219 D: Copy + PartialOrd + Zero + Add<D, Output = D> + Sub<D, Output = D>,
220 W: Fn(G::Edge<'a>) -> D,
221{
222 biastar::find_undirected_path(g, src, snk, weights, NoHeur)
223}
224
225/// Start a bidirectional Dijkstra-search on a directed graph.
226///
227/// This is a convenience wrapper to start the search on an directed graph
228/// with the default data structures.
229///
230/// # Parameters
231///
232/// - `g`: the directed graph
233/// - `src`: the source node at which the path should start.
234/// - `snk`: the snk node at which the path should end.
235/// - `weights`: the weight function for each edge
236/// - `heur`: the heuristic used in the search
237pub fn start_directed<'a, G, D, W>(
238 g: &'a G,
239 src: G::Node<'a>,
240 snk: G::Node<'a>,
241 weights: W,
242) -> BiDijkstraDefault<'a, OutEdges<'a, G>, InEdges<'a, G>, D, W>
243where
244 G: Digraph,
245 G::Node<'a>: Hash,
246 D: Copy + PartialOrd + Zero,
247 W: Fn(G::Edge<'a>) -> D,
248{
249 start(OutEdges(g), InEdges(g), src, snk, weights)
250}
251
252/// Run a bidirectional Dijkstra-search on an directed graph and return the path.
253///
254/// This is a convenience wrapper to run the search on an directed graph
255/// with the default data structures and obtain the shortest path.
256///
257/// # Parameters
258///
259/// - `g`: the directed graph
260/// - `src`: the source node at which the path should start.
261/// - `snk`: the snk node at which the path should end.
262/// - `weights`: the weight function for each edge
263///
264/// The function returns the edges on the path and its length.
265pub fn find_directed_path<'a, G, D, W>(
266 g: &'a G,
267 src: G::Node<'a>,
268 snk: G::Node<'a>,
269 weights: W,
270) -> Option<(Vec<G::Edge<'a>>, D)>
271where
272 G: Digraph,
273 G::Node<'a>: Hash,
274 D: Copy + PartialOrd + Zero + Add<D, Output = D> + Sub<D, Output = D>,
275 W: Fn(G::Edge<'a>) -> D,
276{
277 biastar::find_directed_path(g, src, snk, weights, NoHeur)
278}