Skip to main content

rs_graph/search/
biastar.rs

1/*
2 * Copyright (c) 2019, 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//! Bidirectional A*-search.
19//!
20//! This module implements a bidirectional A*-search for finding a shortest path
21//! between two nodes starting from both endpoints. Each node may be assigned a
22//! potential (or "heuristic value") estimating the distance to the target
23//! nodes. The potential $h\colon V \to \mathbb{R}$ must satisfy
24//! \\[ w(u,v) - h(u) + h(v) \ge 0, (u,v) \in E \\]
25//! where $w\colon E \to \mathbb{R}$ are the weights (or lengths) of the edges.
26//! (The relation must hold for both directions in case the graph is
27//! undirected).
28//!
29//! If $s,t \in V$ are the start and destination nodes of the path,
30//! respectively, and $h_s\colon V \to \mathbb{R}$ and $h_t\colon V \to
31//! \mathbb{R}$ are lower bounds for the distance from each node $u \in V$ to
32//! $s$ and $t$, then the canonical choice of $h$ is \\[ h\colon u \to
33//! \mathbb{R}, u \mapsto \frac12 (h_s(u) - h_t(u)). \\]
34//!
35//! # Example
36//!
37//! ```
38//! use rs_graph::traits::*;
39//! use rs_graph::search::biastar;
40//! use rs_graph::string::{from_ascii, Data};
41//! use rs_graph::LinkedListGraph;
42//!
43//! let Data {
44//!     graph: g,
45//!     weights,
46//!     nodes,
47//! } = from_ascii::<LinkedListGraph>(
48//!     r"
49//! *--2--*--2--*--2--*--2--*--2--*--2--*--2--*--2--*--2--*
50//! |     |     |     |     |     |     |     |     |     |
51//! 2     2     2     2     2     2     2     2     2     2
52//! |     |     |     |     |     |     |     |     |     |
53//! *--2--*--2--*--2--*--2--*--3--e--2--f--2--t--2--*--2--*
54//! |     |     |     |     |     |     |     |     |     |
55//! 2     2     2     2     2     2     3     2     2     2
56//! |     |     |     |     |     |     |     |     |     |
57//! *--2--*--2--*--3--*--3--c--2--d--2--*--3--*--2--*--2--*
58//! |     |     |     |     |     |     |     |     |     |
59//! 2     2     2     2     2     3     2     2     2     2
60//! |     |     |     |     |     |     |     |     |     |
61//! *--2--*--2--s--2--a--2--b--2--*--2--*--3--*--2--*--2--*
62//! |     |     |     |     |     |     |     |     |     |
63//! 2     2     2     2     2     2     2     2     2     2
64//! |     |     |     |     |     |     |     |     |     |
65//! *--2--*--2--*--2--*--2--*--2--*--2--*--2--*--2--*--2--*
66//! ",
67//! )
68//! .unwrap();
69//!
70//! let s = g.id2node(nodes[&'s']);
71//! let t = g.id2node(nodes[&'t']);
72//!
73//! // nodes are numbered row-wise -> get node coordinates
74//! let coords = |u| ((g.node_id(u) % 10) as isize, (g.node_id(u) / 10) as isize);
75//!
76//! let (xs, ys) = coords(s);
77//! let (xt, yt) = coords(t);
78//!
79//! // Manhatten distance heuristic
80//! let manh_heur = |u| {
81//!     let (x, y) = coords(u);
82//!     0.5 * (((x - xt).abs() + (y - yt).abs()) as f64 - ((x - xs).abs() + (y - ys).abs()) as f64)
83//! };
84//!
85//! let (path, dist) = biastar::find_undirected_path(&g, s, t, |e| weights[e.index()] as f64, manh_heur).unwrap();
86//!
87//! assert!((dist - 14.0).abs() < 1e-6);
88//!
89//! let mut pathnodes = vec![s];
90//! for e in path {
91//!     let uv = g.enodes(e);
92//!     if uv.0 == *pathnodes.last().unwrap() {
93//!         pathnodes.push(uv.1);
94//!     } else {
95//!         pathnodes.push(uv.0);
96//!     }
97//! }
98//!
99//! assert_eq!(pathnodes, "sabcdeft".chars().map(|c| g.id2node(nodes[&c])).collect::<Vec<_>>());
100//!
101//! // verify that we did not go too far in the "wrong" direction
102//! for (v, _, _) in biastar::start_undirected(&g, s, t, |e| weights[e.index()] as f64, manh_heur) {
103//!     let (x, y) = coords(v);
104//!     assert!(x + 1 >= xs && x <= xt + 1 && y + 1 >= yt && y <= ys + 1);
105//! }
106//! ```
107
108use crate::adjacencies::{Adjacencies, InEdges, Neighbors, OutEdges};
109use crate::collections::{BinHeap, ItemMap, ItemPriQueue};
110pub use crate::search::astar::AStarHeuristic as Heuristic;
111use crate::search::path_from_incomings;
112use crate::traits::{Digraph, Graph};
113
114use either::Either::{self, Left, Right};
115use num_traits::Zero;
116
117use std::cmp::Ordering;
118use std::collections::HashMap;
119use std::hash::Hash;
120use std::ops::{Add, Neg, Sub};
121
122pub use super::astar::DefaultData;
123
124/// Direction of search.
125#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
126pub enum Direction<E> {
127    /// Edge in forward search.
128    Forward(E),
129    /// Edge in backward search.
130    Backward(E),
131}
132
133/// Predecessor edge information.
134#[derive(Clone, Copy, Debug)]
135pub struct BiData<E, D, H> {
136    /// adjacent edge
137    edge: E,
138    /// distance to source or sink (None if never seen).
139    distance: D,
140    /// the lower bound of this node
141    lower: H,
142}
143
144impl<E, D, H> PartialEq for BiData<E, D, H>
145where
146    D: PartialEq,
147{
148    fn eq(&self, data: &Self) -> bool {
149        self.distance.eq(&data.distance)
150    }
151}
152
153impl<E, D, H> PartialOrd for BiData<E, D, H>
154where
155    D: PartialOrd + Clone,
156    H: Add<D, Output = D> + Clone,
157{
158    fn partial_cmp(&self, data: &Self) -> Option<Ordering> {
159        (self.lower.clone() + self.distance.clone()).partial_cmp(&(data.lower.clone() + data.distance.clone()))
160    }
161}
162
163/// Information about the meeting edge.
164struct Meet<N, E, D> {
165    /// The destination node of the connecting edge.
166    node: N,
167    /// The connecting edge.
168    edge: E,
169    /// The forward distance of the node.
170    fwd_distance: D,
171    /// The total distance of the path.
172    total_distance: D,
173}
174
175/// Iterator for visiting edges in A*-order.
176pub struct BiAStar<'a, Aout, Ain, D, W, M, P, H>
177where
178    Aout: Adjacencies<'a>,
179    Ain: Adjacencies<'a, Node = Aout::Node, Edge = Aout::Edge>,
180    M: ItemMap<Direction<Aout::Node>, Either<P::Item, D>>,
181    P: ItemPriQueue<Direction<Aout::Node>, BiData<Aout::Edge, D, H::Result>>,
182    D: Copy,
183    W: Fn(Aout::Edge) -> D,
184    H: Heuristic<Aout::Node>,
185    H::Result: Copy,
186{
187    adjout: Aout,
188    adjin: Ain,
189    nodes: M,
190    pqueue: P,
191    weights: W,
192    heur: H,
193    /// The meet information, i.e. the final connecting edge.
194    meet: Option<Meet<Aout::Node, Aout::Edge, D>>,
195    /// The currently top-most (i.e. smallest) value in forward direction on the heap.
196    top_fwd: D,
197    /// The currently top-most (i.e. smallest) value in backward direction on the heap.
198    top_bwd: D,
199}
200
201/// Default map type to be used in an A* search.
202///
203/// - `A` is the graph type information
204/// - `D` is the type of distance values
205/// - `H` is the type of heuristic values
206pub type DefaultMap<'a, A, D, H> = HashMap<
207    Direction<<A as Adjacencies<'a>>::Node>,
208    Either<
209        <BinHeap<Direction<<A as Adjacencies<'a>>::Node>, BiData<<A as Adjacencies<'a>>::Edge, D, H>> as ItemPriQueue<
210            Direction<<A as Adjacencies<'a>>::Node>,
211            BiData<<A as Adjacencies<'a>>::Edge, D, H>,
212        >>::Item,
213        D,
214    >,
215>;
216
217/// Default priority queue type to be used in an A* search.
218///
219/// - `A` is the graph type information
220/// - `D` is the type of distance values
221/// - `H` is the type of heuristic values
222/// - `ID` is used for identifying items on the heap internally
223pub type DefaultPriQueue<'a, A, D, H, ID = u32> =
224    BinHeap<Direction<<A as Adjacencies<'a>>::Node>, BiData<<A as Adjacencies<'a>>::Edge, D, H>, ID>;
225
226/// BiAStar iterator with default data structures.
227pub type BiAStarDefault<'a, Aout, Ain, D, W, H> = BiAStar<
228    'a,
229    Aout,
230    Ain,
231    D,
232    W,
233    DefaultMap<'a, Aout, D, <H as Heuristic<<Aout as Adjacencies<'a>>::Node>>::Result>,
234    DefaultPriQueue<'a, Aout, D, <H as Heuristic<<Aout as Adjacencies<'a>>::Node>>::Result>,
235    H,
236>;
237
238/// Start and return a bidirectional A*-iterator using default data structures.
239///
240/// This is a convenience wrapper around [`start_with_data`] using the default
241/// data structures [`DefaultData`].
242///
243/// # Parameters
244/// - `adjout`: adjacency information for the forward search (i.e outgoing edges)
245/// - `adjin`: adjacency information for the backward search (i.e incoming edges)
246/// - `src`: the source node at which the path should start.
247/// - `snk`: the snk node at which the path should end.
248/// - `weights`: the weight function for each edge
249/// - `heur`: the heuristic used in the search
250pub fn start<'a, Aout, Ain, D, W, H>(
251    adjout: Aout,
252    adjin: Ain,
253    src: Aout::Node,
254    snk: Aout::Node,
255    weights: W,
256    heur: H,
257) -> BiAStarDefault<'a, Aout, Ain, D, W, H>
258where
259    Aout: Adjacencies<'a>,
260    Aout::Node: Hash + Eq,
261    Ain: Adjacencies<'a, Node = Aout::Node, Edge = Aout::Edge>,
262    D: Copy + Zero + PartialOrd,
263    W: Fn(Aout::Edge) -> D,
264    H: Heuristic<Aout::Node>,
265    H::Result: Add<D, Output = D> + Add<H::Result, Output = H::Result> + Neg<Output = H::Result>,
266{
267    start_with_data(adjout, adjin, src, snk, weights, heur, DefaultData::default())
268}
269
270/// Start and return a bidirectional A*-iterator.
271///
272/// The returned iterator traverses the edges in the order of a bidirectional
273/// A*-search. The iterator returns the next node, its incoming edge with
274/// direction information and the distance to the start node or end node
275/// depending on the direction.
276///
277/// The heuristic is a assigning a potential to each node. The potential of all
278/// nodes must be so that $w(u,v) - h(u) + h(v) \ge 0$ for all edges $(u,v) \in
279/// E$. The value returned by the heuristic must be compatible with the distance
280/// type, i.e., is must be possible to compute the sum of both.
281///
282/// Note that the start and end nodes are *not* returned by the iterator.
283///
284/// The algorithm requires a pair `(M, P)` with `M` implementing
285/// [`ItemMap<Direction<Node>, Item>`][crate::collections::ItemMap], and `P`
286/// implementing [`ItemPriQueue<Direction<Node>,
287/// D>`][crate::collections::ItemStack] as internal data structures. The map is
288/// used to store information about the last edge on a shortest path for each
289/// reachable node. The priority queue is used the handle the nodes in the
290/// correct order. The data structures can be reused for multiple searches.
291///
292/// # Parameters
293/// - `adjout`: adjacency information for the forward search (i.e outgoing edges)
294/// - `adjin`: adjacency information for the backward search (i.e incoming edges)
295/// - `src`: the source node at which the path should start.
296/// - `snk`: the snk node at which the path should end.
297/// - `weights`: the weight function for each edge
298/// - `heur`: the heuristic used in the search
299/// - `data`: the data structures
300pub fn start_with_data<'a, Aout, Ain, D, W, H, M, P>(
301    adjout: Aout,
302    adjin: Ain,
303    src: Aout::Node,
304    snk: Aout::Node,
305    weights: W,
306    heur: H,
307    data: (M, P),
308) -> BiAStar<'a, Aout, Ain, D, W, M, P, H>
309where
310    Aout: Adjacencies<'a>,
311    Ain: Adjacencies<'a, Node = Aout::Node, Edge = Aout::Edge>,
312    D: Copy + PartialOrd + Zero,
313    W: Fn(Aout::Edge) -> D,
314    M: ItemMap<Direction<Aout::Node>, Either<P::Item, D>>,
315    P: ItemPriQueue<Direction<Aout::Node>, BiData<Aout::Edge, D, H::Result>>,
316    H: Heuristic<Aout::Node>,
317    H::Result: Add<D, Output = D> + Add<H::Result, Output = H::Result> + Neg<Output = H::Result>,
318{
319    let (mut nodes, mut pqueue) = data;
320    pqueue.clear();
321    nodes.clear();
322
323    if src == snk {
324        // if src == snk then we do not start the search at all
325        return BiAStar {
326            adjout,
327            adjin,
328            nodes,
329            pqueue,
330            weights,
331            heur,
332            meet: None,
333            top_fwd: D::zero(),
334            top_bwd: D::zero(),
335        };
336    }
337
338    nodes.insert(Direction::Forward(src), Right(D::zero()));
339    nodes.insert(Direction::Backward(snk), Right(D::zero()));
340
341    // insert neighbors of source
342    for (e, v) in adjout.neighs(src) {
343        let dir_v = Direction::Forward(v);
344        let d = (weights)(e);
345        match nodes.get_mut(dir_v) {
346            Some(Left(item_v)) => {
347                // node is known but unhandled
348                let (distance, lower) = {
349                    let data = pqueue.value(item_v);
350                    (data.distance, data.lower)
351                };
352
353                if d < distance {
354                    pqueue.decrease_key(
355                        item_v,
356                        BiData {
357                            edge: e,
358                            distance: d,
359                            lower,
360                        },
361                    );
362                }
363            }
364            None => {
365                // node is unknown
366                let item_v = pqueue.push(
367                    dir_v,
368                    BiData {
369                        edge: e,
370                        distance: d,
371                        lower: heur.call(v),
372                    },
373                );
374                nodes.insert(dir_v, Left(item_v));
375            }
376            _ => (), // node has already been handled
377        }
378    }
379
380    let mut meet: Option<Meet<_, _, _>> = None;
381    // insert neighbors of sink
382    for (e, v) in adjin.neighs(snk) {
383        let dir_v = Direction::Backward(v);
384        let d = (weights)(e);
385        if v == src {
386            // found a possible first path, this is only possible for (src, snk)
387            // edges and the length of this path is the edge weight
388            if meet.as_ref().map(|m| d < m.total_distance).unwrap_or(true) {
389                // found a better path -> save
390                meet = Some(Meet {
391                    node: snk,
392                    edge: e,
393                    fwd_distance: d,
394                    total_distance: d,
395                });
396            }
397        }
398        match nodes.get_mut(dir_v) {
399            Some(Left(item_v)) => {
400                // node is known but unhandled
401                let (distance, lower) = {
402                    let data = pqueue.value(item_v);
403                    (data.distance, data.lower)
404                };
405                if d < distance {
406                    pqueue.decrease_key(
407                        item_v,
408                        BiData {
409                            edge: e,
410                            distance: d,
411                            lower,
412                        },
413                    );
414                }
415            }
416            None => {
417                // node is unknown
418                let item_v = pqueue.push(
419                    dir_v,
420                    BiData {
421                        edge: e,
422                        distance: d,
423                        lower: -heur.call(v),
424                    },
425                );
426                nodes.insert(dir_v, Left(item_v));
427            }
428            _ => (), // node has already been handled
429        }
430    }
431
432    BiAStar {
433        adjout,
434        adjin,
435        nodes,
436        pqueue,
437        weights,
438        heur,
439        meet,
440        top_fwd: D::zero(),
441        top_bwd: D::zero(),
442    }
443}
444
445impl<'a, Aout, Ain, D, W, M, P, H> Iterator for BiAStar<'a, Aout, Ain, D, W, M, P, H>
446where
447    Aout: Adjacencies<'a>,
448    Ain: Adjacencies<'a, Node = Aout::Node, Edge = Aout::Edge>,
449    D: Copy + PartialOrd + Add<D, Output = D> + Sub<D, Output = D> + Zero,
450    W: Fn(Aout::Edge) -> D,
451    M: ItemMap<Direction<Aout::Node>, Either<P::Item, D>>,
452    P: ItemPriQueue<Direction<Aout::Node>, BiData<Aout::Edge, D, H::Result>>,
453    H: Heuristic<Aout::Node>,
454    H::Result: Add<D, Output = D> + Add<H::Result, Output = H::Result> + Neg<Output = H::Result>,
455{
456    type Item = (Aout::Node, Direction<Aout::Edge>, D);
457
458    fn next(&mut self) -> Option<Self::Item> {
459        if let Some((dir_u, data)) = self.pqueue.pop_min() {
460            // node is not in the queue anymore, forget its item
461            self.nodes.insert_or_replace(dir_u, Right(data.distance));
462            let (distance, edge) = (data.distance, data.edge);
463
464            // stopping test, first update forward or backward bound
465            //
466            // Note: `top_fwd` and `top_bwd` are actually lower bounds on the
467            // values on the heap because the value with that value has just
468            // been removed ... anyway, it is good enough for us
469            if let Direction::Forward(_) = dir_u {
470                self.top_fwd = data.lower + data.distance;
471            } else {
472                self.top_bwd = data.lower + data.distance;
473            };
474
475            if self
476                .meet
477                .as_ref()
478                .map(|m| m.total_distance <= self.top_fwd + self.top_bwd)
479                .unwrap_or(false)
480            {
481                // stopping condition met, we cannot find a better path
482                self.pqueue.clear();
483
484                // return the final connecting edge
485                let meet = self.meet.as_ref().unwrap();
486                return Some((meet.node, Direction::Forward(meet.edge), meet.fwd_distance));
487            }
488
489            match dir_u {
490                // forward search
491                Direction::Forward(u) => {
492                    // look for neighbors
493                    for (e, v) in self.adjout.neighs(u) {
494                        let dir_v = Direction::Forward(v);
495                        let edge_weight = (self.weights)(e);
496                        let d = distance + edge_weight;
497                        if let Some(Right(rdistance)) = self.nodes.get(Direction::Backward(v)) {
498                            // check whether we found a better path
499                            let new_dist = *rdistance + distance + edge_weight;
500                            if self.meet.as_ref().map(|m| new_dist < m.total_distance).unwrap_or(true) {
501                                // found a better path -> save
502                                self.meet = Some(Meet {
503                                    node: v,
504                                    edge: e,
505                                    fwd_distance: d,
506                                    total_distance: new_dist,
507                                });
508                            }
509                        }
510                        match self.nodes.get_mut(dir_v) {
511                            Some(Left(item_v)) => {
512                                // node is known but unhandled
513                                let (distance, lower) = {
514                                    let data = self.pqueue.value(item_v);
515                                    (data.distance, data.lower)
516                                };
517                                if d < distance {
518                                    self.pqueue.decrease_key(
519                                        item_v,
520                                        BiData {
521                                            edge: e,
522                                            distance: d,
523                                            lower,
524                                        },
525                                    );
526                                }
527                            }
528                            None => {
529                                // node is unknown
530                                let item_v = self.pqueue.push(
531                                    dir_v,
532                                    BiData {
533                                        edge: e,
534                                        distance: d,
535                                        lower: self.heur.call(v),
536                                    },
537                                );
538                                self.nodes.insert(dir_v, Left(item_v));
539                            }
540                            _ => (), // node has already been handled
541                        }
542                    }
543                }
544                // backward search
545                Direction::Backward(u) => {
546                    // look for neighbors
547                    for (e, v) in self.adjin.neighs(u) {
548                        assert!((-self.heur.call(v) + self.heur.call(u)) + (self.weights)(e) >= D::zero());
549                        let dir_v = Direction::Backward(v);
550                        let edge_weight = (self.weights)(e);
551                        let d = distance + edge_weight;
552                        if let Some(Right(rdistance)) = self.nodes.get(Direction::Forward(v)) {
553                            // check whether we found a better path
554                            let new_dist = *rdistance + distance + edge_weight;
555                            if self.meet.as_ref().map(|m| new_dist < m.total_distance).unwrap_or(true) {
556                                // found a better path -> save
557                                self.meet = Some(Meet {
558                                    node: u,
559                                    edge: e,
560                                    fwd_distance: *rdistance + edge_weight,
561                                    total_distance: new_dist,
562                                });
563                            }
564                        }
565                        match self.nodes.get_mut(dir_v) {
566                            Some(Left(item_v)) => {
567                                // node is known but unhandled
568                                let (distance, lower) = {
569                                    let data = self.pqueue.value(item_v);
570                                    (data.distance, data.lower)
571                                };
572                                if d < distance {
573                                    self.pqueue.decrease_key(
574                                        item_v,
575                                        BiData {
576                                            edge: e,
577                                            distance: d,
578                                            lower,
579                                        },
580                                    );
581                                }
582                            }
583                            None => {
584                                // node is unknown
585                                let item_v = self.pqueue.push(
586                                    dir_v,
587                                    BiData {
588                                        edge: e,
589                                        distance: d,
590                                        lower: -self.heur.call(v),
591                                    },
592                                );
593                                self.nodes.insert(dir_v, Left(item_v));
594                            }
595                            _ => (), // node has already been handled
596                        }
597                    }
598                }
599            }
600
601            match dir_u {
602                Direction::Forward(u) => Some((u, Direction::Forward(edge), distance)),
603                Direction::Backward(u) => Some((u, Direction::Backward(edge), distance)),
604            }
605        } else {
606            None
607        }
608    }
609}
610
611impl<'a, Aout, Ain, D, W, M, P, H> BiAStar<'a, Aout, Ain, D, W, M, P, H>
612where
613    Aout: Adjacencies<'a>,
614    Ain: Adjacencies<'a, Node = Aout::Node, Edge = Aout::Edge>,
615    D: Copy + PartialOrd + Add<D, Output = D> + Sub<D, Output = D>,
616    W: Fn(Aout::Edge) -> D,
617    M: ItemMap<Direction<Aout::Node>, Either<P::Item, D>>,
618    P: ItemPriQueue<Direction<Aout::Node>, BiData<Aout::Edge, D, H::Result>>,
619    H: Heuristic<Aout::Node>,
620    H::Result: Add<D, Output = D> + Neg<Output = H::Result>,
621{
622    /// Return the meet node.
623    ///
624    /// This is the node where forward and backward search met.
625    fn meet(&self) -> Option<Aout::Node> {
626        self.meet.as_ref().map(|m| m.node)
627    }
628
629    /// Return the value of the shortest path.
630    fn value(&self) -> Option<D> {
631        self.meet.as_ref().map(|m| m.total_distance)
632    }
633}
634
635/// Start a bidirectional A*-search on an undirected graph.
636///
637/// Each edge can be traversed in both directions with the same weight.
638///
639/// This is a convenience wrapper to start the search on an undirected graph
640/// with the default data structures.
641///
642/// # Parameters
643///
644/// - `g`: the undirected graph
645/// - `src`: the source node at which the path should start.
646/// - `snk`: the snk node at which the path should end.
647/// - `weights`: the weight function for each edge
648/// - `heur`: the heuristic used in the search
649pub fn start_undirected<'a, G, D, W, H>(
650    g: &'a G,
651    src: G::Node<'a>,
652    snk: G::Node<'a>,
653    weights: W,
654    heur: H,
655) -> BiAStarDefault<'a, Neighbors<'a, G>, Neighbors<'a, G>, D, W, H>
656where
657    G: Graph,
658    G::Node<'a>: Hash,
659    D: Copy + PartialOrd + Zero,
660    W: Fn(G::Edge<'a>) -> D,
661    H: Heuristic<G::Node<'a>>,
662    H::Result: Add<D, Output = D> + Add<H::Result, Output = H::Result> + Neg<Output = H::Result>,
663{
664    start(Neighbors(g), Neighbors(g), src, snk, weights, heur)
665}
666
667/// Run a bidirectional A*-search on an undirected graph and return the path.
668///
669/// Each edge can be traversed in both directions with the same weight.
670///
671/// This is a convenience wrapper to run the search on an undirected graph
672/// with the default data structures and obtain the shortest path.
673///
674/// # Parameters
675///
676/// - `g`: the undirected graph
677/// - `src`: the source node at which the path should start.
678/// - `snk`: the snk node at which the path should end.
679/// - `weights`: the weight function for each edge
680/// - `heur`: the heuristic used in the search
681///
682/// The function returns the edges on the path and its length.
683pub fn find_undirected_path<'a, G, D, W, H>(
684    g: &'a G,
685    src: G::Node<'a>,
686    snk: G::Node<'a>,
687    weights: W,
688    heur: H,
689) -> Option<(Vec<G::Edge<'a>>, D)>
690where
691    G: Graph,
692    G::Node<'a>: Hash,
693    D: Copy + PartialOrd + Zero + Add<D, Output = D> + Sub<D, Output = D>,
694    W: Fn(G::Edge<'a>) -> D,
695    H: Heuristic<G::Node<'a>>,
696    H::Result: Add<D, Output = D> + Add<H::Result, Output = H::Result> + Neg<Output = H::Result>,
697{
698    if src == snk {
699        return Some((vec![], D::zero()));
700    }
701    // run search until a node has been seen from both sides
702    let mut incoming_edges = HashMap::new();
703    let mut it = start_undirected(g, src, snk, weights, heur);
704    for (u, dir_e, _) in it.by_ref() {
705        match dir_e {
706            Direction::Forward(e) => incoming_edges.insert(Direction::Forward(u), e),
707            Direction::Backward(e) => incoming_edges.insert(Direction::Backward(u), e),
708        };
709    }
710
711    it.meet().map(|meet| {
712        let mut path = path_from_incomings(meet, |u| {
713            incoming_edges
714                .get(&Direction::Forward(u))
715                .map(|&e| (e, g.enodes(e)))
716                .map(|(e, (v, w))| (e, if v == u { w } else { v }))
717        })
718        .collect::<Vec<_>>();
719        path.reverse();
720        path.extend(path_from_incomings(meet, |u| {
721            incoming_edges
722                .get(&Direction::Backward(u))
723                .map(|&e| (e, g.enodes(e)))
724                .map(|(e, (v, w))| (e, if v == u { w } else { v }))
725        }));
726        (path, it.value().unwrap())
727    })
728}
729
730/// Start a bidirectional A*-search on a directed graph.
731///
732/// This is a convenience wrapper to start the search on an directed graph
733/// with the default data structures.
734///
735/// # Parameters
736///
737/// - `g`: the directed graph
738/// - `src`: the source node at which the path should start.
739/// - `snk`: the snk node at which the path should end.
740/// - `weights`: the weight function for each edge
741/// - `heur`: the heuristic used in the search
742pub fn start_directed<'a, G, D, W, H>(
743    g: &'a G,
744    src: G::Node<'a>,
745    snk: G::Node<'a>,
746    weights: W,
747    heur: H,
748) -> BiAStarDefault<'a, OutEdges<'a, G>, InEdges<'a, G>, D, W, H>
749where
750    G: Digraph,
751    G::Node<'a>: Hash,
752    D: Copy + PartialOrd + Zero,
753    W: Fn(G::Edge<'a>) -> D,
754    H: Heuristic<G::Node<'a>>,
755    H::Result: Add<D, Output = D> + Add<H::Result, Output = H::Result> + Neg<Output = H::Result>,
756{
757    start(OutEdges(g), InEdges(g), src, snk, weights, heur)
758}
759
760/// Run a bidirectional A*-search on an directed graph and return the path.
761///
762/// This is a convenience wrapper to run the search on an directed graph
763/// with the default data structures and obtain the shortest path.
764///
765/// # Parameters
766///
767/// - `g`: the directed graph
768/// - `src`: the source node at which the path should start.
769/// - `snk`: the snk node at which the path should end.
770/// - `weights`: the weight function for each edge
771/// - `heur`: the heuristic used in the search
772///
773/// The function returns the edges on the path and its length.
774pub fn find_directed_path<'a, G, D, W, H>(
775    g: &'a G,
776    src: G::Node<'a>,
777    snk: G::Node<'a>,
778    weights: W,
779    heur: H,
780) -> Option<(Vec<G::Edge<'a>>, D)>
781where
782    G: Digraph,
783    G::Node<'a>: Hash,
784    D: Copy + PartialOrd + Zero + Add<D, Output = D> + Sub<D, Output = D>,
785    W: Fn(G::Edge<'a>) -> D,
786    H: Heuristic<G::Node<'a>>,
787    H::Result: Add<D, Output = D> + Add<H::Result, Output = H::Result> + Neg<Output = H::Result>,
788{
789    if src == snk {
790        return Some((vec![], D::zero()));
791    }
792    // run search until a node has been seen from both sides
793    let mut incoming_edges = HashMap::new();
794    let mut it = start_directed(g, src, snk, weights, heur);
795    for (u, dir_e, _) in it.by_ref() {
796        match dir_e {
797            Direction::Forward(e) => incoming_edges.insert(Direction::Forward(u), e),
798            Direction::Backward(e) => incoming_edges.insert(Direction::Backward(u), e),
799        };
800    }
801
802    it.meet().map(|meet| {
803        let mut path = path_from_incomings(meet, |u| {
804            incoming_edges.get(&Direction::Forward(u)).map(|&e| (e, g.src(e)))
805        })
806        .collect::<Vec<_>>();
807        path.reverse();
808        path.extend(path_from_incomings(meet, |u| {
809            incoming_edges.get(&Direction::Backward(u)).map(|&e| (e, g.snk(e)))
810        }));
811
812        (path, it.value().unwrap())
813    })
814}
815
816#[test]
817fn test_biastar() {
818    use crate::search::biastar;
819    use crate::string::{from_ascii, Data};
820    use crate::traits::*;
821    use crate::LinkedListGraph;
822
823    let Data {
824        graph: g,
825        weights,
826        nodes,
827    } = from_ascii::<LinkedListGraph>(
828        r"
829    *--2--*--2--*--2--*--2--*--2--*--2--*--2--*--2--*--2--*
830    |     |     |     |     |     |     |     |     |     |
831    2     2     2     2     2     2     2     2     2     2
832    |     |     |     |     |     |     |     |     |     |
833    *--2--*--2--*--2--*--2--*--3--e--2--f--2--t--2--*--2--*
834    |     |     |     |     |     |     |     |     |     |
835    2     2     2     2     2     2     3     2     2     2
836    |     |     |     |     |     |     |     |     |     |
837    *--2--*--2--*--3--*--3--c--2--d--2--*--3--*--2--*--2--*
838    |     |     |     |     |     |     |     |     |     |
839    2     2     2     2     2     3     2     2     2     2
840    |     |     |     |     |     |     |     |     |     |
841    *--2--*--2--s--2--a--2--b--2--*--2--*--3--*--2--*--2--*
842    |     |     |     |     |     |     |     |     |     |
843    2     2     2     2     2     2     2     2     2     2
844    |     |     |     |     |     |     |     |     |     |
845    *--2--*--2--*--2--*--2--*--2--*--2--*--2--*--2--*--2--*
846    ",
847    )
848    .unwrap();
849
850    let s = g.id2node(nodes[&'s']);
851    let t = g.id2node(nodes[&'t']);
852
853    // nodes are numbered row-wise -> get node coordinates
854    let coords = |u| ((g.node_id(u) % 10) as isize, (g.node_id(u) / 10) as isize);
855
856    let (xs, ys) = coords(s);
857    let (xt, yt) = coords(t);
858
859    // Manhatten distance heuristic
860    let manh_heur = |u| {
861        let (x, y) = coords(u);
862        0.5 * (((x - xt).abs() + (y - yt).abs()) as f64 - ((x - xs).abs() + (y - ys).abs()) as f64)
863    };
864
865    let (path, dist) = biastar::find_undirected_path(&g, s, t, |e| weights[e.index()] as f64, manh_heur).unwrap();
866
867    assert!((dist - 14.0).abs() < 1e-6);
868
869    let mut pathnodes = vec![s];
870    for e in path {
871        let uv = g.enodes(e);
872        if uv.0 == *pathnodes.last().unwrap() {
873            pathnodes.push(uv.1);
874        } else {
875            pathnodes.push(uv.0);
876        }
877    }
878
879    assert_eq!(
880        pathnodes,
881        "sabcdeft".chars().map(|c| g.id2node(nodes[&c])).collect::<Vec<_>>()
882    );
883
884    // verify that we did not go too far in the "wrong" direction
885    for (v, _, _) in biastar::start_undirected(&g, s, t, |e| weights[e.index()] as f64, manh_heur) {
886        let (x, y) = coords(v);
887        assert!(x + 1 >= xs && x <= xt + 1 && y + 1 >= yt && y <= ys + 1);
888    }
889}
890
891#[test]
892fn test_biastar_correct() {
893    use crate::search::biastar;
894    use crate::string::{from_ascii, Data};
895    use crate::traits::*;
896    use crate::LinkedListGraph;
897
898    let Data {
899        graph: g,
900        weights,
901        nodes,
902    } = from_ascii::<LinkedListGraph>(
903        r"
904          b--11--c---1--t
905          |      |
906          1      8
907          |      |
908    s--1--a--10--*
909    ",
910    )
911    .unwrap();
912
913    let s = g.id2node(nodes[&'s']);
914    let t = g.id2node(nodes[&'t']);
915    let a = g.id2node(nodes[&'a']);
916    let b = g.id2node(nodes[&'b']);
917    let c = g.id2node(nodes[&'c']);
918
919    let (path, dist) = biastar::find_undirected_path(&g, s, t, |e| weights[e.index()] as isize, |_| 0).unwrap();
920
921    let length: usize = path.iter().map(|e| weights[e.index()]).sum();
922    assert_eq!(dist, 14);
923    assert_eq!(length, 14);
924
925    let path = path
926        .into_iter()
927        .map(|e| g.enodes(e))
928        .map(|(u, v)| if g.node_id(u) < g.node_id(v) { (u, v) } else { (v, u) })
929        .collect::<Vec<_>>();
930    assert_eq!(path, vec![(s, a), (b, a), (b, c), (c, t)]);
931}