Coverage Report

Created: 2026-03-11 07:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/petgraph-0.8.3/src/algo/dijkstra.rs
Line
Count
Source
1
use alloc::collections::BinaryHeap;
2
use core::hash::Hash;
3
use hashbrown::hash_map::{
4
    Entry::{Occupied, Vacant},
5
    HashMap,
6
};
7
8
use crate::algo::Measure;
9
use crate::scored::MinScored;
10
use crate::visit::{EdgeRef, IntoEdges, IntoEdgesDirected, VisitMap, Visitable};
11
use crate::Direction;
12
13
/// Dijkstra's shortest path algorithm.
14
///
15
/// Compute the length of the shortest path from `start` to every reachable
16
/// node.
17
///
18
/// The function `edge_cost` should return the cost for a particular edge, which is used
19
/// to compute path costs. Edge costs must be non-negative.
20
///
21
/// If `goal` is not `None`, then the algorithm terminates once the `goal` node's
22
/// cost is calculated.
23
///
24
/// # Arguments
25
/// * `graph`: weighted graph.
26
/// * `start`: the start node.
27
/// * `goal`: optional *goal* node.
28
/// * `edge_cost`: closure that returns cost of a particular edge.
29
///
30
/// # Returns
31
/// * `HashMap`: [`struct@hashbrown::HashMap`] that maps `NodeId` to path cost.
32
///
33
/// # Complexity
34
/// * Time complexity: **O((|V|+|E|)log(|V|))**.
35
/// * Auxiliary space: **O(|V|+|E|)**.
36
///
37
/// where **|V|** is the number of nodes and **|E|** is the number of edges.
38
///
39
/// # Example
40
/// ```rust
41
/// use petgraph::Graph;
42
/// use petgraph::algo::dijkstra;
43
/// use petgraph::prelude::*;
44
/// use hashbrown::HashMap;
45
///
46
/// let mut graph: Graph<(), (), Directed> = Graph::new();
47
/// let a = graph.add_node(()); // node with no weight
48
/// let b = graph.add_node(());
49
/// let c = graph.add_node(());
50
/// let d = graph.add_node(());
51
/// let e = graph.add_node(());
52
/// let f = graph.add_node(());
53
/// let g = graph.add_node(());
54
/// let h = graph.add_node(());
55
/// // z will be in another connected component
56
/// let z = graph.add_node(());
57
///
58
/// graph.extend_with_edges(&[
59
///     (a, b),
60
///     (b, c),
61
///     (c, d),
62
///     (d, a),
63
///     (e, f),
64
///     (b, e),
65
///     (f, g),
66
///     (g, h),
67
///     (h, e),
68
/// ]);
69
/// // a ----> b ----> e ----> f
70
/// // ^       |       ^       |
71
/// // |       v       |       v
72
/// // d <---- c       h <---- g
73
///
74
/// let expected_res: HashMap<NodeIndex, usize> = [
75
///     (a, 3),
76
///     (b, 0),
77
///     (c, 1),
78
///     (d, 2),
79
///     (e, 1),
80
///     (f, 2),
81
///     (g, 3),
82
///     (h, 4),
83
/// ].iter().cloned().collect();
84
/// let res = dijkstra(&graph, b, None, |_| 1);
85
/// assert_eq!(res, expected_res);
86
/// // z is not inside res because there is not path from b to z.
87
/// ```
88
0
pub fn dijkstra<G, F, K>(
89
0
    graph: G,
90
0
    start: G::NodeId,
91
0
    goal: Option<G::NodeId>,
92
0
    edge_cost: F,
93
0
) -> HashMap<G::NodeId, K>
94
0
where
95
0
    G: IntoEdges + Visitable,
96
0
    G::NodeId: Eq + Hash,
97
0
    F: FnMut(G::EdgeRef) -> K,
98
0
    K: Measure + Copy,
