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}