feat(algorithms): algorithm implementations
diff --git a/crates/algorithms/src/shortest_paths/astar/impl.rs b/crates/algorithms/src/shortest_paths/astar/impl.rs
index 17bf976..a8d1869 100644
--- a/crates/algorithms/src/shortest_paths/astar/impl.rs
+++ b/crates/algorithms/src/shortest_paths/astar/impl.rs
@@ -19,7 +19,6 @@
Path,
};
-// The graph must outlive the A* instance
pub(super) struct AStarImpl<'graph: 'parent, 'parent, S, E, H, C>
where
S: GraphStorage,
@@ -27,6 +26,7 @@
E: GraphCost<S>,
E::Value: Ord,
{
+ graph: &'graph Graph<S>,
queue: PriorityQueue<'graph, S, E::Value>,
edge_cost: &'parent E,
@@ -39,8 +39,8 @@
predecessor_mode: PredecessorMode,
distances: <S::NodeId as AssociativeGraphId<S>>::AttributeMapper<'graph, E::Value>,
- predecessors:
- <S::NodeId as AssociativeGraphId<S>>::AttributeMapper<'graph, Option<Node<'graph, S>>>,
+ estimates: <S::NodeId as AssociativeGraphId<S>>::AttributeMapper<'graph, E::Value>,
+ predecessors: <S::NodeId as AssociativeGraphId<S>>::AttributeMapper<'graph, Option<S::NodeId>>,
}
impl<'graph: 'parent, 'parent, S, E, H, C> AStarImpl<'graph, 'parent, S, E, H, C>
@@ -72,15 +72,18 @@
.node(target)
.ok_or_else(|| Report::new(AStarError::NodeNotFound))?;
+ let estimate = heuristic.estimate(source_node, target_node);
+
let mut queue = PriorityQueue::new(graph.storage());
- queue.push(
- source_node,
- heuristic.estimate(source_node, target_node).into_owned(),
- );
+ queue.check_admissibility = false;
+
+ queue.push(source_node.id(), estimate.clone().into_owned());
let mut distances = <S::NodeId as AssociativeGraphId<S>>::attribute_mapper(graph.storage());
distances.set(source, E::Value::zero());
+ let estimates = <S::NodeId as AssociativeGraphId<S>>::attribute_mapper(graph.storage());
+
let mut predecessors =
<S::NodeId as AssociativeGraphId<S>>::attribute_mapper(graph.storage());
if predecessor_mode == PredecessorMode::Record {
@@ -88,6 +91,7 @@
}
Ok(Self {
+ graph,
queue,
edge_cost,
@@ -100,33 +104,52 @@
predecessor_mode,
distances,
+ estimates,
predecessors,
})
}
pub(super) fn find(mut self) -> Option<Route<'graph, S, E::Value>> {
- while let Some(PriorityQueueItem { node, .. }) = self.queue.pop_min() {
- if node.id() == self.target.id() {
+ while let Some(PriorityQueueItem {
+ node,
+ priority: current_estimate,
+ }) = self.queue.pop_min()
+ {
+ if node == self.target.id() {
let transit = if self.predecessor_mode == PredecessorMode::Record {
- reconstruct_path_to(&self.predecessors, node.id())
+ reconstruct_path_to::<S>(&self.predecessors, node)
+ .into_iter()
+ .filter_map(|id| self.graph.node(id))
+ .collect()
} else {
Vec::new()
};
- let distance = self.distances.get(node.id())?.clone();
+ let distance = self.distances.get(node)?.clone();
return Some(Route::new(
Path::new(self.source, transit, self.target),
Cost::new(distance),
));
}
- let connections = self.connections.connections(&node);
+ if let Some(estimate) = self.estimates.get_mut(node) {
+ // if the current estimate is better than the estimate in the queue, skip this node
+ if *estimate <= current_estimate {
+ continue;
+ }
+
+ *estimate = current_estimate;
+ } else {
+ self.estimates.set(node, current_estimate);
+ }
+
+ let connections = self.connections.connections(node);
for edge in connections {
- let source_distance = self.distances.get(node.id())?;
+ let source_distance = self.distances.get(node)?;
let alternative = source_distance.add_ref(self.edge_cost.cost(edge).as_ref());
let (u, v) = edge.endpoints();
- let neighbour = if u.id() == node.id() { v } else { u };
+ let neighbour = if u.id() == node { v } else { u };
if let Some(distance) = self.distances.get(neighbour.id()) {
if alternative >= *distance {
@@ -142,7 +165,7 @@
self.predecessors.set(neighbour.id(), Some(node));
}
- self.queue.decrease_priority(neighbour, guess);
+ self.queue.decrease_priority(neighbour.id(), guess);
}
}
diff --git a/crates/algorithms/src/shortest_paths/astar/mod.rs b/crates/algorithms/src/shortest_paths/astar/mod.rs
index e21aa79..4109c44 100644
--- a/crates/algorithms/src/shortest_paths/astar/mod.rs
+++ b/crates/algorithms/src/shortest_paths/astar/mod.rs
@@ -20,14 +20,14 @@
pub use self::{error::AStarError, heuristic::GraphHeuristic, measure::AStarMeasure};
use super::{
common::{
- connections::{outgoing_connections, Connections},
+ connections::Connections,
cost::{Cost, DefaultCost, GraphCost},
route::{DirectRoute, Route},
transit::PredecessorMode,
},
ShortestDistance, ShortestPath,
};
-use crate::polyfill::IteratorExt;
+use crate::{polyfill::IteratorExt, shortest_paths::common::connections::NodeConnections};
/// A* shortest path algorithm.
///
@@ -159,7 +159,7 @@
graph,
&self.edge_cost,
&self.heuristic,
- outgoing_connections as fn(&Node<'graph, S>) -> _,
+ NodeConnections::directed(graph.storage()),
source,
target,
intermediates,
@@ -186,7 +186,7 @@
graph,
&self.edge_cost,
&self.heuristic,
- Node::<'graph, S>::connections as fn(&Node<'graph, S>) -> _,
+ NodeConnections::undirected(graph.storage()),
source,
target,
intermediates,
diff --git a/crates/algorithms/src/shortest_paths/astar/tests.rs b/crates/algorithms/src/shortest_paths/astar/tests.rs
index f13146b..8ff18c1 100644
--- a/crates/algorithms/src/shortest_paths/astar/tests.rs
+++ b/crates/algorithms/src/shortest_paths/astar/tests.rs
@@ -88,7 +88,7 @@
let GraphCollection { graph, nodes, .. } = networkx::create();
let astar = AStar::directed().with_heuristic(no_heuristic);
- let route = astar.path_between(&graph, &nodes.s, &nodes.v).unwrap();
+ let route = astar.path_between(&graph, nodes.s, nodes.v).unwrap();
let (path, cost) = route.into_parts();
let nodes: Vec<_> = path
@@ -106,7 +106,7 @@
let GraphCollection { graph, nodes, .. } = networkx::create();
let astar = AStar::directed().with_heuristic(no_heuristic);
- let route = astar.path_between(&graph, &nodes.s, &nodes.z);
+ let route = astar.path_between(&graph, nodes.s, nodes.z);
assert!(route.is_none());
}
@@ -116,7 +116,7 @@
let GraphCollection { graph, nodes, .. } = networkx::create();
let astar = AStar::undirected().with_heuristic(no_heuristic);
- let route = astar.path_between(&graph, &nodes.s, &nodes.v).unwrap();
+ let route = astar.path_between(&graph, nodes.s, nodes.v).unwrap();
let (path, cost) = route.into_parts();
let nodes: Vec<_> = path
@@ -134,7 +134,7 @@
let GraphCollection { graph, nodes, .. } = networkx::create();
let astar = AStar::undirected().with_heuristic(no_heuristic);
- let route = astar.path_between(&graph, &nodes.s, &nodes.z);
+ let route = astar.path_between(&graph, nodes.s, nodes.z);
assert!(route.is_none());
}
@@ -144,7 +144,7 @@
let GraphCollection { graph, nodes, .. } = networkx::create();
let astar = AStar::directed().with_heuristic(no_heuristic);
- let cost = astar.distance_between(&graph, &nodes.s, &nodes.v).unwrap();
+ let cost = astar.distance_between(&graph, nodes.s, nodes.v).unwrap();
assert_eq!(cost.into_value(), 9);
}
@@ -154,7 +154,7 @@
let GraphCollection { graph, nodes, .. } = networkx::create();
let astar = AStar::directed().with_heuristic(no_heuristic);
- let cost = astar.distance_between(&graph, &nodes.s, &nodes.z);
+ let cost = astar.distance_between(&graph, nodes.s, nodes.z);
assert!(cost.is_none());
}
@@ -164,7 +164,7 @@
let GraphCollection { graph, nodes, .. } = networkx::create();
let astar = AStar::undirected().with_heuristic(no_heuristic);
- let cost = astar.distance_between(&graph, &nodes.s, &nodes.v).unwrap();
+ let cost = astar.distance_between(&graph, nodes.s, nodes.v).unwrap();
assert_eq!(cost.into_value(), 8);
}
@@ -174,7 +174,7 @@
let GraphCollection { graph, nodes, .. } = networkx::create();
let astar = AStar::undirected().with_heuristic(no_heuristic);
- let cost = astar.distance_between(&graph, &nodes.s, &nodes.z);
+ let cost = astar.distance_between(&graph, nodes.s, nodes.z);
assert!(cost.is_none());
}
@@ -212,17 +212,17 @@
.with_edge_cost(ensure_not_nan)
.with_heuristic(manhattan_distance);
- let route = astar.path_between(&graph, &nodes.a, &nodes.f).unwrap();
+ let route = astar.path_between(&graph, nodes.a, nodes.f).unwrap();
let (path, cost) = route.into_parts();
- let path: Vec<_> = path.to_vec().into_iter().map(|node| *node.id()).collect();
+ let path: Vec<_> = path.to_vec().into_iter().map(|node| node.id()).collect();
assert_eq!(path, [nodes.a, nodes.b, nodes.f]);
- let a = graph.node(&nodes.a).unwrap();
- let b = graph.node(&nodes.b).unwrap();
- let f = graph.node(&nodes.f).unwrap();
+ let a = graph.node(nodes.a).unwrap();
+ let b = graph.node(nodes.b).unwrap();
+ let f = graph.node(nodes.f).unwrap();
assert_eq!(
cost.into_value(),
@@ -237,11 +237,11 @@
let astar = AStar::directed()
.with_edge_cost(ensure_not_nan)
.with_heuristic(manhattan_distance);
- let cost = astar.distance_between(&graph, &nodes.a, &nodes.f).unwrap();
+ let cost = astar.distance_between(&graph, nodes.a, nodes.f).unwrap();
- let a = graph.node(&nodes.a).unwrap();
- let b = graph.node(&nodes.b).unwrap();
- let f = graph.node(&nodes.f).unwrap();
+ let a = graph.node(nodes.a).unwrap();
+ let b = graph.node(nodes.b).unwrap();
+ let f = graph.node(nodes.f).unwrap();
assert_eq!(
cost.into_value(),
@@ -294,11 +294,11 @@
let GraphCollection { graph, nodes, .. } = inconsistent::create();
let astar = AStar::directed().with_heuristic(admissible_inconsistent);
- let route = astar.path_between(&graph, &nodes.a, &nodes.d).unwrap();
+ let route = astar.path_between(&graph, nodes.a, nodes.d).unwrap();
let (path, cost) = route.into_parts();
- let path: Vec<_> = path.to_vec().into_iter().map(|node| *node.id()).collect();
+ let path: Vec<_> = path.to_vec().into_iter().map(|node| node.id()).collect();
assert_eq!(path, [nodes.a, nodes.b, nodes.c, nodes.d]);
assert_eq!(cost.into_value(), 9);
@@ -339,7 +339,7 @@
}))
.with_heuristic(no_heuristic);
- astar.path_between(&graph, &nodes.a, &nodes.e).unwrap();
+ astar.path_between(&graph, nodes.a, nodes.e).unwrap();
// A* is runtime optimal in the sense it won't expand more nodes than needed, for the given
// heuristic. Here, A* should expand, in order: A, B, C, D, E. This should should ask for
diff --git a/crates/algorithms/src/shortest_paths/bellman_ford/impl.rs b/crates/algorithms/src/shortest_paths/bellman_ford/impl.rs
index 128cbba..f0b9963 100644
--- a/crates/algorithms/src/shortest_paths/bellman_ford/impl.rs
+++ b/crates/algorithms/src/shortest_paths/bellman_ford/impl.rs
@@ -248,7 +248,7 @@
}
}
- let edges = self.connections.connections(&source);
+ let edges = self.connections.connections(source.id());
for edge in edges {
let (u, v) = edge.endpoints();
diff --git a/crates/algorithms/src/shortest_paths/bellman_ford/mod.rs b/crates/algorithms/src/shortest_paths/bellman_ford/mod.rs
index a1d4c1d..24875b7 100644
--- a/crates/algorithms/src/shortest_paths/bellman_ford/mod.rs
+++ b/crates/algorithms/src/shortest_paths/bellman_ford/mod.rs
@@ -11,20 +11,19 @@
use error_stack::Result;
use petgraph_core::{
edge::marker::{Directed, Undirected},
- DirectedGraphStorage, Graph, GraphDirectionality, GraphStorage, Node,
+ DirectedGraphStorage, Graph, GraphDirectionality, GraphStorage,
};
use self::r#impl::ShortestPathFasterImpl;
pub use self::{error::BellmanFordError, measure::BellmanFordMeasure};
use super::{
common::{
- connections::outgoing_connections,
cost::{DefaultCost, GraphCost},
transit::PredecessorMode,
},
Cost, DirectRoute, Route, ShortestDistance, ShortestPath,
};
-use crate::polyfill::IteratorExt;
+use crate::{polyfill::IteratorExt, shortest_paths::common::connections::NodeConnections};
/// The order in which candidates are inserted into the queue.
///
@@ -230,6 +229,7 @@
/// let path = algorithm.path_between(&graph, &a, &b);
/// assert!(path.is_some());
/// ```
+ #[must_use]
pub fn with_candidate_order(self, candidate_order: CandidateOrder) -> Self {
Self {
direction: self.direction,
@@ -269,6 +269,7 @@
/// let path = algorithm.path_between(&graph, &a, &b);
/// assert!(path.is_some());
/// ```
+ #[must_use]
pub fn with_negative_cycle_heuristics(self, negative_cycle_heuristics: bool) -> Self {
Self {
direction: self.direction,
@@ -307,7 +308,7 @@
ShortestPathFasterImpl::new(
graph,
&self.edge_cost,
- Node::<'graph, S>::connections as fn(&Node<'graph, S>) -> _,
+ NodeConnections::undirected(graph.storage()),
source,
PredecessorMode::Record,
self.candidate_order,
@@ -325,7 +326,7 @@
ShortestPathFasterImpl::new(
graph,
&self.edge_cost,
- Node::<'graph, S>::connections as fn(&Node<'graph, S>) -> _,
+ NodeConnections::undirected(graph.storage()),
source,
PredecessorMode::Record,
self.candidate_order,
@@ -376,7 +377,7 @@
let iter = ShortestPathFasterImpl::new(
graph,
&self.edge_cost,
- Node::<'graph, S>::connections as fn(&Node<'graph, S>) -> _,
+ NodeConnections::undirected(graph.storage()),
source,
PredecessorMode::Discard,
self.candidate_order,
@@ -386,16 +387,16 @@
Ok(iter.all().map(Into::into))
}
- fn distance_between<'graph>(
+ fn distance_between(
&self,
- graph: &'graph Graph<S>,
+ graph: &Graph<S>,
source: S::NodeId,
target: S::NodeId,
) -> Option<Cost<Self::Cost>> {
ShortestPathFasterImpl::new(
graph,
&self.edge_cost,
- Node::<'graph, S>::connections as fn(&Node<'graph, S>) -> _,
+ NodeConnections::undirected(graph.storage()),
source,
PredecessorMode::Discard,
self.candidate_order,
@@ -437,7 +438,7 @@
ShortestPathFasterImpl::new(
graph,
&self.edge_cost,
- outgoing_connections as fn(&Node<'graph, S>) -> _,
+ NodeConnections::directed(graph.storage()),
source,
PredecessorMode::Record,
self.candidate_order,
@@ -455,7 +456,7 @@
ShortestPathFasterImpl::new(
graph,
&self.edge_cost,
- outgoing_connections as fn(&Node<'graph, S>) -> _,
+ NodeConnections::directed(graph.storage()),
source,
PredecessorMode::Record,
self.candidate_order,
@@ -496,7 +497,7 @@
let iter = ShortestPathFasterImpl::new(
graph,
&self.edge_cost,
- outgoing_connections as fn(&Node<'graph, S>) -> _,
+ NodeConnections::directed(graph.storage()),
source,
PredecessorMode::Discard,
self.candidate_order,
@@ -506,16 +507,16 @@
Ok(iter.all().map(Into::into))
}
- fn distance_between<'graph>(
+ fn distance_between(
&self,
- graph: &'graph Graph<S>,
+ graph: &Graph<S>,
source: S::NodeId,
target: S::NodeId,
) -> Option<Cost<Self::Cost>> {
ShortestPathFasterImpl::new(
graph,
&self.edge_cost,
- outgoing_connections as fn(&Node<'graph, S>) -> _,
+ NodeConnections::directed(graph.storage()),
source,
PredecessorMode::Discard,
self.candidate_order,
diff --git a/crates/algorithms/src/shortest_paths/bellman_ford/tests.rs b/crates/algorithms/src/shortest_paths/bellman_ford/tests.rs
index fa55e4b..3295888 100644
--- a/crates/algorithms/src/shortest_paths/bellman_ford/tests.rs
+++ b/crates/algorithms/src/shortest_paths/bellman_ford/tests.rs
@@ -44,11 +44,11 @@
fn source_does_not_exist() {
let GraphCollection { mut graph, .. } = networkx::create();
- let f = *graph.insert_node("F").id();
- graph.remove_node(&f);
+ let f = graph.insert_node("F").id();
+ graph.remove_node(f);
let spfa = BellmanFord::directed();
- let Err(received) = spfa.path_from(&graph, &f) else {
+ let Err(received) = spfa.path_from(&graph, f) else {
panic!("Expected an error");
};
let error = received.current_context();
@@ -60,23 +60,23 @@
fn negative_cycle_heuristic() {
let mut graph = DiDinoGraph::new();
- let a = *graph.insert_node("A").id();
- let b = *graph.insert_node("B").id();
- let c = *graph.insert_node("C").id();
- let d = *graph.insert_node("D").id();
+ let a = graph.insert_node("A").id();
+ let b = graph.insert_node("B").id();
+ let c = graph.insert_node("C").id();
+ let d = graph.insert_node("D").id();
- graph.insert_edge(-1f32, &a, &b);
- graph.insert_edge(-1f32, &b, &c);
- graph.insert_edge(-1f32, &c, &d);
- graph.insert_edge(3f32, &d, &a);
+ graph.insert_edge(-1f32, a, b);
+ graph.insert_edge(-1f32, b, c);
+ graph.insert_edge(-1f32, c, d);
+ graph.insert_edge(3f32, d, a);
let spfa = BellmanFord::directed();
assert!(spfa.every_path(&graph).is_ok());
- let ca = *graph.insert_edge(1.999, &c, &a).id();
+ let ca = graph.insert_edge(1.999, c, a).id();
assert!(spfa.every_path(&graph).is_err());
- *graph.edge_mut(&ca).unwrap().weight_mut() = 2.0;
+ *graph.edge_mut(ca).unwrap().weight_mut() = 2.0;
assert!(spfa.every_path(&graph).is_ok());
}
@@ -88,11 +88,11 @@
{
let mut graph = Graph::new();
- let nodes: [_; N] = array::from_fn(|index| *graph.insert_node(index).id());
+ let nodes: [_; N] = array::from_fn(|index| graph.insert_node(index).id());
let edges: [_; N] = array::from_fn(|index| {
- *graph
- .insert_edge(1f32, &nodes[index], &nodes[(index + 1) % N])
+ graph
+ .insert_edge(1f32, nodes[index], nodes[(index + 1) % N])
.id()
});
@@ -107,13 +107,13 @@
{
let mut graph = Graph::new();
- let nodes: [_; N] = array::from_fn(|index| *graph.insert_node(index).id());
+ let nodes: [_; N] = array::from_fn(|index| graph.insert_node(index).id());
let mut edges = Vec::with_capacity(N * (N - 1) / 2);
for i in 0..N {
for j in i + 1..N {
- edges.push(*graph.insert_edge(1f32, &nodes[i], &nodes[j]).id());
+ edges.push(graph.insert_edge(1f32, nodes[i], nodes[j]).id());
}
}
@@ -128,12 +128,12 @@
{
let mut graph = Graph::new();
- let nodes: [_; N] = array::from_fn(|index| *graph.insert_node(index).id());
+ let nodes: [_; N] = array::from_fn(|index| graph.insert_node(index).id());
let mut edges = Vec::with_capacity(N - 1);
for i in 0..N - 1 {
- edges.push(*graph.insert_edge(1f32, &nodes[i], &nodes[i + 1]).id());
+ edges.push(graph.insert_edge(1f32, nodes[i], nodes[i + 1]).id());
}
(graph, nodes, edges)
@@ -147,7 +147,7 @@
assert!(spfa.every_path(&graph).is_ok());
// add negative cycle between nodes 1 and 2
- *graph.insert_edge(-7f32, &nodes[1], &nodes[2]).id();
+ graph.insert_edge(-7f32, nodes[1], nodes[2]);
assert!(spfa.every_path(&graph).is_err());
}
@@ -159,7 +159,7 @@
assert!(spfa.every_path(&graph).is_ok());
// add negative cycle between nodes 1 and 2
- *graph.insert_edge(-3f32, &nodes[1], &nodes[2]).id();
+ graph.insert_edge(-3f32, nodes[1], nodes[2]);
assert!(spfa.every_path(&graph).is_err());
}
@@ -167,9 +167,9 @@
fn negative_self_cycle() {
let mut graph = DiDinoGraph::new();
- let a = *graph.insert_node("A").id();
+ let a = graph.insert_node("A").id();
- graph.insert_edge(-1f32, &a, &a);
+ graph.insert_edge(-1f32, a, a);
let spfa = BellmanFord::directed();
assert!(spfa.every_path(&graph).is_err());
@@ -183,11 +183,11 @@
assert!(spfa.every_path(&graph).is_ok());
// add zero cycle between nodes 2 and 3
- let edge = *graph.insert_edge(-4f32, &nodes[2], &nodes[3]).id();
+ let edge = graph.insert_edge(-4f32, nodes[2], nodes[3]).id();
assert!(spfa.every_path(&graph).is_ok());
// increase that cycle to a negative cycle
- *graph.edge_mut(&edge).unwrap().weight_mut() = -4.0001f32;
+ *graph.edge_mut(edge).unwrap().weight_mut() = -4.0001f32;
assert!(spfa.every_path(&graph).is_err());
}
@@ -199,7 +199,7 @@
assert!(spfa.every_path(&graph).is_ok());
// add negative weight to edge between nodes 1 and 2
- graph.insert_edge(-3f32, &nodes[1], &nodes[2]);
+ graph.insert_edge(-3f32, nodes[1], nodes[2]);
let expected = expected!(nodes; [
0 -()> 0: 0f32,
@@ -209,19 +209,19 @@
0 -(1, 2, 3)> 4: 0f32,
]);
- TestCase::new(&graph, &spfa, &expected).assert_path_from(&nodes[0]);
+ TestCase::new(&graph, &spfa, &expected).assert_path_from(nodes[0]);
}
#[test]
fn not_connected() {
let (mut graph, nodes, _) = complete_graph::<6, DinoStorage<_, _>>();
- let g = *graph.insert_node(10).id();
- let h = *graph.insert_node(11).id();
- let i = *graph.insert_node(12).id();
+ let g = graph.insert_node(10).id();
+ let h = graph.insert_node(11).id();
+ let i = graph.insert_node(12).id();
- graph.insert_edge(1f32, &g, &h);
- graph.insert_edge(1f32, &g, &i);
+ graph.insert_edge(1f32, g, h);
+ graph.insert_edge(1f32, g, i);
let spfa = BellmanFord::directed();
@@ -234,20 +234,20 @@
0 -()> 5: 1f32,
]);
- TestCase::new(&graph, &spfa, &expected).assert_path_from(&nodes[0]);
+ TestCase::new(&graph, &spfa, &expected).assert_path_from(nodes[0]);
}
#[test]
fn negative_cycle_not_connected() {
let (mut graph, nodes, _) = complete_graph::<6, DinoStorage<_, _>>();
- let g = *graph.insert_node(10).id();
- let h = *graph.insert_node(11).id();
- let i = *graph.insert_node(12).id();
+ let g = graph.insert_node(10).id();
+ let h = graph.insert_node(11).id();
+ let i = graph.insert_node(12).id();
- graph.insert_edge(1f32, &g, &h);
- graph.insert_edge(1f32, &h, &i);
- graph.insert_edge(-3f32, &i, &g);
+ graph.insert_edge(1f32, g, h);
+ graph.insert_edge(1f32, h, i);
+ graph.insert_edge(-3f32, i, g);
let spfa = BellmanFord::directed();
@@ -260,7 +260,7 @@
0 -()> 5: 1f32,
]);
- TestCase::new(&graph, &spfa, &expected).assert_path_from(&nodes[0]);
+ TestCase::new(&graph, &spfa, &expected).assert_path_from(nodes[0]);
}
#[test]
@@ -277,5 +277,5 @@
0 -(1, 2, 3)> 4: 4f32,
]);
- TestCase::new(&graph, &spfa, &expected).assert_path_from(&nodes[0]);
+ TestCase::new(&graph, &spfa, &expected).assert_path_from(nodes[0]);
}
diff --git a/crates/algorithms/src/shortest_paths/common/closures.rs b/crates/algorithms/src/shortest_paths/common/closures.rs
index 8d701aa..90fd2ed 100644
--- a/crates/algorithms/src/shortest_paths/common/closures.rs
+++ b/crates/algorithms/src/shortest_paths/common/closures.rs
@@ -35,12 +35,12 @@
let algorithm = Dijkstra::undirected().with_edge_cost(closure);
let mut graph = DiDinoGraph::new();
- let a = *graph.insert_node("A").id();
- let b = *graph.insert_node("B").id();
+ let a = graph.insert_node("A").id();
+ let b = graph.insert_node("B").id();
- graph.insert_edge(7, &a, &b);
+ graph.insert_edge(7, a, b);
- let path = algorithm.path_between(&graph, &a, &b).expect("path exists");
+ let path = algorithm.path_between(&graph, a, b).expect("path exists");
}
#[test]
@@ -50,12 +50,12 @@
let algorithm = AStar::undirected().with_heuristic(closure);
let mut graph = DiDinoGraph::new();
- let a = *graph.insert_node("A").id();
- let b = *graph.insert_node("B").id();
+ let a = graph.insert_node("A").id();
+ let b = graph.insert_node("B").id();
- graph.insert_edge(7i32, &a, &b);
+ graph.insert_edge(7i32, a, b);
- let path = algorithm.path_between(&graph, &a, &b).expect("path exists");
+ let path = algorithm.path_between(&graph, a, b).expect("path exists");
}
}
diff --git a/crates/algorithms/src/shortest_paths/common/queue/priority.rs b/crates/algorithms/src/shortest_paths/common/queue/priority.rs
index 57e0272..24de078 100644
--- a/crates/algorithms/src/shortest_paths/common/queue/priority.rs
+++ b/crates/algorithms/src/shortest_paths/common/queue/priority.rs
@@ -3,7 +3,7 @@
use petgraph_core::{
id::{AssociativeGraphId, BooleanMapper},
- GraphStorage, Node,
+ GraphStorage,
};
pub(in crate::shortest_paths) struct PriorityQueueItem<I, T> {
@@ -48,6 +48,7 @@
T: Ord,
{
heap: BinaryHeap<PriorityQueueItem<S::NodeId, T>>,
+ pub(crate) check_admissibility: bool,
flags: <S::NodeId as AssociativeGraphId<S>>::BooleanMapper<'a>,
}
@@ -62,6 +63,7 @@
pub(in crate::shortest_paths) fn new(storage: &'a S) -> Self {
Self {
heap: BinaryHeap::new(),
+ check_admissibility: true,
flags: <S::NodeId as AssociativeGraphId<S>>::boolean_mapper(storage),
}
}
@@ -81,7 +83,7 @@
#[inline]
pub(in crate::shortest_paths) fn decrease_priority(&mut self, node: S::NodeId, priority: T) {
- if self.has_been_visited(node) {
+ if self.check_admissibility && self.has_been_visited(node) {
return;
}
@@ -93,7 +95,7 @@
loop {
let item = self.heap.pop()?;
- if self.has_been_visited(item.node) {
+ if self.check_admissibility && self.has_been_visited(item.node) {
continue;
}
diff --git a/crates/algorithms/src/shortest_paths/common/tests.rs b/crates/algorithms/src/shortest_paths/common/tests.rs
index 0839623..629ff62 100644
--- a/crates/algorithms/src/shortest_paths/common/tests.rs
+++ b/crates/algorithms/src/shortest_paths/common/tests.rs
@@ -95,8 +95,8 @@
.collect();
for expect in self.expected {
- let source = &expect.source;
- let target = &expect.target;
+ let source = expect.source;
+ let target = expect.target;
let route = routes.remove(&(source, target)).expect("route not found");
@@ -106,7 +106,7 @@
assert_eq!(path.target().id(), target, "target of {source} -> {target}");
assert_eq!(
path.transit().iter().map(Node::id).collect::<Vec<_>>(),
- expect.transit.iter().collect::<Vec<_>>(),
+ expect.transit.iter().copied().collect::<Vec<_>>(),
"transit of {source} -> {target}"
);
assert_eq!(*cost.value(), expect.cost, "cost of {source} -> {target}");
@@ -120,7 +120,7 @@
}
#[track_caller]
- pub(in crate::shortest_paths) fn assert_path_from(&self, source: &S::NodeId) {
+ pub(in crate::shortest_paths) fn assert_path_from(&self, source: S::NodeId) {
self.assert_path_routes(self.algorithm.path_from(self.graph, source));
}
}
@@ -143,8 +143,8 @@
.collect();
for expect in self.expected {
- let source = &expect.source;
- let target = &expect.target;
+ let source = expect.source;
+ let target = expect.target;
let route = routes.remove(&(source, target)).expect("route not found");
@@ -174,7 +174,7 @@
}
#[track_caller]
- pub(in crate::shortest_paths) fn assert_distance_from(&self, source: &S::NodeId) {
+ pub(in crate::shortest_paths) fn assert_distance_from(&self, source: S::NodeId) {
self.assert_distance_routes(self.algorithm.distance_from(self.graph, source));
}
}
diff --git a/crates/algorithms/src/shortest_paths/common/transit.rs b/crates/algorithms/src/shortest_paths/common/transit.rs
index 28d2451..cd7f47e 100644
--- a/crates/algorithms/src/shortest_paths/common/transit.rs
+++ b/crates/algorithms/src/shortest_paths/common/transit.rs
@@ -110,13 +110,12 @@
} else {
seen.remove(&node.id());
- match top.checked_sub(1) {
- Some(new_top) => top = new_top,
- None => {
- // we have exhausted all paths
- exhausted = true;
- break;
- }
+ if let Some(new_top) = top.checked_sub(1) {
+ top = new_top;
+ } else {
+ // we have exhausted all paths
+ exhausted = true;
+ break;
}
}
diff --git a/crates/algorithms/src/shortest_paths/dijkstra/iter.rs b/crates/algorithms/src/shortest_paths/dijkstra/iter.rs
index 2cf3776..f2972f3 100644
--- a/crates/algorithms/src/shortest_paths/dijkstra/iter.rs
+++ b/crates/algorithms/src/shortest_paths/dijkstra/iter.rs
@@ -193,6 +193,7 @@
let transit = if self.predecessor_mode == PredecessorMode::Discard {
Vec::new()
} else {
+ // TODO: cache via dedicated structure
reconstruct_path_to::<S>(&self.predecessors, node)
.into_iter()
.filter_map(|id| self.graph.node(id))
diff --git a/crates/algorithms/src/shortest_paths/dijkstra/tests.rs b/crates/algorithms/src/shortest_paths/dijkstra/tests.rs
index 0386ab0..c6ee7fa 100644
--- a/crates/algorithms/src/shortest_paths/dijkstra/tests.rs
+++ b/crates/algorithms/src/shortest_paths/dijkstra/tests.rs
@@ -78,7 +78,7 @@
let dijkstra = Dijkstra::directed();
- TestCase::new(&graph, &dijkstra, &expected).assert_path_from(&nodes.a);
+ TestCase::new(&graph, &dijkstra, &expected).assert_path_from(nodes.a);
}
#[test]
@@ -88,7 +88,7 @@
let dijkstra = Dijkstra::directed();
- TestCase::new(&graph, &dijkstra, &expected).assert_distance_from(&nodes.a);
+ TestCase::new(&graph, &dijkstra, &expected).assert_distance_from(nodes.a);
}
fn random_directed_expect_from(
@@ -119,7 +119,7 @@
let dijkstra = Dijkstra::directed().with_edge_cost(edge_cost);
let expected = random_directed_expect_from(&nodes);
- TestCase::new(&graph, &dijkstra, &expected).assert_path_from(&nodes.a);
+ TestCase::new(&graph, &dijkstra, &expected).assert_path_from(nodes.a);
}
#[test]
@@ -129,7 +129,7 @@
let dijkstra = Dijkstra::directed().with_edge_cost(edge_cost);
let expected = random_directed_expect_from(&nodes);
- TestCase::new(&graph, &dijkstra, &expected).assert_distance_from(&nodes.a);
+ TestCase::new(&graph, &dijkstra, &expected).assert_distance_from(nodes.a);
}
fn networkx_undirected_expect_from(
@@ -151,7 +151,7 @@
let dijkstra = Dijkstra::undirected();
let expected = networkx_undirected_expect_from(&nodes);
- TestCase::new(&graph, &dijkstra, &expected).assert_path_from(&nodes.a);
+ TestCase::new(&graph, &dijkstra, &expected).assert_path_from(nodes.a);
}
#[test]
@@ -161,7 +161,7 @@
let dijkstra = Dijkstra::undirected();
let expected = networkx_undirected_expect_from(&nodes);
- TestCase::new(&graph, &dijkstra, &expected).assert_distance_from(&nodes.a);
+ TestCase::new(&graph, &dijkstra, &expected).assert_distance_from(nodes.a);
}
fn random_undirected_expect_from(
@@ -184,7 +184,7 @@
let dijkstra = Dijkstra::undirected().with_edge_cost(edge_cost);
let expected = random_undirected_expect_from(&nodes);
- TestCase::new(&graph, &dijkstra, &expected).assert_path_from(&nodes.a);
+ TestCase::new(&graph, &dijkstra, &expected).assert_path_from(nodes.a);
}
#[test]
@@ -194,7 +194,7 @@
let dijkstra = Dijkstra::undirected().with_edge_cost(edge_cost);
let expected = random_undirected_expect_from(&nodes);
- TestCase::new(&graph, &dijkstra, &expected).assert_distance_from(&nodes.a);
+ TestCase::new(&graph, &dijkstra, &expected).assert_distance_from(nodes.a);
}
#[test]
@@ -204,7 +204,7 @@
let dijkstra = Dijkstra::directed();
let top3: Vec<_> = dijkstra
- .path_from(&graph, &nodes.a)
+ .path_from(&graph, nodes.a)
.unwrap()
.take(3)
.collect();
diff --git a/crates/algorithms/src/shortest_paths/floyd_warshall/tests.rs b/crates/algorithms/src/shortest_paths/floyd_warshall/tests.rs
index c1d970b..1bd28b0 100644
--- a/crates/algorithms/src/shortest_paths/floyd_warshall/tests.rs
+++ b/crates/algorithms/src/shortest_paths/floyd_warshall/tests.rs
@@ -184,7 +184,7 @@
let floyd_warshall = FloydWarshall::directed();
let route = floyd_warshall
- .path_between(&graph, &nodes.a, &nodes.c)
+ .path_between(&graph, nodes.a, nodes.c)
.unwrap();
let (path, cost) = route.into_parts();
@@ -192,7 +192,7 @@
assert_eq!(
path.to_vec()
.into_iter()
- .map(|node| *node.id())
+ .map(|node| node.id())
.collect::<Vec<_>>(),
vec![nodes.a, nodes.b, nodes.c]
);
@@ -204,14 +204,14 @@
let floyd_warshall = FloydWarshall::directed();
- let paths = floyd_warshall.path_from(&graph, &nodes.a).unwrap();
+ let paths = floyd_warshall.path_from(&graph, nodes.a).unwrap();
let mut expected: HashSet<_> = [nodes.a, nodes.b, nodes.c, nodes.d].into_iter().collect();
// cost is tested above, this just checks that the filter is correct.
for route in paths {
- assert_eq!(*route.path().source().id(), nodes.a);
- assert!(expected.remove(route.path().target().id()));
+ assert_eq!(route.path().source().id(), nodes.a);
+ assert!(expected.remove(&route.path().target().id()));
}
}
@@ -221,14 +221,14 @@
let floyd_warshall = FloydWarshall::directed();
- let paths = floyd_warshall.path_to(&graph, &nodes.c).unwrap();
+ let paths = floyd_warshall.path_to(&graph, nodes.c).unwrap();
let mut expected: HashSet<_> = [nodes.a, nodes.b, nodes.c].into_iter().collect();
// cost is tested above, this just checks that the filter is correct.
for route in paths {
- assert!(expected.remove(route.path().source().id()));
- assert_eq!(*route.path().target().id(), nodes.c);
+ assert!(expected.remove(&route.path().source().id()));
+ assert_eq!(route.path().target().id(), nodes.c);
}
}
@@ -285,7 +285,7 @@
let floyd_warshall = FloydWarshall::undirected();
let route = floyd_warshall
- .path_between(&graph, &nodes.a, &nodes.c)
+ .path_between(&graph, nodes.a, nodes.c)
.unwrap();
let (path, cost) = route.into_parts();
@@ -293,7 +293,7 @@
assert_eq!(
path.to_vec()
.into_iter()
- .map(|node| *node.id())
+ .map(|node| node.id())
.collect::<Vec<_>>(),
vec![nodes.a, nodes.b, nodes.c]
);
@@ -305,14 +305,14 @@
let floyd_warshall = FloydWarshall::undirected();
- let paths = floyd_warshall.path_from(&graph, &nodes.a).unwrap();
+ let paths = floyd_warshall.path_from(&graph, nodes.a).unwrap();
let mut expected: HashSet<_> = [nodes.a, nodes.b, nodes.c, nodes.d].into_iter().collect();
// cost is tested above, this just checks that the filter is correct.
for route in paths {
- assert_eq!(*route.path().source().id(), nodes.a);
- assert!(expected.remove(route.path().target().id()));
+ assert_eq!(route.path().source().id(), nodes.a);
+ assert!(expected.remove(&route.path().target().id()));
}
}
@@ -322,14 +322,14 @@
let floyd_warshall = FloydWarshall::undirected();
- let paths = floyd_warshall.path_to(&graph, &nodes.c).unwrap();
+ let paths = floyd_warshall.path_to(&graph, nodes.c).unwrap();
let mut expected: HashSet<_> = [nodes.a, nodes.b, nodes.c, nodes.d].into_iter().collect();
// cost is tested above, this just checks that the filter is correct.
for route in paths {
- assert!(expected.remove(route.path().source().id()));
- assert_eq!(*route.path().target().id(), nodes.c);
+ assert!(expected.remove(&route.path().source().id()));
+ assert_eq!(route.path().target().id(), nodes.c);
}
}
diff --git a/crates/algorithms/src/shortest_paths/mod.rs b/crates/algorithms/src/shortest_paths/mod.rs
index a30ea63..c199eaf 100644
--- a/crates/algorithms/src/shortest_paths/mod.rs
+++ b/crates/algorithms/src/shortest_paths/mod.rs
@@ -50,8 +50,8 @@
//! assert!(path.is_none());
//! ```
-// pub mod astar;
-// pub mod bellman_ford;
+pub mod astar;
+pub mod bellman_ford;
mod common;
pub mod dijkstra;
pub mod floyd_warshall;
@@ -60,8 +60,8 @@
use petgraph_core::{Graph, GraphStorage};
pub use self::{
- // astar::AStar,
- // bellman_ford::BellmanFord,
+ astar::AStar,
+ bellman_ford::BellmanFord,
common::{
cost::{Cost, GraphCost},
path::Path,
@@ -242,7 +242,7 @@
self.every_distance(graph)
.ok()?
.find(move |route| route.source().id() == source && route.target().id() == target)
- .map(|route| route.into_cost())
+ .map(DirectRoute::into_cost)
}
/// Returns an iterator over all shortest distances in the graph.
diff --git a/crates/algorithms/tests/shortest_paths/main.rs b/crates/algorithms/tests/shortest_paths/main.rs
index 5ce7a3d..e2fbafb 100644
--- a/crates/algorithms/tests/shortest_paths/main.rs
+++ b/crates/algorithms/tests/shortest_paths/main.rs
@@ -8,7 +8,7 @@
};
use petgraph_algorithms::shortest_paths::{
- bellman_ford::CandidateOrder, BellmanFord, Dijkstra, FloydWarshall, Route, ShortestPath,
+ BellmanFord, Dijkstra, FloydWarshall, Route, ShortestPath,
};
use petgraph_dino::{DiDinoGraph, DinoStorage, EdgeId, NodeId};
use snapbox::{utils::normalize_lines, Action};
@@ -18,10 +18,10 @@
fn setup(input_path: PathBuf) -> Case {
let name = input_path
.file_stem()
- .unwrap()
+ .expect("file stem")
.to_str()
- .unwrap()
- .to_string();
+ .expect("to str")
+ .to_owned();
let expected = input_path.with_extension("out");
@@ -49,10 +49,10 @@
}
fn parse(mut input: Lines<impl BufRead>) -> Result<Parse, Box<dyn Error>> {
- let meta = input.next().unwrap()?;
+ let meta = input.next().expect("first line")?;
let meta = meta
.split_whitespace()
- .map(|x| x.parse::<usize>())
+ .map(str::parse::<usize>)
.collect::<Result<Vec<_>, _>>()?;
let n = meta[0];
@@ -64,22 +64,22 @@
let mut nodes = Vec::with_capacity(n);
for index in 0..n {
- nodes.push(*graph.insert_node(index).id());
+ nodes.push(graph.insert_node(index).id());
}
let mut edges = Vec::with_capacity(m);
for _ in 0..m {
- let edge = input.next().unwrap()?;
+ let edge = input.next().expect("edge")?;
let edge = edge
.split_whitespace()
- .map(|x| x.parse::<usize>())
+ .map(str::parse::<usize>)
.collect::<Result<Vec<_>, _>>()?;
let u = edge[0];
let v = edge[1];
let w = edge[2] as u64;
- let id = *graph.insert_edge(w, &nodes[u], &nodes[v]).id();
+ let id = graph.insert_edge(w, nodes[u], nodes[v]).id();
edges.push(id);
}
@@ -101,7 +101,7 @@
// so we add `+ 2`, edges are between them, so we subtract `- 1`, resulting in `+ 1`
let y = route.path().transit().len() + 1;
- let mut nodes: Vec<_> = route.into_path().into_iter().map(|x| *x.weight()).collect();
+ let nodes: Vec<_> = route.into_path().into_iter().map(|x| *x.weight()).collect();
let mut output = vec![];
output.push(format!("{x} {y}"));
@@ -127,9 +127,9 @@
} = read(input_path)?;
let route = match name {
- "dijkstra" => Dijkstra::directed().path_between(&graph, &source, &target),
- "bellman_ford" => BellmanFord::directed().path_between(&graph, &source, &target),
- "floyd_warshall" => FloydWarshall::directed().path_between(&graph, &source, &target),
+ "dijkstra" => Dijkstra::directed().path_between(&graph, source, target),
+ "bellman_ford" => BellmanFord::directed().path_between(&graph, source, target),
+ "floyd_warshall" => FloydWarshall::directed().path_between(&graph, source, target),
_ => unreachable!(),
};
diff --git a/crates/utils/src/lib.rs b/crates/utils/src/lib.rs
index 40e3939..bd87a13 100644
--- a/crates/utils/src/lib.rs
+++ b/crates/utils/src/lib.rs
@@ -65,7 +65,7 @@
$graph:ident; $output:ident; $name:ident[$($id:ident : $attr:expr),* $(,)?]
) => {
let $output = $name {
- $($id: *$graph.insert_node($attr).id(),)*
+ $($id: $graph.insert_node($attr).id(),)*
};
};
@@ -73,17 +73,17 @@
@insert: edge
$graph:ident; $nodes:ident; $source:ident; $target:ident; $attr:expr
) => {
- *$graph.insert_edge($attr, &$nodes.$source, &$nodes.$target).id()
+ $graph.insert_edge($attr, $nodes.$source, $nodes.$target).id()
};
(
@insert: edge
$graph:ident; $nodes:ident; $source:ident; $target:ident; @{ $attr:expr }
) => {{
- let $source = $graph.node(&$nodes.$source).unwrap().weight();
- let $target = $graph.node(&$nodes.$target).unwrap().weight();
+ let $source = $graph.node($nodes.$source).unwrap().weight();
+ let $target = $graph.node($nodes.$target).unwrap().weight();
- *$graph.insert_edge($attr, &$nodes.$source, &$nodes.$target).id()
+ $graph.insert_edge($attr, $nodes.$source, $nodes.$target).id()
}};
(