99
{
100
0
    with_dynamic_goal(graph, start, |node| goal.as_ref() == Some(node), edge_cost).scores
101
0
}
102
103
/// Return value of [`with_dynamic_goal`].
104
pub struct AlgoResult<N, K> {
105
    /// A [`struct@hashbrown::HashMap`] that maps `NodeId` to path cost.
106
    pub scores: HashMap<N, K>,
107
    /// The goal node that terminated the search, if any was found.
108
    pub goal_node: Option<N>,
109
}
110
111
/// Dijkstra's shortest path algorithm with a dynamic goal.
112
///
113
/// This algorithm is identical to [`dijkstra`],
114
/// but allows matching multiple goal nodes, whichever is reached first.
115
/// A node is considered a goal if `goal_fn` returns `true` for it.
116
///
117
/// See the [`dijkstra`] function for more details.
118
///
119
/// # Example
120
/// ```rust
121
/// use petgraph::Graph;
122
/// use petgraph::algo::dijkstra;
123
/// use petgraph::prelude::*;
124
/// use hashbrown::HashMap;
125
///
126
/// let mut graph: Graph<(), (), Directed> = Graph::new();
127
/// let a = graph.add_node(()); // node with no weight
128
/// let b = graph.add_node(());
129
/// let c = graph.add_node(());
130
/// let d = graph.add_node(());
131
/// let e = graph.add_node(());
132
/// let f = graph.add_node(());
133
/// let g = graph.add_node(());
134
/// let h = graph.add_node(());
135
/// // z will be in another connected component
136
/// let z = graph.add_node(());
137
///
138
/// graph.extend_with_edges(&[
139
///     (a, b),
140
///     (b, c),
141
///     (c, d),
142
///     (d, a),
143
///     (e, f),
144
///     (b, e),
145
///     (f, g),
146
///     (g, h),
147
///     (h, e),
148
/// ]);
149
/// // a ----> b ----> e ----> f
150
/// // ^       |       ^       |
151
/// // |       v       |       v
152
/// // d <---- c       h <---- g
153
///
154
/// let expected_res: HashMap<NodeIndex, usize> = [
155
///     (b, 0),
156
///     (c, 1),
157
///     (d, 2),
158
///     (e, 1),
159
///     (f, 2),
160
/// ].iter().cloned().collect();
161
/// let res = dijkstra::with_dynamic_goal(&graph, b, |&node| node == d || node == f, |_| 1);
162
/// assert_eq!(res.scores, expected_res);
163
/// assert!(res.goal_node == Some(d) || res.goal_node == Some(f));
164
/// ```
165
0
pub fn with_dynamic_goal<G, GoalFn, CostFn, K>(
166
0
    graph: G,
167
0
    start: G::NodeId,
168
0
    mut goal_fn: GoalFn,
169
0
    mut edge_cost: CostFn,
170
0
) -> AlgoResult<G::NodeId, K>
171
0
where
172
0
    G: IntoEdges + Visitable,
173
0
    G::NodeId: Eq + Hash,
174
0
    GoalFn: FnMut(&G::NodeId) -> bool,
175
0
    CostFn: FnMut(G::EdgeRef) -> K,
176
0
    K: Measure + Copy,
177
{
178
0
    let mut visited = graph.visit_map();
179
0
    let mut scores = HashMap::new();
180
    //let mut predecessor = HashMap::new();
181
0
    let mut visit_next = BinaryHeap::new();
182
0
    let zero_score = K::default();
183
0
    scores.insert(start, zero_score);
184
0
    visit_next.push(MinScored(zero_score, start));
185
0
    let mut goal_node = None;
186
0
    while let Some(MinScored(node_score, node)) = visit_next.pop() {
187
0
        if visited.is_visited(&node) {
188
0
            continue;
189
0
        }
190
0
        if goal_fn(&node) {
191
0
            goal_node = Some(node);
192
0
            break;
193
0
        }
194
0
        for edge in graph.edges(node) {
195
0
            let next = edge.target();
196
0
            if visited.is_visited(&next) {
197
0
                continue;
198
0
            }
199
0
            let next_score = node_score + edge_cost(edge);
200
0
            match scores.entry(next) {
201
0
                Occupied(ent) => {
202
0
                    if next_score < *ent.get() {
203
0
                        *ent.into_mut() = next_score;
204
0
                        visit_next.push(MinScored(next_score, next));
205
0
                        //predecessor.insert(next.clone(), node.clone());
206
0
                    }
207
                }
208
0
                Vacant(ent) => {
209
0
                    ent.insert(next_score);
210
0
                    visit_next.push(MinScored(next_score, next));
211
0
                    //predecessor.insert(next.clone(), node.clone());
212
0
                }
213
            }
214
        }
215
0
        visited.visit(node);
216
    }
217
0
    AlgoResult { scores, goal_node }
218
0
}
219
220
/// Bidirectional Dijkstra's shortest path algorithm.
221
///
222
/// Compute the length of the shortest path from `start` to `target`.
223
///
224
/// Bidirectional Dijkstra has the same time complexity as standard [`Dijkstra`][dijkstra]. However, because it
225
/// searches simultaneously from both the start and goal nodes, meeting in the middle, it often
226
/// explores roughly half the nodes that regular [`Dijkstra`][dijkstra] would explore. This is especially the case
227
/// when the path is long relative to the graph size or when working with sparse graphs.
228
///
229
/// However, regular [`Dijkstra`][dijkstra] may be preferable when you need the shortest paths from the start node
230
/// to multiple goals or when the start and goal are relatively close to each other.
231
///
232
/// The function `edge_cost` should return the cost for a particular edge, which is used
233
/// to compute path costs. Edge costs must be non-negative.
234
///
235
/// # Arguments
236
/// * `graph`: weighted graph.
237
/// * `start`: the start node.
238
/// * `goal`: the goal node.
239
/// * `edge_cost`: closure that returns the cost of a particular edge.
240
///
241
/// # Returns
242
/// * `Some(K)` - the total cost from start to finish, if one was found.
243
/// * `None` - if such a path was not found.
244
///
245
/// # Complexity
246
/// * Time complexity: **O((|V|+|E|)log(|V|))**.
247
/// * Auxiliary space: **O(|V|+|E|)**.
248
///
249
/// where **|V|** is the number of nodes and **|E|** is the number of edges.
250
///
251
/// # Example
252
/// ```rust
253
/// use petgraph::Graph;
254
/// use petgraph::algo::bidirectional_dijkstra;
255
/// use petgraph::prelude::*;
256
/// use hashbrown::HashMap;
257
///
258
/// let mut graph: Graph<(), (), Directed> = Graph::new();
259
/// let a = graph.add_node(());
260
/// let b = graph.add_node(());
261
/// let c = graph.add_node(());
262
/// let d = graph.add_node(());
263
/// let e = graph.add_node(());
264
/// let f = graph.add_node(());
265
/// let g = graph.add_node(());
266
/// let h = graph.add_node(());
267
///
268
/// graph.extend_with_edges(&[
269
///     (a, b),
270
///     (b, c),
271
///     (c, d),
272
///     (d, a),
273
///     (e, f),
274
///     (b, e),
275
///     (f, g),
276
///     (g, h),
277
///     (h, e),
278
/// ]);
279
/// // a ----> b ----> e ----> f
280
/// // ^       |       ^       |
281
/// // |       v       |       v
282
/// // d <---- c       h <---- g
283
///
284
/// let result = bidirectional_dijkstra(&graph, a, g, |_| 1);
285
/// assert_eq!(result, Some(4));
286
/// ```
287
0
pub fn bidirectional_dijkstra<G, F, K>(
288
0
    graph: G,
289
0
    start: G::NodeId,
290
0
    goal: G::NodeId,
291
0
    mut edge_cost: F,
292
0
) -> Option<K>
293
0
where
294
0
    G: Visitable + IntoEdgesDirected,
295
0
    G::NodeId: Eq + Hash,
296
0
    F: FnMut(G::EdgeRef) -> K,
297
0
    K: Measure + Copy,
298
{
299
0
    let mut forward_visited = graph.visit_map();
300
0
    let mut forward_distance = HashMap::new();
301
0
    forward_distance.insert(start, K::default());
302
303
0
    let mut backward_visited = graph.visit_map();
304
0
    let mut backward_distance = HashMap::new();
305
0
    backward_distance.insert(goal, K::default());
306
307
0
    let mut forward_heap = BinaryHeap::new();
308
0
    let mut backward_heap = BinaryHeap::new();
309
310
0
    forward_heap.push(MinScored(K::default(), start));
311
0
    backward_heap.push(MinScored(K::default(), goal));
312
313
0
    let mut best_value = None;
314
315
0
    while !forward_heap.is_empty() && !backward_heap.is_empty() {
316
0
        let MinScored(_, u) = forward_heap.pop().unwrap();
317
0
        let MinScored(_, v) = backward_heap.pop().unwrap();
318
319
0
        forward_visited.visit(u);
320
0
        backward_visited.visit(v);
321
322
0
        let distance_to_u = forward_distance[&u];
323
0
        let distance_to_v = backward_distance[&v];
324
325
0
        for edge in graph.edges_directed(u, Direction::Outgoing) {
326
0
            let x = edge.target();
327
0
            let current_edge_cost = edge_cost(edge);
328
329
0
            if !forward_visited.is_visited(&x) {
330
0
                let next_score = distance_to_u + current_edge_cost;
331
332
0
                match forward_distance.entry(x) {
333
0
                    Occupied(entry) => {
334
0
                        if next_score < *entry.get() {
335
0
                            *entry.into_mut() = next_score;
336
0
                            forward_heap.push(MinScored(next_score, x));
337
0
                        }
338
                    }
339
0
                    Vacant(entry) => {
340
0
                        entry.insert(next_score);
341
0
                        forward_heap.push(MinScored(next_score, x));
342
0
                    }
343
                }
344
0
            }
345
346
0
            if !backward_visited.is_visited(&x) {
347
0
                continue;
348
0
            }
349
350
0
            let potential_best_value = distance_to_u + current_edge_cost + backward_distance[&x];
351
352
0
            let improves_best_value = match best_value {
353
0
                None => true,
354
0
                Some(current_best_value) => potential_best_value < current_best_value,
355
            };
356
357
0
            if improves_best_value {
358
0
                best_value = Some(potential_best_value);
359
0
            }
360
        }
361
362
0
        for edge in graph.edges_directed(v, Direction::Incoming) {
363
0
            let x = edge.source();
364
0
            let edge_cost = edge_cost(edge);
365
366
0
            if !backward_visited.is_visited(&x) {
367
0
                let next_score = distance_to_v + edge_cost;
368
369
0
                match backward_distance.entry(x) {
370
0
                    Occupied(entry) => {
371
0
                        if next_score < *entry.get() {
372
0
                            *entry.into_mut() = next_score;
373
0
                            backward_heap.push(MinScored(next_score, x));
374
0
                        }
375
                    }
376
0
                    Vacant(entry) => {
377
0
                        entry.insert(next_score);
378
0
                        backward_heap.push(MinScored(next_score, x));
379
0
                    }
380
                }
381
0
            }
382
383
0
            if !forward_visited.is_visited(&x) {
384
0
                continue;
385
0
            }
386
387
0
            let potential_best_value = distance_to_v + edge_cost + forward_distance[&x];
388
389
0
            let improves_best_value = match best_value {
390
0
                None => true,
391
0
                Some(mu) => potential_best_value < mu,
392
            };
393
394
0
            if improves_best_value {
395
0
                best_value = Some(potential_best_value);
396
0
            }
397
        }
398
399
0
        if let Some(best_value) = best_value {
400
0
            if distance_to_u + distance_to_v >= best_value {
401
0
                return Some(best_value);
402
0
            }
403
0
        }
404
    }
405
406
0
    None
407
0
}