Simplify trait definition (#605)
diff --git a/Cargo.toml b/Cargo.toml
index 503db1e..24a1bc6 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -39,7 +39,7 @@
petgraph-core = { path = "crates/core", default-features = false }
petgraph-graph = { path = "crates/graph", default-features = false }
petgraph-adjacency-matrix = { path = "crates/adjacency-matrix", default-features = false }
-petgraph-graphmap = { path = "crates/graphmap", default-features = false }
+petgraph-entry = { path = "crates/entry", default-features = false }
petgraph-csr = { path = "crates/csr", default-features = false }
petgraph-matrix-graph = { path = "crates/matrix-graph", default-features = false }
petgraph-algorithms = { path = "crates/algorithms", default-features = false }
@@ -61,19 +61,18 @@
petgraph-generators = { workspace = true, default-features = false, optional = true }
petgraph-io = { workspace = true, default-features = false, optional = true }
-[dependencies.petgraph-graphmap]
+[dependencies.petgraph-entry]
workspace = true
-features = ["convert"]
optional = true
default-features = false
[features]
adjacency-matrix = ["dep:petgraph-adjacency-matrix", "petgraph-algorithms/remove-me-only-intended-for-move-adjacency-matrix"]
csr = ["dep:petgraph-csr"]
-default = ["std", "graphmap", "stable-graph", "matrix-graph", "csr", "adjacency-matrix"]
-graphmap = ["dep:petgraph-graphmap"]
+default = ["std", "entry", "stable-graph", "matrix-graph", "csr", "adjacency-matrix"]
+entry = ["dep:petgraph-entry"]
matrix-graph = ["dep:petgraph-matrix-graph"]
-serde = ["petgraph-graph/serde", "petgraph-graphmap?/serde"]
+serde = ["petgraph-graph/serde"]
stable-graph = ["petgraph-graph/stable"]
io = ["petgraph-io"]
unstable-generators = ["petgraph-generators"]
@@ -81,11 +80,10 @@
"petgraph-core/std",
"petgraph-graph/std",
"petgraph-adjacency-matrix?/std",
- "petgraph-graphmap?/std",
"petgraph-csr?/std",
"petgraph-io?/std"
]
-proptest = ["petgraph-graph/proptest", "petgraph-graphmap?/proptest"]
+proptest = ["petgraph-graph/proptest"]
[lib]
bench = false
diff --git a/crates/algorithms/benches/shortest_paths/large.rs b/crates/algorithms/benches/shortest_paths/large.rs
index 18a31ef..15725da 100644
--- a/crates/algorithms/benches/shortest_paths/large.rs
+++ b/crates/algorithms/benches/shortest_paths/large.rs
@@ -9,7 +9,8 @@
use criterion::{criterion_group, BenchmarkId, Criterion};
use petgraph_algorithms::shortest_paths::{Dijkstra, ShortestDistance};
-use petgraph_dino::{DiDinoGraph, NodeId};
+use petgraph_core::node::NodeId;
+use petgraph_dino::DiDinoGraph;
fn get_cargo_workspace() -> Arc<Path> {
static WORKSPACES: Mutex<BTreeMap<String, Arc<Path>>> = Mutex::new(BTreeMap::new());
diff --git a/crates/algorithms/src/shortest_paths/astar/impl.rs b/crates/algorithms/src/shortest_paths/astar/impl.rs
index a8d1869..cf79bfc 100644
--- a/crates/algorithms/src/shortest_paths/astar/impl.rs
+++ b/crates/algorithms/src/shortest_paths/astar/impl.rs
@@ -3,7 +3,11 @@
use error_stack::{Report, Result};
use numi::num::{identity::Zero, ops::AddRef};
use petgraph_core::{
- id::{AssociativeGraphId, AttributeMapper},
+ node::NodeId,
+ storage::{
+ auxiliary::{FrequencyHint, Hints, OccupancyHint, PerformanceHint, SecondaryGraphStorage},
+ AuxiliaryGraphStorage,
+ },
Graph, GraphStorage, Node,
};
@@ -22,7 +26,6 @@
pub(super) struct AStarImpl<'graph: 'parent, 'parent, S, E, H, C>
where
S: GraphStorage,
- S::NodeId: AssociativeGraphId<S>,
E: GraphCost<S>,
E::Value: Ord,
{
@@ -38,15 +41,14 @@
predecessor_mode: PredecessorMode,
- distances: <S::NodeId as AssociativeGraphId<S>>::AttributeMapper<'graph, E::Value>,
- estimates: <S::NodeId as AssociativeGraphId<S>>::AttributeMapper<'graph, E::Value>,
- predecessors: <S::NodeId as AssociativeGraphId<S>>::AttributeMapper<'graph, Option<S::NodeId>>,
+ distances: S::SecondaryNodeStorage<'graph, E::Value>,
+ estimates: S::SecondaryNodeStorage<'graph, E::Value>,
+ predecessors: S::SecondaryNodeStorage<'graph, Option<NodeId>>,
}
impl<'graph: 'parent, 'parent, S, E, H, C> AStarImpl<'graph, 'parent, S, E, H, C>
where
S: GraphStorage,
- S::NodeId: AssociativeGraphId<S>,
E: GraphCost<S>,
E::Value: AStarMeasure,
H: GraphHeuristic<S, Value = E::Value>,
@@ -59,8 +61,8 @@
heuristic: &'parent H,
connections: C,
- source: S::NodeId,
- target: S::NodeId,
+ source: NodeId,
+ target: NodeId,
predecessor_mode: PredecessorMode,
) -> Result<Self, AStarError> {
@@ -79,13 +81,30 @@
queue.push(source_node.id(), estimate.clone().into_owned());
- let mut distances = <S::NodeId as AssociativeGraphId<S>>::attribute_mapper(graph.storage());
+ let mut distances = graph.storage().secondary_node_storage(Hints {
+ performance: PerformanceHint {
+ read: FrequencyHint::Frequent,
+ write: FrequencyHint::Frequent,
+ },
+ occupancy: OccupancyHint::Dense,
+ });
distances.set(source, E::Value::zero());
- let estimates = <S::NodeId as AssociativeGraphId<S>>::attribute_mapper(graph.storage());
+ let estimates = graph.storage().secondary_node_storage(Hints {
+ performance: PerformanceHint {
+ read: FrequencyHint::Frequent,
+ write: FrequencyHint::Infrequent,
+ },
+ occupancy: OccupancyHint::Dense,
+ });
- let mut predecessors =
- <S::NodeId as AssociativeGraphId<S>>::attribute_mapper(graph.storage());
+ let mut predecessors = graph.storage().secondary_node_storage(Hints {
+ performance: PerformanceHint {
+ read: FrequencyHint::Infrequent,
+ write: FrequencyHint::Frequent,
+ },
+ occupancy: OccupancyHint::Dense,
+ });
if predecessor_mode == PredecessorMode::Record {
predecessors.set(source, None);
}
diff --git a/crates/algorithms/src/shortest_paths/astar/mod.rs b/crates/algorithms/src/shortest_paths/astar/mod.rs
index 4109c44..f1c0e4a 100644
--- a/crates/algorithms/src/shortest_paths/astar/mod.rs
+++ b/crates/algorithms/src/shortest_paths/astar/mod.rs
@@ -12,8 +12,9 @@
use error_stack::Result;
use petgraph_core::{
edge::marker::{Directed, Undirected},
- id::AssociativeGraphId,
- DirectedGraphStorage, Graph, GraphDirectionality, GraphStorage, Node,
+ node::NodeId,
+ storage::AuxiliaryGraphStorage,
+ DirectedGraphStorage, Graph, GraphDirectionality, GraphStorage,
};
use self::r#impl::AStarImpl;
@@ -80,12 +81,12 @@
/// let algorithm = AStar::directed().with_heuristic(heuristic);
///
/// let mut graph = DiDinoGraph::new();
- /// let a = *graph.insert_node((0, 1)).id();
- /// let b = *graph.insert_node((2, 2)).id();
+ /// let a = graph.insert_node((0, 1)).id();
+ /// let b = graph.insert_node((2, 2)).id();
///
- /// graph.insert_edge(5, &a, &b);
+ /// graph.insert_edge(5, a, b);
///
- /// let path = algorithm.path_between(&graph, &a, &b).expect("path exists");
+ /// let path = algorithm.path_between(&graph, a, b).expect("path exists");
/// assert_eq!(path.cost().into_value(), 5);
/// ```
pub fn directed() -> Self {
@@ -144,13 +145,12 @@
fn call<'graph: 'this, 'this, S>(
&'this self,
graph: &'graph Graph<S>,
- source: S::NodeId,
- target: S::NodeId,
+ source: NodeId,
+ target: NodeId,
intermediates: PredecessorMode,
) -> Result<AStarImpl<'graph, 'this, S, E, H, impl Connections<'graph, S> + 'this>, AStarError>
where
S: DirectedGraphStorage,
- S::NodeId: AssociativeGraphId<S>,
E: GraphCost<S>,
E::Value: AStarMeasure,
H: GraphHeuristic<S, Value = E::Value>,
@@ -171,13 +171,12 @@
fn call<'graph: 'this, 'this, S>(
&'this self,
graph: &'graph Graph<S>,
- source: S::NodeId,
- target: S::NodeId,
+ source: NodeId,
+ target: NodeId,
intermediates: PredecessorMode,
) -> Result<AStarImpl<'graph, 'this, S, E, H, impl Connections<'graph, S> + 'this>, AStarError>
where
S: GraphStorage,
- S::NodeId: AssociativeGraphId<S>,
E: GraphCost<S>,
E::Value: AStarMeasure,
H: GraphHeuristic<S, Value = E::Value>,
@@ -200,7 +199,6 @@
impl<S, E, H> ShortestPath<S> for AStar<Undirected, E, H>
where
S: GraphStorage,
- S::NodeId: AssociativeGraphId<S>,
E: GraphCost<S>,
E::Value: AStarMeasure,
H: GraphHeuristic<S, Value = E::Value>,
@@ -211,7 +209,7 @@
fn path_to<'graph: 'this, 'this>(
&'this self,
graph: &'graph Graph<S>,
- target: S::NodeId,
+ target: NodeId,
) -> Result<impl Iterator<Item = Route<'graph, S, Self::Cost>> + 'this, Self::Error> {
let sources = graph.nodes().map(|node| node.id());
@@ -225,7 +223,7 @@
fn path_from<'graph: 'this, 'this>(
&'this self,
graph: &'graph Graph<S>,
- source: S::NodeId,
+ source: NodeId,
) -> Result<impl Iterator<Item = Route<'graph, S, Self::Cost>> + 'this, Self::Error> {
let targets = graph.nodes().map(|node| node.id());
@@ -239,8 +237,8 @@
fn path_between<'graph>(
&self,
graph: &'graph Graph<S>,
- source: S::NodeId,
- target: S::NodeId,
+ source: NodeId,
+ target: NodeId,
) -> Option<Route<'graph, S, Self::Cost>> {
self.call(graph, source, target, PredecessorMode::Record)
.ok()?
@@ -269,7 +267,6 @@
impl<S, E, H> ShortestDistance<S> for AStar<Undirected, E, H>
where
S: GraphStorage,
- S::NodeId: AssociativeGraphId<S>,
E: GraphCost<S>,
E::Value: AStarMeasure,
H: GraphHeuristic<S, Value = E::Value>,
@@ -280,7 +277,7 @@
fn distance_to<'graph: 'this, 'this>(
&'this self,
graph: &'graph Graph<S>,
- target: S::NodeId,
+ target: NodeId,
) -> Result<impl Iterator<Item = DirectRoute<'graph, S, Self::Cost>> + 'this, Self::Error> {
let sources = graph.nodes().map(|node| node.id());
@@ -294,7 +291,7 @@
fn distance_from<'graph: 'this, 'this>(
&'this self,
graph: &'graph Graph<S>,
- source: S::NodeId,
+ source: NodeId,
) -> Result<impl Iterator<Item = DirectRoute<'graph, S, Self::Cost>> + 'this, Self::Error> {
let targets = graph.nodes().map(|node| node.id());
@@ -308,8 +305,8 @@
fn distance_between(
&self,
graph: &Graph<S>,
- source: S::NodeId,
- target: S::NodeId,
+ source: NodeId,
+ target: NodeId,
) -> Option<Cost<Self::Cost>> {
self.call(graph, source, target, PredecessorMode::Discard)
.ok()?
@@ -339,7 +336,6 @@
impl<S, E, H> ShortestPath<S> for AStar<Directed, E, H>
where
S: DirectedGraphStorage,
- S::NodeId: AssociativeGraphId<S>,
E: GraphCost<S>,
E::Value: AStarMeasure,
H: GraphHeuristic<S, Value = E::Value>,
@@ -350,7 +346,7 @@
fn path_to<'graph: 'this, 'this>(
&'this self,
graph: &'graph Graph<S>,
- target: S::NodeId,
+ target: NodeId,
) -> Result<impl Iterator<Item = Route<'graph, S, Self::Cost>> + 'this, Self::Error> {
let sources = graph.nodes().map(|node| node.id());
@@ -364,7 +360,7 @@
fn path_from<'graph: 'this, 'this>(
&'this self,
graph: &'graph Graph<S>,
- source: S::NodeId,
+ source: NodeId,
) -> Result<impl Iterator<Item = Route<'graph, S, Self::Cost>> + 'this, Self::Error> {
let targets = graph.nodes().map(|node| node.id());
@@ -378,8 +374,8 @@
fn path_between<'graph>(
&self,
graph: &'graph Graph<S>,
- source: S::NodeId,
- target: S::NodeId,
+ source: NodeId,
+ target: NodeId,
) -> Option<Route<'graph, S, Self::Cost>> {
self.call(graph, source, target, PredecessorMode::Record)
.ok()?
@@ -408,7 +404,6 @@
impl<S, E, H> ShortestDistance<S> for AStar<Directed, E, H>
where
S: DirectedGraphStorage,
- S::NodeId: AssociativeGraphId<S>,
E: GraphCost<S>,
E::Value: AStarMeasure,
H: GraphHeuristic<S, Value = E::Value>,
@@ -419,7 +414,7 @@
fn distance_to<'graph: 'this, 'this>(
&'this self,
graph: &'graph Graph<S>,
- target: S::NodeId,
+ target: NodeId,
) -> Result<impl Iterator<Item = DirectRoute<'graph, S, Self::Cost>> + 'this, Self::Error> {
let sources = graph.nodes().map(|node| node.id());
@@ -433,7 +428,7 @@
fn distance_from<'graph: 'this, 'this>(
&'this self,
graph: &'graph Graph<S>,
- source: S::NodeId,
+ source: NodeId,
) -> Result<impl Iterator<Item = DirectRoute<'graph, S, Self::Cost>> + 'this, Self::Error> {
let targets = graph.nodes().map(|node| node.id());
@@ -447,8 +442,8 @@
fn distance_between(
&self,
graph: &Graph<S>,
- source: S::NodeId,
- target: S::NodeId,
+ source: NodeId,
+ target: NodeId,
) -> Option<Cost<Self::Cost>> {
self.call(graph, source, target, PredecessorMode::Discard)
.ok()?
diff --git a/crates/algorithms/src/shortest_paths/astar/tests.rs b/crates/algorithms/src/shortest_paths/astar/tests.rs
index 8ff18c1..ee676c2 100644
--- a/crates/algorithms/src/shortest_paths/astar/tests.rs
+++ b/crates/algorithms/src/shortest_paths/astar/tests.rs
@@ -4,7 +4,7 @@
use numi::borrow::Moo;
use ordered_float::NotNan;
use petgraph_core::{edge::marker::Directed, Edge, GraphStorage, Node};
-use petgraph_dino::{DiDinoGraph, DinoStorage, EdgeId, NodeId};
+use petgraph_dino::{DiDinoGraph, DinoStorage};
use petgraph_utils::{graph, GraphCollection};
use crate::shortest_paths::{AStar, ShortestDistance, ShortestPath};
@@ -21,7 +21,7 @@
x: "X",
y: "Y",
z: "Z",
- ] as NodeId,
+ ],
[
su: s -> u: 10,
sx: s -> x: 5,
@@ -33,7 +33,7 @@
xy: x -> y: 2,
ys: y -> s: 7,
yv: y -> v: 6,
- ] as EdgeId
+ ]
);
#[derive(Debug, Copy, Clone, PartialEq)]
@@ -62,7 +62,7 @@
e: Point { x: 3.0, y: 3.0 },
f: Point { x: 4.0, y: 2.0 },
g: Point { x: 5.0, y: 5.0 },
- ] as NodeId,
+ ],
[
ab: a -> b: @{ a.distance(*b) },
ad: a -> d: @{ a.distance(*d) },
@@ -71,7 +71,7 @@
ce: c -> e: @{ c.distance(*e) },
ef: e -> f: @{ e.distance(*f) },
de: d -> e: @{ d.distance(*e) },
- ] as EdgeId
+ ]
);
const fn no_heuristic<'a, S>(_: Node<'a, S>, _: Node<'a, S>) -> Moo<'a, usize>
@@ -255,17 +255,17 @@
b: "B",
c: "C",
d: "D",
- ] as NodeId,
+ ],
[
ab: a -> b: 3,
bc: b -> c: 3,
cd: c -> d: 3,
ac: a -> c: 8,
ad: a -> d: 10,
- ] as EdgeId
+ ]
);
-fn admissible_inconsistent<'a, S>(source: Node<'a, S>, target: Node<'a, S>) -> Moo<'a, usize>
+fn admissible_inconsistent<'a, S>(source: Node<'a, S>, _target: Node<'a, S>) -> Moo<'a, usize>
where
S: GraphStorage,
S::NodeWeight: AsRef<str>,
@@ -311,14 +311,14 @@
c: 'C',
d: 'D',
e: 'E',
- ] as NodeId,
+ ],
[
ab: a -> b: 2,
ac: a -> c: 3,
bd: b -> d: 3,
cd: c -> d: 1,
de: d -> e: 1,
- ] as EdgeId
+ ]
);
#[test]
diff --git a/crates/algorithms/src/shortest_paths/bellman_ford/impl.rs b/crates/algorithms/src/shortest_paths/bellman_ford/impl.rs
index f0b9963..b25907d 100644
--- a/crates/algorithms/src/shortest_paths/bellman_ford/impl.rs
+++ b/crates/algorithms/src/shortest_paths/bellman_ford/impl.rs
@@ -5,7 +5,7 @@
use fxhash::FxBuildHasher;
use hashbrown::HashMap;
use numi::num::{identity::Zero, ops::AddRef};
-use petgraph_core::{Graph, GraphStorage, Node};
+use petgraph_core::{node::NodeId, Graph, GraphStorage, Node};
use super::error::BellmanFordError;
use crate::shortest_paths::{
@@ -26,7 +26,6 @@
) -> bool
where
S: GraphStorage,
- S::NodeId: Eq + Hash,
E: GraphCost<S>,
E::Value: BellmanFordMeasure,
{
@@ -52,7 +51,7 @@
) -> bool
where
S: GraphStorage,
- S::NodeId: Eq + Hash,
+ NodeId: Eq + Hash,
E: GraphCost<S>,
E::Value: BellmanFordMeasure,
{
@@ -94,20 +93,14 @@
}
}
-struct Heuristic<S>
-where
- S: GraphStorage,
-{
+// TODO: make use of auxiliary graph storage
+struct Heuristic {
enabled: bool,
- recent_update: HashMap<S::NodeId, (S::NodeId, S::NodeId)>,
- predecessor: HashMap<S::NodeId, S::NodeId>,
+ recent_update: HashMap<NodeId, (NodeId, NodeId)>,
+ predecessor: HashMap<NodeId, NodeId>,
}
-impl<S> Heuristic<S>
-where
- S: GraphStorage,
- S::NodeId: Eq + Hash,
-{
+impl Heuristic {
fn new(enabled: bool) -> Self {
Self {
enabled,
@@ -116,11 +109,7 @@
}
}
- fn update(
- &mut self,
- source: S::NodeId,
- target: S::NodeId,
- ) -> core::result::Result<(), S::NodeId> {
+ fn update(&mut self, source: NodeId, target: NodeId) -> core::result::Result<(), NodeId> {
if !self.enabled {
return Ok(());
}
@@ -168,14 +157,13 @@
candidate_order: CandidateOrder,
negative_cycle_heuristics: bool,
- distances: HashMap<S::NodeId, E::Value, FxBuildHasher>,
- predecessors: HashMap<S::NodeId, Vec<Node<'graph, S>>, FxBuildHasher>,
+ distances: HashMap<NodeId, E::Value, FxBuildHasher>,
+ predecessors: HashMap<NodeId, Vec<Node<'graph, S>>, FxBuildHasher>,
}
impl<'graph: 'parent, 'parent, S, E, G> ShortestPathFasterImpl<'graph, 'parent, S, E, G>
where
S: GraphStorage,
- S::NodeId: Eq + Hash,
E: GraphCost<S>,
E::Value: BellmanFordMeasure,
G: Connections<'graph, S>,
@@ -186,7 +174,7 @@
edge_cost: &'parent E,
connections: G,
- source: S::NodeId,
+ source: NodeId,
predecessor_mode: PredecessorMode,
candidate_order: CandidateOrder,
@@ -226,10 +214,10 @@
/// Inner Relaxation Loop for the Bellman-Ford algorithm, an implementation of SPFA.
///
/// Based on [networkx](https://github.com/networkx/networkx/blob/f93f0e2a066fc456aa447853af9d00eec1058542/networkx/algorithms/shortest_paths/weighted.py#L1363)
- fn relax(&mut self) -> core::result::Result<(), S::NodeId> {
+ fn relax(&mut self) -> core::result::Result<(), NodeId> {
// we always need to record predecessors to be able to skip relaxations
let mut queue = DoubleEndedQueue::new();
- let mut heuristic: Heuristic<S> = Heuristic::new(self.negative_cycle_heuristics);
+ let mut heuristic = Heuristic::new(self.negative_cycle_heuristics);
let mut occurrences = HashMap::new();
let num_nodes = self.graph.num_nodes();
@@ -325,7 +313,7 @@
Ok(())
}
- pub(crate) fn between(mut self, target: S::NodeId) -> Option<Route<'graph, S, E::Value>> {
+ pub(crate) fn between(mut self, target: NodeId) -> Option<Route<'graph, S, E::Value>> {
let cost = self.distances.remove(&target)?;
let target = self.graph.node(target)?;
diff --git a/crates/algorithms/src/shortest_paths/bellman_ford/measure.rs b/crates/algorithms/src/shortest_paths/bellman_ford/measure.rs
index d0ac726..0e48f49 100644
--- a/crates/algorithms/src/shortest_paths/bellman_ford/measure.rs
+++ b/crates/algorithms/src/shortest_paths/bellman_ford/measure.rs
@@ -4,7 +4,7 @@
};
use numi::{
- cast::{CastFrom, TryCastFrom},
+ cast::TryCastFrom,
num::{identity::Zero, ops::AddRef},
};
diff --git a/crates/algorithms/src/shortest_paths/bellman_ford/mod.rs b/crates/algorithms/src/shortest_paths/bellman_ford/mod.rs
index 24875b7..7190e97 100644
--- a/crates/algorithms/src/shortest_paths/bellman_ford/mod.rs
+++ b/crates/algorithms/src/shortest_paths/bellman_ford/mod.rs
@@ -6,11 +6,11 @@
mod tests;
use alloc::vec::Vec;
-use core::hash::Hash;
use error_stack::Result;
use petgraph_core::{
edge::marker::{Directed, Undirected},
+ node::NodeId,
DirectedGraphStorage, Graph, GraphDirectionality, GraphStorage,
};
@@ -96,15 +96,15 @@
/// let algorithm = BellmanFord::directed();
///
/// 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(2, &a, &b);
+ /// graph.insert_edge(2, a, b);
///
- /// let path = algorithm.path_between(&graph, &a, &b);
+ /// let path = algorithm.path_between(&graph, a, b);
/// assert!(path.is_some());
///
- /// let path = algorithm.path_between(&graph, &b, &a);
+ /// let path = algorithm.path_between(&graph, b, a);
/// assert!(path.is_none());
/// ```
pub fn directed() -> Self {
@@ -132,15 +132,15 @@
/// let algorithm = BellmanFord::undirected();
///
/// 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(2, &a, &b);
+ /// graph.insert_edge(2, a, b);
///
- /// let path = algorithm.path_between(&graph, &a, &b);
+ /// let path = algorithm.path_between(&graph, a, b);
/// assert!(path.is_some());
///
- /// let path = algorithm.path_between(&graph, &b, &a);
+ /// let path = algorithm.path_between(&graph, b, a);
/// assert!(path.is_some());
/// ```
pub fn undirected() -> Self {
@@ -184,12 +184,12 @@
/// let algorithm = BellmanFord::directed().with_edge_cost(edge_cost);
///
/// 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("AB", &a, &b);
+ /// graph.insert_edge("AB", a, b);
///
- /// let path = algorithm.path_between(&graph, &a, &b);
+ /// let path = algorithm.path_between(&graph, a, b);
/// assert!(path.is_some());
/// ```
pub fn with_edge_cost<S, F>(self, edge_cost: F) -> BellmanFord<D, F>
@@ -221,12 +221,12 @@
/// let algorithm = BellmanFord::directed().with_candidate_order(CandidateOrder::LargeLast);
///
/// 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(2, &a, &b);
+ /// graph.insert_edge(2, a, b);
///
- /// let path = algorithm.path_between(&graph, &a, &b);
+ /// let path = algorithm.path_between(&graph, a, b);
/// assert!(path.is_some());
/// ```
#[must_use]
@@ -261,12 +261,12 @@
/// let algorithm = BellmanFord::directed().with_negative_cycle_heuristics(false);
///
/// 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(2, &a, &b);
+ /// graph.insert_edge(2, a, b);
///
- /// let path = algorithm.path_between(&graph, &a, &b);
+ /// let path = algorithm.path_between(&graph, a, b);
/// assert!(path.is_some());
/// ```
#[must_use]
@@ -283,7 +283,6 @@
impl<S, E> ShortestPath<S> for BellmanFord<Undirected, E>
where
S: GraphStorage,
- S::NodeId: PartialEq + Eq + Hash,
E: GraphCost<S>,
E::Value: BellmanFordMeasure,
{
@@ -293,7 +292,7 @@
fn path_to<'graph: 'this, 'this>(
&'this self,
graph: &'graph Graph<S>,
- target: S::NodeId,
+ target: NodeId,
) -> Result<impl Iterator<Item = Route<'graph, S, Self::Cost>>, Self::Error> {
let iter = self.path_from(graph, target)?;
@@ -303,7 +302,7 @@
fn path_from<'graph: 'this, 'this>(
&'this self,
graph: &'graph Graph<S>,
- source: S::NodeId,
+ source: NodeId,
) -> Result<impl Iterator<Item = Route<'graph, S, Self::Cost>>, Self::Error> {
ShortestPathFasterImpl::new(
graph,
@@ -320,8 +319,8 @@
fn path_between<'graph>(
&self,
graph: &'graph Graph<S>,
- source: S::NodeId,
- target: S::NodeId,
+ source: NodeId,
+ target: NodeId,
) -> Option<Route<'graph, S, Self::Cost>> {
ShortestPathFasterImpl::new(
graph,
@@ -352,7 +351,6 @@
impl<S, E> ShortestDistance<S> for BellmanFord<Undirected, E>
where
S: GraphStorage,
- S::NodeId: Eq + Hash,
E: GraphCost<S>,
E::Value: BellmanFordMeasure,
{
@@ -362,7 +360,7 @@
fn distance_to<'graph: 'this, 'this>(
&'this self,
graph: &'graph Graph<S>,
- target: S::NodeId,
+ target: NodeId,
) -> Result<impl Iterator<Item = DirectRoute<'graph, S, Self::Cost>>, Self::Error> {
let iter = self.distance_from(graph, target)?;
@@ -372,7 +370,7 @@
fn distance_from<'graph: 'this, 'this>(
&'this self,
graph: &'graph Graph<S>,
- source: S::NodeId,
+ source: NodeId,
) -> Result<impl Iterator<Item = DirectRoute<'graph, S, Self::Cost>> + 'this, Self::Error> {
let iter = ShortestPathFasterImpl::new(
graph,
@@ -390,8 +388,8 @@
fn distance_between(
&self,
graph: &Graph<S>,
- source: S::NodeId,
- target: S::NodeId,
+ source: NodeId,
+ target: NodeId,
) -> Option<Cost<Self::Cost>> {
ShortestPathFasterImpl::new(
graph,
@@ -423,7 +421,6 @@
impl<S, E> ShortestPath<S> for BellmanFord<Directed, E>
where
S: DirectedGraphStorage,
- S::NodeId: Eq + Hash,
E: GraphCost<S>,
E::Value: BellmanFordMeasure,
{
@@ -433,7 +430,7 @@
fn path_from<'graph: 'this, 'this>(
&'this self,
graph: &'graph Graph<S>,
- source: S::NodeId,
+ source: NodeId,
) -> Result<impl Iterator<Item = Route<'graph, S, Self::Cost>> + 'this, Self::Error> {
ShortestPathFasterImpl::new(
graph,
@@ -450,8 +447,8 @@
fn path_between<'graph>(
&self,
graph: &'graph Graph<S>,
- source: S::NodeId,
- target: S::NodeId,
+ source: NodeId,
+ target: NodeId,
) -> Option<Route<'graph, S, Self::Cost>> {
ShortestPathFasterImpl::new(
graph,
@@ -482,7 +479,6 @@
impl<S, E> ShortestDistance<S> for BellmanFord<Directed, E>
where
S: DirectedGraphStorage,
- S::NodeId: Eq + Hash,
E: GraphCost<S>,
E::Value: BellmanFordMeasure,
{
@@ -492,7 +488,7 @@
fn distance_from<'graph: 'this, 'this>(
&'this self,
graph: &'graph Graph<S>,
- source: S::NodeId,
+ source: NodeId,
) -> Result<impl Iterator<Item = DirectRoute<'graph, S, Self::Cost>>, Self::Error> {
let iter = ShortestPathFasterImpl::new(
graph,
@@ -510,8 +506,8 @@
fn distance_between(
&self,
graph: &Graph<S>,
- source: S::NodeId,
- target: S::NodeId,
+ source: NodeId,
+ target: NodeId,
) -> Option<Cost<Self::Cost>> {
ShortestPathFasterImpl::new(
graph,
diff --git a/crates/algorithms/src/shortest_paths/bellman_ford/tests.rs b/crates/algorithms/src/shortest_paths/bellman_ford/tests.rs
index 3295888..3405aee 100644
--- a/crates/algorithms/src/shortest_paths/bellman_ford/tests.rs
+++ b/crates/algorithms/src/shortest_paths/bellman_ford/tests.rs
@@ -2,10 +2,14 @@
use core::array;
use petgraph_core::{
- edge::marker::{Directed, Undirected},
- Graph, GraphStorage, ManagedGraphId,
+ edge::{
+ marker::{Directed, Undirected},
+ EdgeId,
+ },
+ node::NodeId,
+ Graph, GraphStorage,
};
-use petgraph_dino::{DiDinoGraph, DinoStorage, EdgeId, NodeId};
+use petgraph_dino::{DiDinoGraph, DinoStorage};
use petgraph_utils::{graph, GraphCollection};
use super::BellmanFord;
@@ -26,7 +30,7 @@
c: "C",
d: "D",
e: "E",
- ] as NodeId, [
+ ], [
ab: a -> b: 10f32,
ac: a -> c: 5f32,
bd: b -> d: 1f32,
@@ -37,7 +41,7 @@
ce: c -> e: 2f32,
ea: e -> a: 7f32,
ed: e -> d: 6f32,
- ] as EdgeId
+ ]
);
#[test]
@@ -80,11 +84,9 @@
assert!(spfa.every_path(&graph).is_ok());
}
-fn cycle_graph<const N: usize, S>() -> (Graph<S>, [S::NodeId; N], [S::EdgeId; N])
+fn cycle_graph<const N: usize, S>() -> (Graph<S>, [NodeId; N], [EdgeId; N])
where
S: GraphStorage<NodeWeight = usize, EdgeWeight = f32>,
- S::NodeId: ManagedGraphId + Copy,
- S::EdgeId: ManagedGraphId + Copy,
{
let mut graph = Graph::new();
@@ -99,11 +101,9 @@
(graph, nodes, edges)
}
-fn complete_graph<const N: usize, S>() -> (Graph<S>, [S::NodeId; N], Vec<S::EdgeId>)
+fn complete_graph<const N: usize, S>() -> (Graph<S>, [NodeId; N], Vec<EdgeId>)
where
S: GraphStorage<NodeWeight = usize, EdgeWeight = f32>,
- S::NodeId: ManagedGraphId + Copy,
- S::EdgeId: ManagedGraphId + Copy,
{
let mut graph = Graph::new();
@@ -120,11 +120,9 @@
(graph, nodes, edges)
}
-fn path_graph<const N: usize, S>() -> (Graph<S>, [S::NodeId; N], Vec<S::EdgeId>)
+fn path_graph<const N: usize, S>() -> (Graph<S>, [NodeId; N], Vec<EdgeId>)
where
S: GraphStorage<NodeWeight = usize, EdgeWeight = f32>,
- S::NodeId: ManagedGraphId + Copy,
- S::EdgeId: ManagedGraphId + Copy,
{
let mut graph = Graph::new();
diff --git a/crates/algorithms/src/shortest_paths/common/closures.rs b/crates/algorithms/src/shortest_paths/common/closures.rs
index 90fd2ed..4b360cc 100644
--- a/crates/algorithms/src/shortest_paths/common/closures.rs
+++ b/crates/algorithms/src/shortest_paths/common/closures.rs
@@ -1,10 +1,6 @@
-use core::marker::PhantomData;
-
use numi::borrow::Moo;
use petgraph_core::{Edge, GraphStorage, Node};
-use crate::shortest_paths::GraphCost;
-
pub fn cost<S, T>(closure: impl Fn(Edge<S>) -> Moo<T>) -> impl Fn(Edge<S>) -> Moo<T> {
closure
}
@@ -40,12 +36,12 @@
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]
fn bind_heuristic() {
- let closure = heuristic(|source, target| Moo::Owned(0i32));
+ let closure = heuristic(|_source, _target| Moo::Owned(0i32));
let algorithm = AStar::undirected().with_heuristic(closure);
@@ -55,12 +51,6 @@
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");
}
}
-
-// pub fn bind_heuristic<S, T>(
-// closure: impl Fn(Node<S>, Node<S>) -> Moo<T>,
-// ) -> impl Fn(Node<S>, Node<S>) -> Moo<T> {
-// closure
-// }
diff --git a/crates/algorithms/src/shortest_paths/common/connections.rs b/crates/algorithms/src/shortest_paths/common/connections.rs
index ad1006a..8d8d9f5 100644
--- a/crates/algorithms/src/shortest_paths/common/connections.rs
+++ b/crates/algorithms/src/shortest_paths/common/connections.rs
@@ -5,6 +5,7 @@
marker::{Directed, Undirected},
Direction,
},
+ node::NodeId,
DirectedGraphStorage, Edge, GraphDirectionality, GraphStorage,
};
@@ -45,14 +46,14 @@
where
S: GraphStorage + 'a,
{
- fn connections(&self, node: S::NodeId) -> impl Iterator<Item = Edge<'a, S>> + 'a;
+ fn connections(&self, node: NodeId) -> impl Iterator<Item = Edge<'a, S>> + 'a;
}
impl<'graph, S> Connections<'graph, S> for NodeConnections<'graph, S, Directed>
where
S: DirectedGraphStorage,
{
- fn connections(&self, node: S::NodeId) -> impl Iterator<Item = Edge<'graph, S>> + 'graph {
+ fn connections(&self, node: NodeId) -> impl Iterator<Item = Edge<'graph, S>> + 'graph {
self.storage
.node_directed_connections(node, Direction::Outgoing)
}
@@ -62,7 +63,7 @@
where
S: GraphStorage,
{
- fn connections(&self, node: S::NodeId) -> impl Iterator<Item = Edge<'graph, S>> + 'graph {
+ fn connections(&self, node: NodeId) -> impl Iterator<Item = Edge<'graph, S>> + 'graph {
self.storage.node_connections(node)
}
}
diff --git a/crates/algorithms/src/shortest_paths/common/mod.rs b/crates/algorithms/src/shortest_paths/common/mod.rs
index ea32da1..979a59f 100644
--- a/crates/algorithms/src/shortest_paths/common/mod.rs
+++ b/crates/algorithms/src/shortest_paths/common/mod.rs
@@ -1,5 +1,5 @@
//! Common utility types, traits and functions for shortest paths algorithms.
-mod closures;
+pub(super) mod closures;
pub(super) mod connections;
pub(super) mod cost;
pub(super) mod path;
diff --git a/crates/algorithms/src/shortest_paths/common/path.rs b/crates/algorithms/src/shortest_paths/common/path.rs
index 25a4192..5874b8f 100644
--- a/crates/algorithms/src/shortest_paths/common/path.rs
+++ b/crates/algorithms/src/shortest_paths/common/path.rs
@@ -102,7 +102,6 @@
impl<S> Display for Path<'_, S>
where
S: GraphStorage,
- S::NodeId: Display,
{
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
Display::fmt(&self.source.id(), f)?;
diff --git a/crates/algorithms/src/shortest_paths/common/queue/double_ended.rs b/crates/algorithms/src/shortest_paths/common/queue/double_ended.rs
index 97783ff..5522118 100644
--- a/crates/algorithms/src/shortest_paths/common/queue/double_ended.rs
+++ b/crates/algorithms/src/shortest_paths/common/queue/double_ended.rs
@@ -1,6 +1,5 @@
use alloc::collections::VecDeque;
use core::{
- hash::Hash,
iter::Sum,
ops::{Add, Div},
};
@@ -8,7 +7,7 @@
use fxhash::FxBuildHasher;
use hashbrown::HashSet;
use numi::{cast::TryCastFrom, num::identity::Zero};
-use petgraph_core::{GraphStorage, Node};
+use petgraph_core::{node::NodeId, GraphStorage, Node};
pub(in crate::shortest_paths) struct DoubleEndedQueueItem<'graph, S, T>
where
@@ -42,13 +41,12 @@
S: GraphStorage,
{
queue: VecDeque<DoubleEndedQueueItem<'graph, S, T>>,
- active: HashSet<S::NodeId, FxBuildHasher>,
+ active: HashSet<NodeId, FxBuildHasher>,
}
impl<'graph, S, T> DoubleEndedQueue<'graph, S, T>
where
S: GraphStorage,
- S::NodeId: Eq + Hash,
{
pub(in crate::shortest_paths) fn new() -> Self {
Self {
@@ -143,7 +141,7 @@
self.queue.len()
}
- pub(in crate::shortest_paths) fn contains_node(&self, node: &S::NodeId) -> bool {
+ pub(in crate::shortest_paths) fn contains_node(&self, node: &NodeId) -> bool {
self.active.contains(node)
}
}
diff --git a/crates/algorithms/src/shortest_paths/common/queue/priority.rs b/crates/algorithms/src/shortest_paths/common/queue/priority.rs
index 24de078..118352e 100644
--- a/crates/algorithms/src/shortest_paths/common/queue/priority.rs
+++ b/crates/algorithms/src/shortest_paths/common/queue/priority.rs
@@ -2,17 +2,20 @@
use core::cmp::Ordering;
use petgraph_core::{
- id::{AssociativeGraphId, BooleanMapper},
- GraphStorage,
+ node::NodeId,
+ storage::{
+ auxiliary::{BooleanGraphStorage, FrequencyHint, Hints, OccupancyHint, PerformanceHint},
+ AuxiliaryGraphStorage,
+ },
};
-pub(in crate::shortest_paths) struct PriorityQueueItem<I, T> {
- pub(in crate::shortest_paths) node: I,
+pub(in crate::shortest_paths) struct PriorityQueueItem<T> {
+ pub(in crate::shortest_paths) node: NodeId,
pub(in crate::shortest_paths) priority: T,
}
-impl<I, T> PartialEq for PriorityQueueItem<I, T>
+impl<T> PartialEq for PriorityQueueItem<T>
where
T: PartialEq,
{
@@ -21,9 +24,9 @@
}
}
-impl<I, T> Eq for PriorityQueueItem<I, T> where T: Eq {}
+impl<T> Eq for PriorityQueueItem<T> where T: Eq {}
-impl<I, T> PartialOrd for PriorityQueueItem<I, T>
+impl<T> PartialOrd for PriorityQueueItem<T>
where
T: PartialOrd,
{
@@ -32,7 +35,7 @@
}
}
-impl<I, T> Ord for PriorityQueueItem<I, T>
+impl<T> Ord for PriorityQueueItem<T>
where
T: Ord,
{
@@ -41,48 +44,52 @@
}
}
-pub(in crate::shortest_paths) struct PriorityQueue<'a, S, T>
+pub(in crate::shortest_paths) struct PriorityQueue<'graph, S, T>
where
- S: GraphStorage + 'a,
- S::NodeId: AssociativeGraphId<S>,
+ S: AuxiliaryGraphStorage + 'graph,
T: Ord,
{
- heap: BinaryHeap<PriorityQueueItem<S::NodeId, T>>,
+ heap: BinaryHeap<PriorityQueueItem<T>>,
pub(crate) check_admissibility: bool,
- flags: <S::NodeId as AssociativeGraphId<S>>::BooleanMapper<'a>,
+ flags: S::BooleanNodeStorage<'graph>,
}
-impl<'a, S, T> PriorityQueue<'a, S, T>
+impl<'graph, S, T> PriorityQueue<'graph, S, T>
where
- S: GraphStorage,
- S::NodeId: AssociativeGraphId<S>,
+ S: AuxiliaryGraphStorage + 'graph,
T: Ord,
{
#[inline]
- pub(in crate::shortest_paths) fn new(storage: &'a S) -> Self {
+ pub(in crate::shortest_paths) fn new(storage: &'graph S) -> Self {
Self {
heap: BinaryHeap::new(),
check_admissibility: true,
- flags: <S::NodeId as AssociativeGraphId<S>>::boolean_mapper(storage),
+ flags: storage.boolean_node_storage(Hints {
+ performance: PerformanceHint {
+ read: FrequencyHint::Frequent,
+ write: FrequencyHint::Infrequent,
+ },
+ occupancy: OccupancyHint::Dense,
+ }),
}
}
- pub(in crate::shortest_paths) fn push(&mut self, node: S::NodeId, priority: T) {
+ pub(in crate::shortest_paths) fn push(&mut self, node: NodeId, priority: T) {
self.heap.push(PriorityQueueItem { node, priority });
}
- pub(in crate::shortest_paths) fn visit(&mut self, id: S::NodeId) {
+ pub(in crate::shortest_paths) fn visit(&mut self, id: NodeId) {
self.flags.set(id, true);
}
#[inline]
- pub(in crate::shortest_paths) fn has_been_visited(&self, id: S::NodeId) -> bool {
- self.flags.index(id)
+ pub(in crate::shortest_paths) fn has_been_visited(&self, id: NodeId) -> bool {
+ self.flags.get(id).unwrap_or(false)
}
#[inline]
- pub(in crate::shortest_paths) fn decrease_priority(&mut self, node: S::NodeId, priority: T) {
+ pub(in crate::shortest_paths) fn decrease_priority(&mut self, node: NodeId, priority: T) {
if self.check_admissibility && self.has_been_visited(node) {
return;
}
@@ -91,7 +98,7 @@
}
#[inline]
- pub(in crate::shortest_paths) fn pop_min(&mut self) -> Option<PriorityQueueItem<S::NodeId, T>> {
+ pub(in crate::shortest_paths) fn pop_min(&mut self) -> Option<PriorityQueueItem<T>> {
loop {
let item = self.heap.pop()?;
diff --git a/crates/algorithms/src/shortest_paths/common/route.rs b/crates/algorithms/src/shortest_paths/common/route.rs
index 23829fc..dfd6abb 100644
--- a/crates/algorithms/src/shortest_paths/common/route.rs
+++ b/crates/algorithms/src/shortest_paths/common/route.rs
@@ -74,7 +74,6 @@
impl<S, T> Display for Route<'_, S, T>
where
S: GraphStorage,
- S::NodeId: Display,
T: Display,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
@@ -229,7 +228,6 @@
impl<S, T> Display for DirectRoute<'_, S, T>
where
S: GraphStorage,
- S::NodeId: Display,
T: Display,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
diff --git a/crates/algorithms/src/shortest_paths/common/tests.rs b/crates/algorithms/src/shortest_paths/common/tests.rs
index 629ff62..02bf986 100644
--- a/crates/algorithms/src/shortest_paths/common/tests.rs
+++ b/crates/algorithms/src/shortest_paths/common/tests.rs
@@ -1,8 +1,5 @@
use alloc::vec::Vec;
-use core::{
- fmt::{Debug, Display},
- hash::Hash,
-};
+use core::fmt::Debug;
use error_stack::Result;
use hashbrown::HashMap;
@@ -39,12 +36,13 @@
}
pub(in crate::shortest_paths) use expected;
+use petgraph_core::node::NodeId;
-pub(in crate::shortest_paths) struct Expect<N, T> {
- pub(in crate::shortest_paths) source: N,
- pub(in crate::shortest_paths) target: N,
+pub(in crate::shortest_paths) struct Expect<T> {
+ pub(in crate::shortest_paths) source: NodeId,
+ pub(in crate::shortest_paths) target: NodeId,
- pub(in crate::shortest_paths) transit: Vec<N>,
+ pub(in crate::shortest_paths) transit: Vec<NodeId>,
pub(in crate::shortest_paths) cost: T,
}
@@ -55,7 +53,7 @@
{
graph: &'a Graph<S>,
algorithm: &'a A,
- expected: &'a [Expect<S::NodeId, T>],
+ expected: &'a [Expect<T>],
}
impl<'a, S, A, T> TestCase<'a, S, A, T>
@@ -65,7 +63,7 @@
pub(crate) const fn new(
graph: &'a Graph<S>,
algorithm: &'a A,
- expected: &'a [Expect<<S as GraphStorage>::NodeId, T>],
+ expected: &'a [Expect<T>],
) -> Self {
Self {
graph,
@@ -78,7 +76,6 @@
impl<'a, S, A, T> TestCase<'a, S, A, T>
where
S: GraphStorage,
- S::NodeId: Eq + Hash + Debug + Display,
A: ShortestPath<S, Cost = T>,
T: PartialEq + Debug,
{
@@ -120,7 +117,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: NodeId) {
self.assert_path_routes(self.algorithm.path_from(self.graph, source));
}
}
@@ -128,7 +125,6 @@
impl<'a, S, A, T> TestCase<'a, S, A, T>
where
S: GraphStorage,
- S::NodeId: Eq + Hash + Debug + Display,
A: ShortestDistance<S, Cost = T>,
T: PartialEq + Debug,
{
@@ -174,7 +170,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: 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 cd7f47e..ebf7147 100644
--- a/crates/algorithms/src/shortest_paths/common/transit.rs
+++ b/crates/algorithms/src/shortest_paths/common/transit.rs
@@ -1,12 +1,10 @@
use alloc::{vec, vec::Vec};
-use core::{
- hash::{BuildHasher, Hash},
- iter,
-};
+use core::{hash::BuildHasher, iter};
use hashbrown::{HashMap, HashSet};
use petgraph_core::{
- id::{AssociativeGraphId, AttributeMapper},
+ node::NodeId,
+ storage::{auxiliary::SecondaryGraphStorage, AuxiliaryGraphStorage},
GraphStorage, Node,
};
@@ -17,26 +15,25 @@
}
pub(in crate::shortest_paths) fn reconstruct_path_to<S>(
- predecessors: &<S::NodeId as AssociativeGraphId<S>>::AttributeMapper<'_, Option<S::NodeId>>,
- target: S::NodeId,
-) -> Vec<S::NodeId>
+ predecessors: &S::SecondaryNodeStorage<'_, Option<NodeId>>,
+ target: NodeId,
+) -> Vec<NodeId>
where
- S: GraphStorage,
- S::NodeId: AssociativeGraphId<S>,
+ S: AuxiliaryGraphStorage,
{
let mut current = target;
let mut path = Vec::new();
loop {
- let &Some(node) = predecessors.index(current) else {
+ let Some(node) = predecessors.get(current).copied().flatten() else {
// this case should in theory _never_ happen, as the next statement
// terminates if the next node is `None` (we're at a source node)
// we do it this way, so that we don't need to push and then pop immediately.
break;
};
- if predecessors.index(node).is_none() {
+ if predecessors.get(node).copied().flatten().is_none() {
// we have reached the source node
break;
}
@@ -54,13 +51,12 @@
///
/// This has been adapted from the [NetworkX implementation](https://github.com/networkx/networkx/blob/f93f0e2a066fc456aa447853af9d00eec1058542/networkx/algorithms/shortest_paths/generic.py#L655)
pub(in crate::shortest_paths) fn reconstruct_paths_between<'a, 'graph, S, H>(
- predecessors: &'a HashMap<S::NodeId, Vec<Node<'graph, S>>, H>,
- source: S::NodeId,
+ predecessors: &'a HashMap<NodeId, Vec<Node<'graph, S>>, H>,
+ source: NodeId,
target: Node<'graph, S>,
) -> impl Iterator<Item = Vec<Node<'graph, S>>> + 'a
where
S: GraphStorage,
- S::NodeId: Eq + Hash,
H: BuildHasher,
{
let mut seen = HashSet::new();
diff --git a/crates/algorithms/src/shortest_paths/dijkstra/iter.rs b/crates/algorithms/src/shortest_paths/dijkstra/iter.rs
index f2972f3..84cf81e 100644
--- a/crates/algorithms/src/shortest_paths/dijkstra/iter.rs
+++ b/crates/algorithms/src/shortest_paths/dijkstra/iter.rs
@@ -4,7 +4,11 @@
use error_stack::{Report, Result};
use numi::num::{identity::Zero, ops::AddRef};
use petgraph_core::{
- id::{AssociativeGraphId, AttributeMapper},
+ node::NodeId,
+ storage::{
+ auxiliary::{FrequencyHint, Hints, OccupancyHint, PerformanceHint, SecondaryGraphStorage},
+ AuxiliaryGraphStorage,
+ },
Graph, GraphStorage, Node,
};
@@ -23,7 +27,6 @@
pub(super) struct DijkstraIter<'graph: 'parent, 'parent, S, E, G>
where
S: GraphStorage,
- S::NodeId: AssociativeGraphId<S>,
E: GraphCost<S>,
E::Value: DijkstraMeasure,
{
@@ -38,18 +41,17 @@
num_nodes: usize,
init: bool,
- next: Option<PriorityQueueItem<S::NodeId, E::Value>>,
+ next: Option<PriorityQueueItem<E::Value>>,
predecessor_mode: PredecessorMode,
- distances: <S::NodeId as AssociativeGraphId<S>>::AttributeMapper<'graph, E::Value>,
- predecessors: <S::NodeId as AssociativeGraphId<S>>::AttributeMapper<'graph, Option<S::NodeId>>,
+ distances: S::SecondaryNodeStorage<'graph, E::Value>,
+ predecessors: S::SecondaryNodeStorage<'graph, Option<NodeId>>,
}
impl<'graph: 'parent, 'parent, S, E, G> DijkstraIter<'graph, 'parent, S, E, G>
where
S: GraphStorage,
- S::NodeId: AssociativeGraphId<S>,
E: GraphCost<S>,
E::Value: DijkstraMeasure,
G: Connections<'graph, S>,
@@ -60,7 +62,7 @@
edge_cost: &'parent E,
connections: G,
- source: S::NodeId,
+ source: NodeId,
predecessor_mode: PredecessorMode,
) -> Result<Self, DijkstraError> {
@@ -70,11 +72,22 @@
let queue = PriorityQueue::new(graph.storage());
- let mut distances = <S::NodeId as AssociativeGraphId<S>>::attribute_mapper(graph.storage());
+ let mut distances = graph.storage().secondary_node_storage(Hints {
+ performance: PerformanceHint {
+ read: FrequencyHint::Frequent,
+ write: FrequencyHint::Frequent,
+ },
+ occupancy: OccupancyHint::Dense,
+ });
distances.set(source, E::Value::zero());
- let mut predecessors =
- <S::NodeId as AssociativeGraphId<S>>::attribute_mapper(graph.storage());
+ let mut predecessors = graph.storage().secondary_node_storage(Hints {
+ performance: PerformanceHint {
+ read: FrequencyHint::Infrequent,
+ write: FrequencyHint::Frequent,
+ },
+ occupancy: OccupancyHint::Dense,
+ });
if predecessor_mode == PredecessorMode::Record {
predecessors.set(source, None);
}
@@ -98,7 +111,6 @@
impl<'graph: 'parent, 'parent, S, E, G> Iterator for DijkstraIter<'graph, 'parent, S, E, G>
where
S: GraphStorage,
- S::NodeId: AssociativeGraphId<S>,
E: GraphCost<S>,
E::Value: DijkstraMeasure,
G: Connections<'graph, S>,
@@ -158,9 +170,7 @@
self.predecessors.set(target, Some(node));
}
- // if let Some(target) = self.graph.node(target) {
self.queue.decrease_priority(target, alternative);
- // }
}
// this is what makes this special: instead of getting the next node as the start of next
diff --git a/crates/algorithms/src/shortest_paths/dijkstra/mod.rs b/crates/algorithms/src/shortest_paths/dijkstra/mod.rs
index 9dec7ba..adc3e0c 100644
--- a/crates/algorithms/src/shortest_paths/dijkstra/mod.rs
+++ b/crates/algorithms/src/shortest_paths/dijkstra/mod.rs
@@ -11,8 +11,9 @@
use error_stack::Result;
use petgraph_core::{
edge::marker::{Directed, Undirected},
- id::AssociativeGraphId,
- DirectedGraphStorage, Graph, GraphDirectionality, GraphStorage, Node,
+ node::NodeId,
+ storage::AuxiliaryGraphStorage,
+ DirectedGraphStorage, Graph, GraphDirectionality, GraphStorage,
};
use self::iter::DijkstraIter;
@@ -65,15 +66,15 @@
/// let algorithm = Dijkstra::directed();
///
/// 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(2, &a, &b);
+ /// graph.insert_edge(2, a, b);
///
- /// let path = algorithm.path_between(&graph, &a, &b);
+ /// let path = algorithm.path_between(&graph, a, b);
/// assert!(path.is_some());
///
- /// let path = algorithm.path_between(&graph, &b, &a);
+ /// let path = algorithm.path_between(&graph, b, a);
/// assert!(path.is_none());
/// ```
#[must_use]
@@ -100,15 +101,15 @@
/// let algorithm = Dijkstra::undirected();
///
/// 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(2, &a, &b);
+ /// graph.insert_edge(2, a, b);
///
- /// let path = algorithm.path_between(&graph, &a, &b);
+ /// let path = algorithm.path_between(&graph, a, b);
/// assert!(path.is_some());
///
- /// let path = algorithm.path_between(&graph, &b, &a);
+ /// let path = algorithm.path_between(&graph, b, a);
/// assert!(path.is_some());
/// ```
#[must_use]
@@ -151,12 +152,12 @@
/// let algorithm = Dijkstra::directed().with_edge_cost(edge_cost);
///
/// 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("AB", &a, &b);
+ /// graph.insert_edge("AB", a, b);
///
- /// let path = algorithm.path_between(&graph, &a, &b);
+ /// let path = algorithm.path_between(&graph, a, b);
/// assert!(path.is_some());
/// ```
pub fn with_edge_cost<S, F>(self, edge_cost: F) -> Dijkstra<D, F>
@@ -174,7 +175,6 @@
impl<S, E> ShortestPath<S> for Dijkstra<Undirected, E>
where
S: GraphStorage,
- S::NodeId: AssociativeGraphId<S>,
E: GraphCost<S>,
E::Value: DijkstraMeasure,
{
@@ -184,7 +184,7 @@
fn path_to<'graph: 'this, 'this>(
&'this self,
graph: &'graph Graph<S>,
- target: S::NodeId,
+ target: NodeId,
) -> Result<impl Iterator<Item = Route<'graph, S, Self::Cost>>, Self::Error> {
let iter = self.path_from(graph, target)?;
@@ -194,7 +194,7 @@
fn path_from<'graph: 'this, 'this>(
&'this self,
graph: &'graph Graph<S>,
- source: S::NodeId,
+ source: NodeId,
) -> Result<impl Iterator<Item = Route<'graph, S, Self::Cost>> + 'this, Self::Error> {
DijkstraIter::new(
graph,
@@ -221,7 +221,6 @@
impl<S, E> ShortestPath<S> for Dijkstra<Directed, E>
where
S: DirectedGraphStorage,
- S::NodeId: AssociativeGraphId<S>,
E: GraphCost<S>,
E::Value: DijkstraMeasure,
{
@@ -231,7 +230,7 @@
fn path_from<'graph: 'this, 'this>(
&'this self,
graph: &'graph Graph<S>,
- source: S::NodeId,
+ source: NodeId,
) -> Result<impl Iterator<Item = Route<'graph, S, Self::Cost>> + 'this, Self::Error> {
DijkstraIter::new(
graph,
@@ -258,7 +257,6 @@
impl<S, E> ShortestDistance<S> for Dijkstra<Undirected, E>
where
S: GraphStorage,
- S::NodeId: AssociativeGraphId<S>,
E: GraphCost<S>,
E::Value: DijkstraMeasure,
{
@@ -268,7 +266,7 @@
fn distance_to<'graph: 'this, 'this>(
&'this self,
graph: &'graph Graph<S>,
- target: S::NodeId,
+ target: NodeId,
) -> Result<impl Iterator<Item = DirectRoute<'graph, S, Self::Cost>>, Self::Error> {
let iter = self.distance_from(graph, target)?;
@@ -278,7 +276,7 @@
fn distance_from<'graph: 'this, 'this>(
&'this self,
graph: &'graph Graph<S>,
- source: S::NodeId,
+ source: NodeId,
) -> Result<impl Iterator<Item = DirectRoute<'graph, S, Self::Cost>> + 'this, Self::Error> {
let iter = DijkstraIter::new(
graph,
@@ -307,7 +305,6 @@
impl<S, E> ShortestDistance<S> for Dijkstra<Directed, E>
where
S: DirectedGraphStorage,
- S::NodeId: AssociativeGraphId<S>,
E: GraphCost<S>,
E::Value: DijkstraMeasure,
{
@@ -317,7 +314,7 @@
fn distance_from<'graph: 'this, 'this>(
&'this self,
graph: &'graph Graph<S>,
- source: S::NodeId,
+ source: NodeId,
) -> Result<impl Iterator<Item = DirectRoute<'graph, S, Self::Cost>>, Self::Error> {
let iter = DijkstraIter::new(
graph,
diff --git a/crates/algorithms/src/shortest_paths/dijkstra/tests.rs b/crates/algorithms/src/shortest_paths/dijkstra/tests.rs
index c6ee7fa..905f7ea 100644
--- a/crates/algorithms/src/shortest_paths/dijkstra/tests.rs
+++ b/crates/algorithms/src/shortest_paths/dijkstra/tests.rs
@@ -2,7 +2,7 @@
use numi::borrow::Moo;
use petgraph_core::{Edge, GraphStorage};
-use petgraph_dino::{DiDinoGraph, EdgeId, NodeId};
+use petgraph_dino::DiDinoGraph;
use petgraph_utils::{graph, GraphCollection};
use crate::shortest_paths::{
@@ -22,7 +22,7 @@
c: "C",
d: "D",
e: "E",
- ] as NodeId, [
+ ], [
ab: a -> b: 10,
ac: a -> c: 5,
bd: b -> d: 1,
@@ -33,12 +33,10 @@
ce: c -> e: 2,
ea: e -> a: 7,
ed: e -> d: 6,
- ] as EdgeId
+ ]
);
-fn networkx_directed_expect_from(
- nodes: &networkx::NodeCollection<NodeId>,
-) -> Vec<Expect<NodeId, i32>> {
+fn networkx_directed_expect_from(nodes: &networkx::NodeCollection) -> Vec<Expect<i32>> {
expected!(nodes; [
a -()> a: 0,
a -(c)> b: 8,
@@ -58,7 +56,7 @@
d: "D",
e: "E",
f: "F",
- ] as NodeId, [
+ ], [
ab: a -> b: "apple",
bc: b -> c: "cat",
cd: c -> d: "giraffe",
@@ -66,7 +64,7 @@
ef: e -> f: "banana",
fa: f -> a: "bear",
ad: a -> d: "elephant",
- ] as EdgeId
+ ]
);
// TODO: multigraph
@@ -91,9 +89,7 @@
TestCase::new(&graph, &dijkstra, &expected).assert_distance_from(nodes.a);
}
-fn random_directed_expect_from(
- nodes: &random::NodeCollection<NodeId>,
-) -> Vec<Expect<NodeId, usize>> {
+fn random_directed_expect_from(nodes: &random::NodeCollection) -> Vec<Expect<usize>> {
expected!(nodes; [
a -()> a: 0,
a -()> b: 5,
@@ -132,9 +128,7 @@
TestCase::new(&graph, &dijkstra, &expected).assert_distance_from(nodes.a);
}
-fn networkx_undirected_expect_from(
- nodes: &networkx::NodeCollection<NodeId>,
-) -> Vec<Expect<NodeId, i32>> {
+fn networkx_undirected_expect_from(nodes: &networkx::NodeCollection) -> Vec<Expect<i32>> {
expected!(nodes; [
a -()> a: 0,
a -(c)> b: 7,
@@ -164,9 +158,7 @@
TestCase::new(&graph, &dijkstra, &expected).assert_distance_from(nodes.a);
}
-fn random_undirected_expect_from(
- nodes: &random::NodeCollection<NodeId>,
-) -> Vec<Expect<NodeId, usize>> {
+fn random_undirected_expect_from(nodes: &random::NodeCollection) -> Vec<Expect<usize>> {
expected!(nodes; [
a -()> a: 0,
a -()> b: 5,
diff --git a/crates/algorithms/src/shortest_paths/floyd_warshall/error.rs b/crates/algorithms/src/shortest_paths/floyd_warshall/error.rs
index 534fdce..5b835bb 100644
--- a/crates/algorithms/src/shortest_paths/floyd_warshall/error.rs
+++ b/crates/algorithms/src/shortest_paths/floyd_warshall/error.rs
@@ -1,12 +1,26 @@
+use alloc::vec::Vec;
use core::{
fmt,
fmt::{Debug, Display, Formatter},
};
use error_stack::Context;
+use petgraph_core::node::NodeId;
+/// An error that can occur during the Floyd-Warshall algorithm.
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum FloydWarshallError {
+ /// The graph contains a negative cycle.
+ ///
+ /// The error attaches all nodes where a negative cycle was detected via the [`NegativeCycle`]
+ /// type.
+ ///
+ /// Note that multiple negative cycles may exist in the graph, and each attachment only means
+ /// that it is part of a negative cycle and not which cycle(s) it is part of.
+ ///
+ /// To find all negative cycles, use [`BellmanFord`] instead.
+ ///
+ /// [`BellmanFord`]: crate::shortest_paths::BellmanFord
NegativeCycle,
}
@@ -19,3 +33,18 @@
}
impl Context for FloydWarshallError {}
+
+/// A node that is part of a negative cycle.
+#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct NegativeCycle(NodeId);
+
+impl NegativeCycle {
+ pub(crate) fn new(node: NodeId) -> Self {
+ Self(node)
+ }
+
+ /// Returns the node that is part of a negative cycle.
+ pub fn node(&self) -> NodeId {
+ self.0
+ }
+}
diff --git a/crates/algorithms/src/shortest_paths/floyd_warshall/impl.rs b/crates/algorithms/src/shortest_paths/floyd_warshall/impl.rs
index 72082d3..95a7e1a 100644
--- a/crates/algorithms/src/shortest_paths/floyd_warshall/impl.rs
+++ b/crates/algorithms/src/shortest_paths/floyd_warshall/impl.rs
@@ -5,7 +5,7 @@
borrow::Moo,
num::{checked::CheckedAdd, identity::Zero},
};
-use petgraph_core::{id::LinearGraphId, Graph, GraphStorage, Node};
+use petgraph_core::{node::NodeId, storage::SequentialGraphStorage, Graph, GraphStorage, Node};
use crate::shortest_paths::{
common::{
@@ -13,18 +13,19 @@
route::Route,
transit::PredecessorMode,
},
- floyd_warshall::{error::FloydWarshallError, matrix::SlotMatrix, FloydWarshallMeasure},
+ floyd_warshall::{
+ error::FloydWarshallError, matrix::SlotMatrix, FloydWarshallMeasure, NegativeCycle,
+ },
Path,
};
pub(super) fn init_directed_edge_distance<'graph: 'this, 'this, S, E>(
matrix: &mut SlotMatrix<'graph, S, Moo<'this, E::Value>>,
- u: S::NodeId,
- v: S::NodeId,
+ u: NodeId,
+ v: NodeId,
value: Option<Moo<'this, E::Value>>,
) where
S: GraphStorage,
- S::NodeId: LinearGraphId<S> + Clone,
E: GraphCost<S>,
E::Value: Clone,
{
@@ -33,12 +34,11 @@
pub(super) fn init_undirected_edge_distance<'graph: 'this, 'this, S, E>(
matrix: &mut SlotMatrix<'graph, S, Moo<'this, E::Value>>,
- u: S::NodeId,
- v: S::NodeId,
+ u: NodeId,
+ v: NodeId,
value: Option<Moo<'this, E::Value>>,
) where
S: GraphStorage,
- S::NodeId: LinearGraphId<S> + Clone,
E: GraphCost<S>,
E::Value: Clone,
{
@@ -50,36 +50,33 @@
}
pub(super) fn init_directed_edge_predecessor<S>(
- matrix: &mut SlotMatrix<S, S::NodeId>,
- u: S::NodeId,
- v: S::NodeId,
+ matrix: &mut SlotMatrix<S, NodeId>,
+ u: NodeId,
+ v: NodeId,
) where
S: GraphStorage,
- S::NodeId: LinearGraphId<S> + Clone,
{
matrix.set(u, v, Some(u));
}
pub(super) fn init_undirected_edge_predecessor<S>(
- matrix: &mut SlotMatrix<S, S::NodeId>,
- u: S::NodeId,
- v: S::NodeId,
+ matrix: &mut SlotMatrix<S, NodeId>,
+ u: NodeId,
+ v: NodeId,
) where
S: GraphStorage,
- S::NodeId: LinearGraphId<S> + Clone,
{
matrix.set(u, v, Some(u));
matrix.set(v, u, Some(v));
}
fn reconstruct_path<S>(
- matrix: &SlotMatrix<'_, S, S::NodeId>,
- source: S::NodeId,
- target: S::NodeId,
-) -> Vec<S::NodeId>
+ matrix: &SlotMatrix<'_, S, NodeId>,
+ source: NodeId,
+ target: NodeId,
+) -> Vec<NodeId>
where
S: GraphStorage,
- S::NodeId: LinearGraphId<S> + Clone,
{
let mut path = Vec::new();
@@ -113,21 +110,16 @@
}
type InitEdgeDistanceFn<'graph, 'this, S, E> = fn(
&mut SlotMatrix<'graph, S, Moo<'this, <E as GraphCost<S>>::Value>>,
- <S as GraphStorage>::NodeId,
- <S as GraphStorage>::NodeId,
+ NodeId,
+ NodeId,
Option<Moo<'this, <E as GraphCost<S>>::Value>>,
);
-type InitEdgePredecessorFn<'graph, S> = fn(
- &mut SlotMatrix<'graph, S, <S as GraphStorage>::NodeId>,
- <S as GraphStorage>::NodeId,
- <S as GraphStorage>::NodeId,
-);
+type InitEdgePredecessorFn<'graph, S> = fn(&mut SlotMatrix<'graph, S, NodeId>, NodeId, NodeId);
pub(super) struct FloydWarshallImpl<'graph: 'parent, 'parent, S, E>
where
S: GraphStorage,
- S::NodeId: LinearGraphId<S>,
E: GraphCost<S>,
{
graph: &'graph Graph<S>,
@@ -139,14 +131,12 @@
init_edge_predecessor: InitEdgePredecessorFn<'graph, S>,
distances: SlotMatrix<'graph, S, Moo<'parent, E::Value>>,
- predecessors: SlotMatrix<'graph, S, S::NodeId>,
+ predecessors: SlotMatrix<'graph, S, NodeId>,
}
-// TODO: relax `NodeId` requirements or make `Send + Sync + 'static` across the board
impl<'graph: 'parent, 'parent, S, E> FloydWarshallImpl<'graph, 'parent, S, E>
where
S: GraphStorage,
- S::NodeId: LinearGraphId<S> + Clone + Send + Sync + 'static,
E: GraphCost<S>,
E::Value: FloydWarshallMeasure,
{
@@ -268,9 +258,11 @@
continue;
};
+ let cycle = NegativeCycle::new(node);
+
result = match result {
- Ok(()) => Err(Report::new(FloydWarshallError::NegativeCycle).attach(node)),
- Err(report) => Err(report.attach(node)),
+ Ok(()) => Err(Report::new(FloydWarshallError::NegativeCycle).attach(cycle)),
+ Err(report) => Err(report.attach(cycle)),
};
}
@@ -316,11 +308,7 @@
})
}
- pub(super) fn pick(
- self,
- source: S::NodeId,
- target: S::NodeId,
- ) -> Option<Route<'graph, S, E::Value>> {
+ pub(super) fn pick(self, source: NodeId, target: NodeId) -> Option<Route<'graph, S, E::Value>> {
let Self {
graph,
distances,
diff --git a/crates/algorithms/src/shortest_paths/floyd_warshall/matrix.rs b/crates/algorithms/src/shortest_paths/floyd_warshall/matrix.rs
index 5915c2e..8a424fd 100644
--- a/crates/algorithms/src/shortest_paths/floyd_warshall/matrix.rs
+++ b/crates/algorithms/src/shortest_paths/floyd_warshall/matrix.rs
@@ -1,8 +1,8 @@
use alloc::vec::Vec;
-use numi::borrow::Moo;
use petgraph_core::{
- id::{IndexMapper, LinearGraphId},
+ node::NodeId,
+ storage::{sequential::GraphIdBijection, SequentialGraphStorage},
Graph, GraphStorage,
};
@@ -11,9 +11,9 @@
Discard,
}
-impl<I, T> IndexMapper<T> for MatrixIndexMapper<I>
+impl<I, T> GraphIdBijection<T> for MatrixIndexMapper<I>
where
- I: IndexMapper<T>,
+ I: GraphIdBijection<T>,
T: PartialEq,
{
fn max(&self) -> usize {
@@ -41,9 +41,8 @@
pub(super) struct SlotMatrix<'graph, S, T>
where
S: GraphStorage + 'graph,
- S::NodeId: LinearGraphId<S>,
{
- mapper: MatrixIndexMapper<<S::NodeId as LinearGraphId<S>>::Mapper<'graph>>,
+ mapper: MatrixIndexMapper<S::NodeIdBijection<'graph>>,
matrix: Vec<Option<T>>,
length: usize,
}
@@ -51,13 +50,10 @@
impl<'graph, S, T> SlotMatrix<'graph, S, T>
where
S: GraphStorage,
- S::NodeId: LinearGraphId<S>,
{
pub(crate) fn new(graph: &'graph Graph<S>) -> Self {
let length = graph.num_nodes();
- let mapper = MatrixIndexMapper::Store(<S::NodeId as LinearGraphId<S>>::index_mapper(
- graph.storage(),
- ));
+ let mapper = MatrixIndexMapper::Store(graph.storage().node_id_bijection());
let mut matrix = Vec::with_capacity(length * length);
matrix.resize_with(length * length, Default::default);
@@ -81,7 +77,7 @@
}
}
- pub(crate) fn set(&mut self, source: S::NodeId, target: S::NodeId, value: Option<T>) {
+ pub(crate) fn set(&mut self, source: NodeId, target: NodeId, value: Option<T>) {
if matches!(self.mapper, MatrixIndexMapper::Discard) {
// this should never happen, even if it does, we don't want to panic here (map call)
// so we simply return.
@@ -104,16 +100,16 @@
/// Returns `None` if the node cannot be looked up, this only happens if you try to query for a
/// value on an index that has not yet been set via `set`.
///
- /// See the contract described on the [`IndexMapper`] for more information about the
+ /// See the contract described on the [`GraphIdBijection`] for more information about the
/// `map/lookup` contract.
- pub(crate) fn get(&self, source: S::NodeId, target: S::NodeId) -> Option<&T> {
+ pub(crate) fn get(&self, source: NodeId, target: NodeId) -> Option<&T> {
let source = self.mapper.get(source)?;
let target = self.mapper.get(target)?;
self.matrix[source * self.length + target].as_ref()
}
- pub(crate) fn resolve(&self, index: usize) -> Option<S::NodeId> {
+ pub(crate) fn resolve(&self, index: usize) -> Option<NodeId> {
self.mapper.reverse(index)
}
}
@@ -125,7 +121,6 @@
impl<'a, S, T> SlotMatrix<'a, S, T>
where
S: GraphStorage,
- S::NodeId: LinearGraphId<S> + Clone,
{
pub(crate) fn diagonal(&self) -> impl Iterator<Item = Option<&T>> + Captures<'a> + '_ {
let len = self.length;
diff --git a/crates/algorithms/src/shortest_paths/floyd_warshall/mod.rs b/crates/algorithms/src/shortest_paths/floyd_warshall/mod.rs
index 2049e2b..5ece800 100644
--- a/crates/algorithms/src/shortest_paths/floyd_warshall/mod.rs
+++ b/crates/algorithms/src/shortest_paths/floyd_warshall/mod.rs
@@ -11,15 +11,19 @@
use error_stack::Result;
use petgraph_core::{
edge::marker::{Directed, Undirected},
- id::LinearGraphId,
- Graph, GraphStorage,
+ node::NodeId,
+ storage::SequentialGraphStorage,
+ DirectedGraphStorage, Graph, GraphStorage,
};
use self::r#impl::{
init_directed_edge_distance, init_directed_edge_predecessor, init_undirected_edge_distance,
init_undirected_edge_predecessor, FloydWarshallImpl,
};
-pub use self::{error::FloydWarshallError, measure::FloydWarshallMeasure};
+pub use self::{
+ error::{FloydWarshallError, NegativeCycle},
+ measure::FloydWarshallMeasure,
+};
use super::{
common::{
cost::{DefaultCost, GraphCost},
@@ -70,15 +74,15 @@
/// let algorithm = FloydWarshall::directed();
///
/// 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);
+ /// let path = algorithm.path_between(&graph, a, b);
/// assert!(path.is_some());
///
- /// let path = algorithm.path_between(&graph, &b, &a);
+ /// let path = algorithm.path_between(&graph, b, a);
/// assert!(path.is_none());
/// ```
#[must_use]
@@ -104,15 +108,15 @@
/// let algorithm = FloydWarshall::undirected();
///
/// 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);
+ /// let path = algorithm.path_between(&graph, a, b);
/// assert!(path.is_some());
///
- /// let path = algorithm.path_between(&graph, &b, &a);
+ /// let path = algorithm.path_between(&graph, b, a);
/// assert!(path.is_some());
/// ```
#[must_use]
@@ -150,15 +154,15 @@
/// let algorithm = FloydWarshall::directed().with_edge_cost(edge_cost);
///
/// 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("AB", &a, &b);
+ /// graph.insert_edge("AB", a, b);
///
- /// let path = algorithm.path_between(&graph, &a, &b);
+ /// let path = algorithm.path_between(&graph, a, b);
/// assert!(path.is_some());
///
- /// let path = algorithm.path_between(&graph, &b, &a);
+ /// let path = algorithm.path_between(&graph, b, a);
/// assert!(path.is_none());
/// ```
pub fn with_edge_cost<S, F>(self, edge_cost: F) -> FloydWarshall<D, F>
@@ -176,7 +180,6 @@
impl<S, E> ShortestPath<S> for FloydWarshall<Undirected, E>
where
S: GraphStorage,
- S::NodeId: LinearGraphId<S> + Clone + Send + Sync + 'static,
E: GraphCost<S>,
E::Value: FloydWarshallMeasure,
{
@@ -186,7 +189,7 @@
fn path_to<'graph: 'this, 'this>(
&'this self,
graph: &'graph Graph<S>,
- target: S::NodeId,
+ target: NodeId,
) -> Result<impl Iterator<Item = Route<'graph, S, Self::Cost>> + 'this, Self::Error> {
FloydWarshallImpl::new(
graph,
@@ -201,7 +204,7 @@
fn path_from<'graph: 'this, 'this>(
&'this self,
graph: &'graph Graph<S>,
- source: S::NodeId,
+ source: NodeId,
) -> Result<impl Iterator<Item = Route<'graph, S, Self::Cost>> + 'this, Self::Error> {
FloydWarshallImpl::new(
graph,
@@ -216,8 +219,8 @@
fn path_between<'graph>(
&self,
graph: &'graph Graph<S>,
- source: S::NodeId,
- target: S::NodeId,
+ source: NodeId,
+ target: NodeId,
) -> Option<Route<'graph, S, Self::Cost>> {
let r#impl = FloydWarshallImpl::new(
graph,
@@ -249,7 +252,6 @@
impl<S, E> ShortestDistance<S> for FloydWarshall<Undirected, E>
where
S: GraphStorage,
- S::NodeId: LinearGraphId<S> + Clone + Send + Sync + 'static,
E: GraphCost<S>,
E::Value: FloydWarshallMeasure,
{
@@ -259,7 +261,7 @@
fn distance_to<'graph: 'this, 'this>(
&'this self,
graph: &'graph Graph<S>,
- target: S::NodeId,
+ target: NodeId,
) -> Result<impl Iterator<Item = DirectRoute<'graph, S, Self::Cost>> + 'this, Self::Error> {
let iter = FloydWarshallImpl::new(
graph,
@@ -277,7 +279,7 @@
fn distance_from<'graph: 'this, 'this>(
&'this self,
graph: &'graph Graph<S>,
- source: S::NodeId,
+ source: NodeId,
) -> Result<impl Iterator<Item = DirectRoute<'graph, S, Self::Cost>> + 'this, Self::Error> {
let iter = FloydWarshallImpl::new(
graph,
@@ -295,8 +297,8 @@
fn distance_between(
&self,
graph: &Graph<S>,
- source: S::NodeId,
- target: S::NodeId,
+ source: NodeId,
+ target: NodeId,
) -> Option<Cost<Self::Cost>> {
let iter = FloydWarshallImpl::new(
graph,
@@ -328,8 +330,7 @@
impl<S, E> ShortestPath<S> for FloydWarshall<Directed, E>
where
- S: GraphStorage,
- S::NodeId: LinearGraphId<S> + Clone + Send + Sync + 'static,
+ S: DirectedGraphStorage,
E: GraphCost<S>,
E::Value: FloydWarshallMeasure,
{
@@ -339,7 +340,7 @@
fn path_to<'graph: 'this, 'this>(
&'this self,
graph: &'graph Graph<S>,
- target: S::NodeId,
+ target: NodeId,
) -> Result<impl Iterator<Item = Route<'graph, S, Self::Cost>> + 'this, Self::Error> {
FloydWarshallImpl::new(
graph,
@@ -354,7 +355,7 @@
fn path_from<'graph: 'this, 'this>(
&'this self,
graph: &'graph Graph<S>,
- source: S::NodeId,
+ source: NodeId,
) -> Result<impl Iterator<Item = Route<'graph, S, Self::Cost>> + 'this, Self::Error> {
FloydWarshallImpl::new(
graph,
@@ -370,8 +371,8 @@
fn path_between<'graph>(
&self,
graph: &'graph Graph<S>,
- source: S::NodeId,
- target: S::NodeId,
+ source: NodeId,
+ target: NodeId,
) -> Option<Route<'graph, S, Self::Cost>> {
let r#impl = FloydWarshallImpl::new(
graph,
@@ -402,8 +403,7 @@
impl<S, E> ShortestDistance<S> for FloydWarshall<Directed, E>
where
- S: GraphStorage,
- S::NodeId: LinearGraphId<S> + Clone + Send + Sync + 'static,
+ S: DirectedGraphStorage,
E: GraphCost<S>,
E::Value: FloydWarshallMeasure,
{
@@ -413,7 +413,7 @@
fn distance_to<'graph: 'this, 'this>(
&'this self,
graph: &'graph Graph<S>,
- target: S::NodeId,
+ target: NodeId,
) -> Result<impl Iterator<Item = DirectRoute<'graph, S, Self::Cost>> + 'this, Self::Error> {
let iter = FloydWarshallImpl::new(
graph,
@@ -431,7 +431,7 @@
fn distance_from<'graph: 'this, 'this>(
&'this self,
graph: &'graph Graph<S>,
- source: S::NodeId,
+ source: NodeId,
) -> Result<impl Iterator<Item = DirectRoute<'graph, S, Self::Cost>> + 'this, Self::Error> {
let iter = FloydWarshallImpl::new(
graph,
@@ -449,8 +449,8 @@
fn distance_between(
&self,
graph: &Graph<S>,
- source: S::NodeId,
- target: S::NodeId,
+ source: NodeId,
+ target: NodeId,
) -> Option<Cost<Self::Cost>> {
let iter = FloydWarshallImpl::new(
graph,
diff --git a/crates/algorithms/src/shortest_paths/floyd_warshall/tests.rs b/crates/algorithms/src/shortest_paths/floyd_warshall/tests.rs
index 1bd28b0..b91d4fb 100644
--- a/crates/algorithms/src/shortest_paths/floyd_warshall/tests.rs
+++ b/crates/algorithms/src/shortest_paths/floyd_warshall/tests.rs
@@ -1,12 +1,13 @@
use alloc::{vec, vec::Vec};
use hashbrown::HashSet;
-use petgraph_dino::{DiDinoGraph, EdgeId, NodeId};
+use petgraph_core::node::NodeId;
+use petgraph_dino::DiDinoGraph;
use petgraph_utils::{graph, GraphCollection};
use crate::shortest_paths::{
common::tests::{expected, Expect, TestCase},
- floyd_warshall::{error::FloydWarshallError, FloydWarshall},
+ floyd_warshall::{error::FloydWarshallError, FloydWarshall, NegativeCycle},
ShortestPath,
};
@@ -28,7 +29,7 @@
f: "F",
g: "G",
h: "H",
- ] as NodeId,
+ ],
[
ab: a -> b: 1,
bc: b -> c: 1,
@@ -39,10 +40,10 @@
fg: f -> g: 1,
gh: g -> h: 1,
he: h -> e: 1,
- ] as EdgeId
+ ]
);
-fn uniform_expect(nodes: &uniform::NodeCollection<NodeId>) -> Vec<Expect<NodeId, usize>> {
+fn uniform_expect(nodes: &uniform::NodeCollection) -> Vec<Expect<usize>> {
expected!(nodes; [
a -()> a: 0,
a -()> b: 1,
@@ -136,7 +137,7 @@
b: "B",
c: "C",
d: "D",
- ] as NodeId,
+ ],
[
ab: a -> b: 1,
ac: a -> c: 4,
@@ -144,12 +145,10 @@
bc: b -> c: 2,
bd: b -> d: 2,
cd: c -> d: 2,
- ] as EdgeId
+ ]
);
-fn directed_weighted_expect(
- nodes: &weighted::NodeCollection<NodeId>,
-) -> Vec<Expect<NodeId, usize>> {
+fn directed_weighted_expect(nodes: &weighted::NodeCollection) -> Vec<Expect<usize>> {
expected!(nodes; [
a -()> a: 0,
a -()> b: 1,
@@ -242,9 +241,7 @@
TestCase::new(&graph, &floyd_warshall, &expected).assert_every_distance();
}
-fn undirected_weighted_expect(
- nodes: &weighted::NodeCollection<NodeId>,
-) -> Vec<Expect<NodeId, usize>> {
+fn undirected_weighted_expect(nodes: &weighted::NodeCollection) -> Vec<Expect<usize>> {
expected!(nodes; [
a -()> a: 0,
a -()> b: 1,
@@ -348,12 +345,12 @@
a: "A",
b: "B",
c: "C",
- ] as NodeId,
+ ],
[
ab: a -> b: 1,
bc: b -> c: -3,
ca: c -> a: 1,
- ] as EdgeId
+ ]
);
#[test]
@@ -367,7 +364,10 @@
};
assert_eq!(error.current_context(), &FloydWarshallError::NegativeCycle);
- let participants: HashSet<_> = error.request_ref::<NodeId>().copied().collect();
+ let participants: HashSet<_> = error
+ .request_ref::<NegativeCycle>()
+ .map(|cycle| cycle.node())
+ .collect();
assert_eq!(
participants,
diff --git a/crates/algorithms/src/shortest_paths/mod.rs b/crates/algorithms/src/shortest_paths/mod.rs
index c199eaf..9137dce 100644
--- a/crates/algorithms/src/shortest_paths/mod.rs
+++ b/crates/algorithms/src/shortest_paths/mod.rs
@@ -38,15 +38,15 @@
//! use petgraph_dino::DiDinoGraph;
//!
//! let mut graph = DiDinoGraph::new();
-//! let a = *graph.insert_node("A").id();
-//! let b = *graph.insert_node("B").id();
-//! graph.insert_edge(7, &a, &b);
+//! let a = graph.insert_node("A").id();
+//! let b = graph.insert_node("B").id();
+//! graph.insert_edge(7, a, b);
//!
//! let dijkstra = Dijkstra::directed();
-//! let path = dijkstra.path_between(&graph, &a, &b);
+//! let path = dijkstra.path_between(&graph, a, b);
//! assert!(path.is_some());
//!
-//! let path = dijkstra.path_between(&graph, &b, &a);
+//! let path = dijkstra.path_between(&graph, b, a);
//! assert!(path.is_none());
//! ```
@@ -57,7 +57,7 @@
pub mod floyd_warshall;
use error_stack::{Context, Result};
-use petgraph_core::{Graph, GraphStorage};
+use petgraph_core::{node::NodeId, Graph, GraphStorage};
pub use self::{
astar::AStar,
@@ -71,6 +71,18 @@
floyd_warshall::FloydWarshall,
};
+pub mod utilities {
+ //! # Utilities
+ //!
+ //! This module contains utilities that can be used with shortest path algorithms.
+ //!
+ //! Currently this module only contains the [`cost`] and [`heuristic`] functions, which resolve
+ //! common lifetime problems whenever closures are used for the cost and heuristic functions.
+
+ pub use super::common::closures::{cost, heuristic};
+}
+
+// TODO: should algorithms take `Node<T>` instead?!
/// # Shortest Path
///
/// A shortest path algorithm is an algorithm that finds a path between two nodes in a graph such
@@ -93,21 +105,21 @@
/// use petgraph_dino::DiDinoGraph;
///
/// 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 a = graph.insert_node("A").id();
+/// let b = graph.insert_node("B").id();
+/// let c = graph.insert_node("C").id();
///
-/// graph.insert_edge(7, &a, &b);
-/// graph.insert_edge(5, &b, &c);
-/// graph.insert_edge(3, &a, &c);
+/// graph.insert_edge(7, a, b);
+/// graph.insert_edge(5, b, c);
+/// graph.insert_edge(3, a, c);
///
/// let dijkstra = Dijkstra::directed();
-/// let path = dijkstra.path_between(&graph, &a, &c).expect("path exists");
+/// let path = dijkstra.path_between(&graph, a, c).expect("path exists");
///
/// assert_eq!(path.cost().into_value(), 3);
///
-/// assert_eq!(path.path().source().id(), &a);
-/// assert_eq!(path.path().target().id(), &c);
+/// assert_eq!(path.path().source().id(), a);
+/// assert_eq!(path.path().target().id(), c);
///
/// assert!(path.path().transit().is_empty());
/// ```
@@ -128,7 +140,7 @@
fn path_to<'graph: 'this, 'this>(
&'this self,
graph: &'graph Graph<S>,
- target: S::NodeId,
+ target: NodeId,
) -> Result<impl Iterator<Item = Route<'graph, S, Self::Cost>> + 'this, Self::Error> {
let iter = self.every_path(graph)?;
@@ -143,7 +155,7 @@
fn path_from<'graph: 'this, 'this>(
&'this self,
graph: &'graph Graph<S>,
- source: S::NodeId,
+ source: NodeId,
) -> Result<impl Iterator<Item = Route<'graph, S, Self::Cost>> + 'this, Self::Error> {
let iter = self.every_path(graph)?;
@@ -156,8 +168,8 @@
fn path_between<'graph>(
&self,
graph: &'graph Graph<S>,
- source: S::NodeId,
- target: S::NodeId,
+ source: NodeId,
+ target: NodeId,
) -> Option<Route<'graph, S, Self::Cost>> {
self.path_from(graph, source)
.ok()?
@@ -207,7 +219,7 @@
fn distance_to<'graph: 'this, 'this>(
&'this self,
graph: &'graph Graph<S>,
- target: S::NodeId,
+ target: NodeId,
) -> Result<impl Iterator<Item = DirectRoute<'graph, S, Self::Cost>> + 'this, Self::Error> {
let iter = self.every_distance(graph)?;
@@ -223,7 +235,7 @@
fn distance_from<'graph: 'this, 'this>(
&'this self,
graph: &'graph Graph<S>,
- source: S::NodeId,
+ source: NodeId,
) -> Result<impl Iterator<Item = DirectRoute<'graph, S, Self::Cost>> + 'this, Self::Error> {
let iter = self.every_distance(graph)?;
@@ -236,8 +248,8 @@
fn distance_between(
&self,
graph: &Graph<S>,
- source: S::NodeId,
- target: S::NodeId,
+ source: NodeId,
+ target: NodeId,
) -> Option<Cost<Self::Cost>> {
self.every_distance(graph)
.ok()?
diff --git a/crates/algorithms/tests/shortest_paths/harness.rs b/crates/algorithms/tests/shortest_paths/harness.rs
index 0930eb2..4956ded 100644
--- a/crates/algorithms/tests/shortest_paths/harness.rs
+++ b/crates/algorithms/tests/shortest_paths/harness.rs
@@ -10,12 +10,9 @@
WalkBuilder,
};
use libtest_mimic::Trial;
-use snapbox::{
- report::{write_diff, Palette},
- Action, Assert, Data, DataFormat, NormalizeNewlines,
-};
+use snapbox::{report::Palette, Action};
-pub struct Harness<S, T> {
+pub(crate) struct Harness<S, T> {
root: std::path::PathBuf,
overrides: Option<Override>,
each: Option<&'static [&'static str]>,
@@ -31,7 +28,7 @@
S: Fn(std::path::PathBuf) -> Case + Send + Sync + 'static,
T: Fn(&Path, &'static str) -> Result<I, E> + Send + Sync + 'static + Clone,
{
- pub fn new(root: impl Into<std::path::PathBuf>, setup: S, test: T) -> Self {
+ pub(crate) fn new(root: impl Into<std::path::PathBuf>, setup: S, test: T) -> Self {
Self {
root: root.into(),
overrides: None,
@@ -47,7 +44,7 @@
/// Path patterns for selecting input files
///
/// This used gitignore syntax
- pub fn select<'p>(mut self, patterns: impl IntoIterator<Item = &'p str>) -> Self {
+ pub(crate) fn select<'p>(mut self, patterns: impl IntoIterator<Item = &'p str>) -> Self {
let mut overrides = OverrideBuilder::new(&self.root);
for line in patterns {
overrides.add(line).unwrap();
@@ -56,20 +53,13 @@
self
}
- pub fn each(mut self, names: &'static [&'static str]) -> Self {
+ pub(crate) fn each(mut self, names: &'static [&'static str]) -> Self {
self.each = Some(names);
self
}
- /// Read the failure action from an environment variable
- pub fn action_env(mut self, var_name: &str) -> Self {
- let action = Action::with_env_var(var_name);
- self.action = action.unwrap_or(self.action);
- self
- }
-
/// Override the failure action
- pub fn action(mut self, action: Action) -> Self {
+ pub(crate) fn action(mut self, action: Action) -> Self {
self.action = action;
self
}
@@ -115,7 +105,7 @@
}
/// Run tests
- pub fn test(self) -> ! {
+ pub(crate) fn test(self) -> ! {
let each = self.each.unwrap_or(&[""]);
let tests = each.iter().flat_map(|name| self.trials(name)).collect();
@@ -364,8 +354,8 @@
}
}
-pub struct Case {
- pub name: String,
- pub fixture: std::path::PathBuf,
- pub expected: std::path::PathBuf,
+pub(crate) struct Case {
+ pub(crate) name: String,
+ pub(crate) fixture: std::path::PathBuf,
+ pub(crate) expected: std::path::PathBuf,
}
diff --git a/crates/algorithms/tests/shortest_paths/main.rs b/crates/algorithms/tests/shortest_paths/main.rs
index e2fbafb..90b4cb8 100644
--- a/crates/algorithms/tests/shortest_paths/main.rs
+++ b/crates/algorithms/tests/shortest_paths/main.rs
@@ -10,7 +10,8 @@
use petgraph_algorithms::shortest_paths::{
BellmanFord, Dijkstra, FloydWarshall, Route, ShortestPath,
};
-use petgraph_dino::{DiDinoGraph, DinoStorage, EdgeId, NodeId};
+use petgraph_core::{edge::EdgeId, node::NodeId};
+use petgraph_dino::{DiDinoGraph, DinoStorage};
use snapbox::{utils::normalize_lines, Action};
use crate::harness::{Case, Harness};
diff --git a/crates/core/src/attributes.rs b/crates/core/src/attributes.rs
deleted file mode 100644
index 07b75c9..0000000
--- a/crates/core/src/attributes.rs
+++ /dev/null
@@ -1,185 +0,0 @@
-//! Attributes for graph elements.
-//!
-//! This module is used for types that are used to describe the attributes of a graph element on
-//! insertion.
-//!
-//! A consumer is not expected to use this module directly, but instead use the [`From`]
-//! implementations for [`Attributes`].
-//!
-//! This module is exposed to allow for people who like to use a more explicit style to do so and
-//! for the [`NoValue`] type, which cannot be created[^1] and is used to signal that an identifier
-//! is managed by the storage implementation.
-//!
-//! [^1]: See source code for [`NoValue`] if you _really really really_ need to construct it.
-//!
-//! [`GraphStorage::next_node_id`]: crate::storage::GraphStorage::next_node_id
-//! [`GraphStorage::next_edge_id`]: crate::storage::GraphStorage::next_edge_id
-use crate::id::ArbitraryGraphId;
-
-/// Marker type for `GraphId` which are managed by the graph.
-///
-/// This type is used to represent an `id` on insertion and deletion that is unused.
-/// You normally do not need to construct this value directly, as [`Graph::insert_node`] and
-/// [`Graph::insert_edge`] use `Attributes`.
-///
-/// # Implementation Details
-///
-/// The existence of this type is under stability guarantee, meaning that it will only be removed or
-/// renamed according to `SemVer`, but the internals, such as layout or size, are not.
-/// This includes the construction method.
-///
-/// [`Graph::insert_node`]: crate::graph::Graph::insert_node
-/// [`Graph::insert_edge`]: crate::graph::Graph::insert_edge
-pub struct NoValue(());
-
-impl NoValue {
- /// Construct a new `NoValue`.
- ///
- /// This is only available for testing purposes.
- #[doc(hidden)]
- #[must_use]
- pub const fn new() -> Self {
- Self(())
- }
-}
-
-/// Attributes for graph elements.
-///
-/// This type is used to represent the attributes of a graph element on insertion.
-///
-/// This type is completely opaque and is only used internally in the [`Graph`] implementation to
-/// allow for transparent insertion using [`From`] implementations for elements that require either
-/// of the types of `id`: [`ManagedGraphId`] or [`ArbitraryGraphId`].
-///
-/// You shouldn't need to construct this type directly, but instead simply use the [`From`]
-/// implementation via `graph.insert_node(<weight>)` or `graph.insert_node((<weight>,))` for a
-/// [`ManagedGraphId`] or `graph.insert_node((<id>, <weight>))` for an [`ArbitraryGraphId`].
-/// This also applies for `insert_edge`.
-///
-/// [`Graph`]: crate::graph::Graph
-/// [`ManagedGraphId`]: crate::id::ManagedGraphId
-pub struct Attributes<I, W> {
- pub(crate) id: I,
- pub(crate) weight: W,
-}
-
-impl<W> Attributes<NoValue, W> {
- /// Construct a new `Attributes` with the given weight.
- ///
- /// This will not set the `id` of the attributes, and can only be used for graphs where the `id`
- /// of the element must be a [`ManagedGraphId`].
- ///
- /// [`ManagedGraphId`]: crate::id::ManagedGraphId
- pub const fn new(weight: W) -> Self {
- Self {
- id: NoValue(()),
- weight,
- }
- }
-}
-
-impl<W> Attributes<NoValue, W> {
- /// Set the `id` of the attributes.
- ///
- /// This will return a new `Attributes` with the given `id`, converting it from attributes that
- /// are only valid for elements that have a [`ManagedGraphId`] as their `id`, to ones that only
- /// have an [`ArbitraryGraphId`].
- ///
- /// [`ManagedGraphId`]: crate::id::ManagedGraphId
- pub fn with_id<I>(self, id: impl Into<I>) -> Attributes<I, W>
- where
- I: ArbitraryGraphId,
- {
- Attributes {
- id: id.into(),
- weight: self.weight,
- }
- }
-}
-
-impl<I, J, W> From<(J, W)> for Attributes<I, W>
-where
- I: From<J> + ArbitraryGraphId,
-{
- fn from(value: (J, W)) -> Self {
- Self {
- id: I::from(value.0),
- weight: value.1,
- }
- }
-}
-
-impl<W> From<(W,)> for Attributes<NoValue, W> {
- fn from((weight,): (W,)) -> Self {
- Self {
- id: NoValue(()),
- weight,
- }
- }
-}
-
-impl<W> From<W> for Attributes<NoValue, W> {
- fn from(weight: W) -> Self {
- Self {
- id: NoValue(()),
- weight,
- }
- }
-}
-
-#[cfg(test)]
-mod tests {
- use core::{fmt::Debug, marker::PhantomData};
-
- use crate::{
- attributes::{Attributes, NoValue},
- ArbitraryGraphId, GraphId, ManagedGraphId,
- };
-
- #[derive(Debug, Copy, Clone, PartialEq)]
- struct Managed(usize);
-
- impl GraphId for Managed {
- type AttributeIndex = NoValue;
- }
-
- impl ManagedGraphId for Managed {}
-
- #[derive(Debug, Copy, Clone, PartialEq)]
- struct Arbitrary<V>(V);
-
- impl<V> GraphId for Arbitrary<V>
- where
- V: Debug + Copy + Clone + PartialEq,
- {
- type AttributeIndex = Self;
- }
-
- impl<V> ArbitraryGraphId for Arbitrary<V> where V: Debug + Copy + Clone + PartialEq {}
-
- impl<T> From<T> for Arbitrary<T> {
- fn from(value: T) -> Self {
- Self(value)
- }
- }
-
- struct Fake<T> {
- _marker: PhantomData<T>,
- }
-
- impl<T> Fake<T>
- where
- T: GraphId,
- {
- fn invoke(attributes: impl Into<Attributes<T::AttributeIndex, usize>>) {
- let _attr = attributes.into();
- }
- }
-
- #[test]
- fn from() {
- Fake::<Managed>::invoke(2usize);
-
- Fake::<Arbitrary<usize>>::invoke((2usize, 2usize));
- }
-}
diff --git a/crates/core/src/edge/compat.rs b/crates/core/src/edge/compat.rs
index 18b9892..4e578c8 100644
--- a/crates/core/src/edge/compat.rs
+++ b/crates/core/src/edge/compat.rs
@@ -1,24 +1,27 @@
//! Compatability implementations for deprecated graph traits.
#![allow(deprecated)]
-use crate::{deprecated::visit::EdgeRef, edge::Edge, storage::GraphStorage};
+use crate::{
+ deprecated::visit::EdgeRef,
+ edge::{Edge, EdgeId},
+ node::NodeId,
+ storage::GraphStorage,
+};
impl<S> EdgeRef for Edge<'_, S>
where
S: GraphStorage,
- S::NodeId: Clone,
- S::EdgeId: Clone,
{
- type EdgeId = S::EdgeId;
- type NodeId = S::NodeId;
+ type EdgeId = EdgeId;
+ type NodeId = NodeId;
type Weight = S::EdgeWeight;
fn source(&self) -> Self::NodeId {
- self.u.clone()
+ self.u
}
fn target(&self) -> Self::NodeId {
- self.v.clone()
+ self.v
}
fn weight(&self) -> &Self::Weight {
@@ -26,6 +29,6 @@
}
fn id(&self) -> Self::EdgeId {
- self.id.clone()
+ self.id
}
}
diff --git a/crates/core/src/edge/mod.rs b/crates/core/src/edge/mod.rs
index 5ca357b..1415612 100644
--- a/crates/core/src/edge/mod.rs
+++ b/crates/core/src/edge/mod.rs
@@ -21,16 +21,87 @@
mod direction;
pub mod marker;
-use core::fmt::{Debug, Formatter};
+use core::fmt::{Debug, Display, Formatter};
pub use self::{direction::Direction, marker::GraphDirectionality};
-use crate::{node::Node, storage::GraphStorage, DirectedGraphStorage};
+use crate::{
+ node::{Node, NodeId},
+ storage::GraphStorage,
+ DirectedGraphStorage,
+};
-type DetachedStorageEdge<S> = DetachedEdge<
- <S as GraphStorage>::EdgeId,
- <S as GraphStorage>::NodeId,
- <S as GraphStorage>::EdgeWeight,
->;
+type DetachedStorageEdge<S> = DetachedEdge<<S as GraphStorage>::EdgeWeight>;
+
+/// ID of an edge in a graph.
+///
+/// This is guaranteed to be unique within the graph, library authors and library consumers **must**
+/// treat this as an opaque type akin to [`TypeId`].
+///
+/// The layout of the type is semver stable, but not part of the public API.
+///
+/// [`GraphStorage`] implementations may uphold additional invariants on the inner value and
+/// code outside of the [`GraphStorage`] should **never** construct a [`EdgeId`] directly.
+///
+/// Accessing a [`GraphStorage`] implementation with a [`EdgeId`] not returned by an instance itself
+/// is considered undefined behavior.
+///
+/// [`TypeId`]: core::any::TypeId
+#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct EdgeId(usize);
+
+impl Display for EdgeId {
+ // we could also utilize a VTable here instead, that would allow for custom formatting
+ // but that would be an additional pointer added to the type that must be carried around
+ // that's about ~8 bytes on 64-bit systems
+ fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
+ write!(f, "EdgeId({})", self.0)
+ }
+}
+
+// TODO: find a better way to gate these functions
+impl EdgeId {
+ /// Creates a new [`EdgeId`].
+ ///
+ /// # Note
+ ///
+ /// Using this outside of the [`GraphStorage`] implementation is considered undefined behavior.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use petgraph_core::edge::EdgeId;
+ ///
+ /// let id = EdgeId::new(0);
+ /// ```
+ // Hidden so that non-GraphStorage implementors are not tempted to use this.
+ #[doc(hidden)]
+ #[must_use]
+ pub const fn new(id: usize) -> Self {
+ Self(id)
+ }
+
+ /// Returns the inner value of the [`EdgeId`].
+ ///
+ /// # Note
+ ///
+ /// Using this outside of the [`GraphStorage`] implementation is considered undefined behavior.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use petgraph_core::edge::EdgeId;
+ ///
+ /// let id = EdgeId::new(0);
+ ///
+ /// assert_eq!(id.into_inner(), 0);
+ /// ```
+ // Hidden so that non-GraphStorage implementors are not tempted to use this.
+ #[doc(hidden)]
+ #[must_use]
+ pub const fn into_inner(self) -> usize {
+ self.0
+ }
+}
/// Active edge in the graph.
///
@@ -64,10 +135,10 @@
{
storage: &'a S,
- id: S::EdgeId,
+ id: EdgeId,
- u: S::NodeId,
- v: S::NodeId,
+ u: NodeId,
+ v: NodeId,
weight: &'a S::EdgeWeight,
}
@@ -151,11 +222,11 @@
pub fn new(
storage: &'a S,
- id: S::EdgeId,
+ id: EdgeId,
weight: &'a S::EdgeWeight,
- u: S::NodeId,
- v: S::NodeId,
+ u: NodeId,
+ v: NodeId,
) -> Self {
debug_assert!(storage.contains_node(u));
debug_assert!(storage.contains_node(v));
@@ -195,7 +266,7 @@
/// assert_eq!(edge.id(), &aa);
/// ```
#[must_use]
- pub const fn id(&self) -> S::EdgeId {
+ pub const fn id(&self) -> EdgeId {
self.id
}
@@ -230,7 +301,7 @@
/// assert!((u, v) == (&a, &b) || (u, v) == (&b, &a));
/// ```
#[must_use]
- pub const fn endpoint_ids(&self) -> (S::NodeId, S::NodeId) {
+ pub const fn endpoint_ids(&self) -> (NodeId, NodeId) {
(self.u, self.v)
}
@@ -309,6 +380,33 @@
pub const fn weight(&self) -> &'a S::EdgeWeight {
self.weight
}
+
+ /// Change the underlying storage of this edge.
+ ///
+ /// Should only be used when layering multiple [`GraphStorage`] implementations on top of each
+ /// other.
+ ///
+ /// # Note
+ ///
+ /// This should not lead to any undefined behaviour, but might have unintended consequences if
+ /// the storage does not recognize the inner id as valid.
+ /// You should only use this if you know what you are doing.
+ #[must_use]
+ pub const fn change_storage_unchecked<T>(self, storage: &'a T) -> Edge<'a, T>
+ where
+ T: GraphStorage<EdgeWeight = S::EdgeWeight>,
+ {
+ Edge {
+ storage,
+
+ id: self.id,
+
+ u: self.u,
+ v: self.v,
+
+ weight: self.weight,
+ }
+ }
}
impl<'a, S> Edge<'a, S>
@@ -336,7 +434,7 @@
/// assert_eq!(edge.source_id(), &a);
/// ```
#[must_use]
- pub const fn source_id(&self) -> S::NodeId {
+ pub const fn source_id(&self) -> NodeId {
self.u
}
@@ -401,7 +499,7 @@
/// let edge = graph.edge(&ab).unwrap();
/// assert_eq!(edge.target_id(), &b);
/// ```
- pub const fn target_id(&self) -> S::NodeId {
+ pub const fn target_id(&self) -> NodeId {
self.v
}
@@ -518,12 +616,12 @@
where
S: GraphStorage,
{
- id: S::EdgeId,
+ id: EdgeId,
weight: &'a mut S::EdgeWeight,
- u: S::NodeId,
- v: S::NodeId,
+ u: NodeId,
+ v: NodeId,
}
impl<'a, S> EdgeMut<'a, S>
@@ -547,7 +645,7 @@
///
/// [`Graph::edge_mut`]: crate::graph::Graph::edge_mut
/// [`Graph::insert_edge`]: crate::graph::Graph::insert_edge
- pub fn new(id: S::EdgeId, weight: &'a mut S::EdgeWeight, u: S::NodeId, v: S::NodeId) -> Self {
+ pub fn new(id: EdgeId, weight: &'a mut S::EdgeWeight, u: NodeId, v: NodeId) -> Self {
Self { id, weight, u, v }
}
@@ -572,7 +670,7 @@
/// assert_eq!(edge.id(), &ab);
/// ```
#[must_use]
- pub const fn id(&self) -> S::EdgeId {
+ pub const fn id(&self) -> EdgeId {
self.id
}
@@ -605,7 +703,7 @@
/// assert!((u, v) == (&a, &b) || (u, v) == (&b, &a));
/// ```
#[must_use]
- pub const fn endpoint_ids(&self) -> (S::NodeId, S::NodeId) {
+ pub const fn endpoint_ids(&self) -> (NodeId, NodeId) {
(self.u, self.v)
}
@@ -651,6 +749,31 @@
pub fn weight_mut(&mut self) -> &mut S::EdgeWeight {
self.weight
}
+
+ /// Change the underlying storage of this edge.
+ ///
+ /// Should only be used when layering multiple [`GraphStorage`] implementations on top of each
+ /// other.
+ ///
+ /// # Note
+ ///
+ /// This should not lead to any undefined behaviour, but might have unintended consequences if
+ /// the storage does not recognize the inner id as valid.
+ /// You should only use this if you know what you are doing.
+ #[must_use]
+ pub fn change_storage_unchecked<T>(self) -> EdgeMut<'a, T>
+ where
+ T: GraphStorage<EdgeWeight = S::EdgeWeight>,
+ {
+ EdgeMut {
+ id: self.id,
+
+ weight: self.weight,
+
+ u: self.u,
+ v: self.v,
+ }
+ }
}
impl<'a, S> EdgeMut<'a, S>
@@ -675,7 +798,7 @@
/// assert_eq!(edge.source_id(), &a);
/// ```
#[must_use]
- pub const fn source_id(&self) -> S::NodeId {
+ pub const fn source_id(&self) -> NodeId {
self.u
}
@@ -697,7 +820,7 @@
/// assert_eq!(edge.target_id(), &b);
/// ```
#[must_use]
- pub const fn target_id(&self) -> S::NodeId {
+ pub const fn target_id(&self) -> NodeId {
self.v
}
}
@@ -771,20 +894,20 @@
/// [`Graph::into_parts`]: crate::graph::Graph::into_parts
/// [`Graph::from_parts`]: crate::graph::Graph::from_parts
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub struct DetachedEdge<E, N, W> {
+pub struct DetachedEdge<W> {
/// The unique id of the edge.
- pub id: E,
+ pub id: EdgeId,
/// The `u` endpoint of the `(u, v)` pair of endpoints.
- pub u: N,
+ pub u: NodeId,
/// The `v` endpoint of the `(u, v)` pair of endpoints.
- pub v: N,
+ pub v: NodeId,
/// The weight of the edge.
pub weight: W,
}
-impl<E, N, W> DetachedEdge<E, N, W> {
+impl<W> DetachedEdge<W> {
/// Create a new detached edge.
///
/// In an undirected graph `u` and `v` are interchangeable, but in a directed graph (implements
@@ -797,7 +920,7 @@
///
/// let edge = DetachedEdge::new(0, "A → B", 1, 2);
/// ```
- pub const fn new(id: E, weight: W, u: N, v: N) -> Self {
+ pub const fn new(id: EdgeId, weight: W, u: NodeId, v: NodeId) -> Self {
Self { id, u, v, weight }
}
}
diff --git a/crates/core/src/graph/compat.rs b/crates/core/src/graph/compat.rs
index b5f997e..c0aa4c2 100644
--- a/crates/core/src/graph/compat.rs
+++ b/crates/core/src/graph/compat.rs
@@ -4,33 +4,32 @@
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
-use bitvec::{boxed::BitBox, vec::BitVec};
-
#[cfg(feature = "alloc")]
use crate::deprecated::data::{Build, Create, DataMap, FromElements};
#[cfg(feature = "fixedbitset")]
use crate::deprecated::visit::GetAdjacencyMatrix;
use crate::{
deprecated::visit::{
- Data, EdgeCount, EdgeIndexable, FilterEdge, FilterNode, GraphBase, IntoEdgeReferences,
- IntoEdges, IntoEdgesDirected, IntoNeighbors, IntoNeighborsDirected, IntoNodeIdentifiers,
+ Data, EdgeCount, EdgeIndexable, FilterNode, GraphBase, IntoEdgeReferences, IntoEdges,
+ IntoEdgesDirected, IntoNeighbors, IntoNeighborsDirected, IntoNodeIdentifiers,
IntoNodeReferences, NodeCompactIndexable, NodeCount, NodeIndexable, VisitMap, Visitable,
},
- edge::{Direction, Edge},
+ edge::{Direction, Edge, EdgeId},
graph::Graph,
- id::{IndexMapper, LinearGraphId, ManagedGraphId},
- node::Node,
- storage::{DirectedGraphStorage, GraphStorage},
+ node::{Node, NodeId},
+ storage::{
+ auxiliary::{BooleanGraphStorage, Hints},
+ sequential::GraphIdBijection,
+ DirectedGraphStorage, GraphStorage,
+ },
};
impl<S> GraphBase for Graph<S>
where
S: GraphStorage,
- S::NodeId: Copy,
- S::EdgeId: Copy,
{
- type EdgeId = S::EdgeId;
- type NodeId = S::NodeId;
+ type EdgeId = EdgeId;
+ type NodeId = NodeId;
}
// TODO: GraphProp?!
@@ -38,8 +37,6 @@
impl<S> NodeCount for Graph<S>
where
S: GraphStorage,
- S::NodeId: Copy,
- S::EdgeId: Copy,
{
fn node_count(&self) -> usize {
self.num_nodes()
@@ -49,37 +46,28 @@
impl<S> NodeIndexable for Graph<S>
where
S: GraphStorage,
- S::NodeId: LinearGraphId<S> + Copy,
- S::EdgeId: Copy,
{
fn node_bound(&self) -> usize {
self.num_nodes()
}
fn to_index(&self, a: Self::NodeId) -> usize {
- S::NodeId::index_mapper(&self.storage).index(a)
+ self.storage.node_id_bijection().index(a)
}
fn from_index(&self, i: usize) -> Self::NodeId {
- S::NodeId::index_mapper(&self.storage)
+ self.storage
+ .node_id_bijection()
.reverse(i)
.expect("unable to determine index")
}
}
-impl<S> NodeCompactIndexable for Graph<S>
-where
- S: GraphStorage,
- S::NodeId: LinearGraphId<S> + Copy,
- S::EdgeId: Copy,
-{
-}
+impl<S> NodeCompactIndexable for Graph<S> where S: GraphStorage {}
impl<S> EdgeCount for Graph<S>
where
S: GraphStorage,
- S::NodeId: Copy,
- S::EdgeId: Copy,
{
fn edge_count(&self) -> usize {
self.num_edges()
@@ -89,19 +77,18 @@
impl<S> EdgeIndexable for Graph<S>
where
S: GraphStorage,
- S::NodeId: Copy,
- S::EdgeId: LinearGraphId<S> + Copy,
{
fn edge_bound(&self) -> usize {
self.num_edges()
}
fn to_index(&self, a: Self::EdgeId) -> usize {
- S::EdgeId::index_mapper(&self.storage).index(a)
+ self.storage.edge_id_bijection().index(a)
}
fn from_index(&self, i: usize) -> Self::EdgeId {
- S::EdgeId::index_mapper(&self.storage)
+ self.storage
+ .edge_id_bijection()
.reverse(i)
.expect("unable to determine index")
}
@@ -110,8 +97,6 @@
impl<S> Data for Graph<S>
where
S: GraphStorage,
- S::NodeId: Copy,
- S::EdgeId: Copy,
{
type EdgeWeight = S::EdgeWeight;
type NodeWeight = S::NodeWeight;
@@ -120,8 +105,6 @@
impl<S> IntoNodeIdentifiers for &Graph<S>
where
S: GraphStorage,
- S::NodeId: Copy,
- S::EdgeId: Copy,
{
type NodeIdentifiers = impl Iterator<Item = Self::NodeId>;
@@ -133,8 +116,6 @@
impl<'a, S> IntoNodeReferences for &'a Graph<S>
where
S: GraphStorage,
- S::NodeId: Copy,
- S::EdgeId: Copy,
{
type NodeRef = Node<'a, S>;
@@ -148,8 +129,6 @@
impl<'a, S> IntoEdgeReferences for &'a Graph<S>
where
S: GraphStorage,
- S::NodeId: Copy,
- S::EdgeId: Copy,
{
type EdgeRef = Edge<'a, S>;
@@ -164,8 +143,6 @@
impl<'a, S> IntoNeighbors for &'a Graph<S>
where
S: GraphStorage,
- S::NodeId: Copy,
- S::EdgeId: Copy,
{
type Neighbors = impl Iterator<Item = Self::NodeId>;
@@ -185,8 +162,6 @@
impl<'a, S> IntoNeighborsDirected for &'a Graph<S>
where
S: DirectedGraphStorage,
- S::NodeId: Copy,
- S::EdgeId: Copy,
{
type NeighborsDirected = impl Iterator<Item = Self::NodeId>;
@@ -203,8 +178,6 @@
impl<'a, S> IntoEdges for &'a Graph<S>
where
S: GraphStorage,
- S::NodeId: Copy,
- S::EdgeId: Copy,
{
type Edges = impl Iterator<Item = Self::EdgeRef>;
@@ -218,8 +191,6 @@
impl<'a, S> IntoEdgesDirected for &'a Graph<S>
where
S: DirectedGraphStorage,
- S::NodeId: Copy,
- S::EdgeId: Copy,
{
type EdgesDirected = impl Iterator<Item = Self::EdgeRef>;
@@ -231,84 +202,45 @@
}
}
-pub struct VisitationMap<'a, S, T>
+pub struct NodeVisitationMap<'a, S>
where
S: GraphStorage + 'a,
- T: LinearGraphId<S> + Clone + 'a,
{
- inner: BitBox,
- mapper: <T as LinearGraphId<S>>::Mapper<'a>,
+ inner: S::BooleanNodeStorage<'a>,
}
-impl<'a, S> VisitationMap<'a, S, S::NodeId>
+impl<'a, S> VisitMap<NodeId> for NodeVisitationMap<'a, S>
where
S: GraphStorage + 'a,
- S::NodeId: LinearGraphId<S> + Clone,
{
- fn new_node(size: usize, mapper: <S::NodeId as LinearGraphId<S>>::Mapper<'a>) -> Self {
- Self {
- inner: BitVec::repeat(false, size).into_boxed_bitslice(),
- mapper,
- }
+ fn visit(&mut self, a: NodeId) -> bool {
+ self.inner.set(a, true).is_none()
+ }
+
+ fn is_visited(&self, a: &NodeId) -> bool {
+ self.inner.get(*a).unwrap_or(false)
}
}
-impl<'a, S, T> VisitMap<T> for VisitationMap<'a, S, T>
+impl<'a, S> FilterNode<NodeId> for NodeVisitationMap<'a, S>
where
S: GraphStorage + 'a,
- T: LinearGraphId<S> + Clone,
{
- fn visit(&mut self, a: T) -> bool {
- let Some(index) = self.mapper.get(a) else {
- return false;
- };
-
- !self.inner.replace(index, true)
- }
-
- fn is_visited(&self, a: &T) -> bool {
- let Some(index) = self.mapper.get(*a) else {
- return false;
- };
-
- let Some(bit) = self.inner.get(index) else {
- return false;
- };
-
- *bit
- }
-}
-
-impl<'a, S> FilterNode<S::NodeId> for VisitationMap<'a, S, S::NodeId>
-where
- S: GraphStorage + 'a,
- S::NodeId: LinearGraphId<S> + Clone,
-{
- fn include_node(&self, node: S::NodeId) -> bool {
+ fn include_node(&self, node: NodeId) -> bool {
self.is_visited(&node)
}
}
-impl<'a, S> FilterEdge<S::EdgeId> for VisitationMap<'a, S, S::EdgeId>
-where
- S: GraphStorage + 'a,
- S::EdgeId: LinearGraphId<S> + Clone,
-{
- fn include_edge(&self, edge: S::EdgeId) -> bool {
- self.is_visited(&edge)
- }
-}
-
impl<'a, S> Visitable for &'a Graph<S>
where
S: GraphStorage,
- S::NodeId: LinearGraphId<S> + Copy,
- S::EdgeId: Copy,
{
- type Map = VisitationMap<'a, S, S::NodeId>;
+ type Map = NodeVisitationMap<'a, S>;
fn visit_map(&self) -> Self::Map {
- VisitationMap::new_node(self.num_nodes(), S::NodeId::index_mapper(&self.storage))
+ NodeVisitationMap {
+ inner: self.storage.boolean_node_storage(Hints::default()),
+ }
}
fn reset_map(&self, map: &mut Self::Map) {
@@ -320,14 +252,12 @@
impl<S> GetAdjacencyMatrix for Graph<S>
where
S: GraphStorage,
- S::NodeId: Copy,
- S::EdgeId: Copy,
{
type AdjMatrix = ();
fn adjacency_matrix(&self) -> Self::AdjMatrix {}
- fn is_adjacent(&self, _: &Self::AdjMatrix, a: Self::NodeId, b: Self::NodeId) -> bool {
+ fn is_adjacent(&self, (): &Self::AdjMatrix, a: Self::NodeId, b: Self::NodeId) -> bool {
self.edges_between(a, b).next().is_some()
}
}
@@ -336,8 +266,6 @@
impl<S> DataMap for Graph<S>
where
S: GraphStorage,
- S::NodeId: Copy,
- S::EdgeId: Copy,
{
fn node_weight(&self, id: Self::NodeId) -> Option<&Self::NodeWeight> {
self.node(id).map(|node| node.weight())
@@ -352,8 +280,6 @@
impl<S> Build for Graph<S>
where
S: GraphStorage,
- S::NodeId: ManagedGraphId + Copy,
- S::EdgeId: ManagedGraphId + Copy,
{
fn add_node(&mut self, weight: Self::NodeWeight) -> Self::NodeId {
self.insert_node(weight).id()
@@ -373,8 +299,6 @@
impl<S> Create for Graph<S>
where
S: GraphStorage,
- S::NodeId: ManagedGraphId + Copy,
- S::EdgeId: ManagedGraphId + Copy,
{
fn with_capacity(nodes: usize, edges: usize) -> Self {
Self::with_capacity(Some(nodes), Some(edges))
@@ -382,10 +306,4 @@
}
#[cfg(feature = "alloc")]
-impl<S> FromElements for Graph<S>
-where
- S: GraphStorage,
- S::NodeId: ManagedGraphId + Copy,
- S::EdgeId: ManagedGraphId + Copy,
-{
-}
+impl<S> FromElements for Graph<S> where S: GraphStorage {}
diff --git a/crates/core/src/graph/directed.rs b/crates/core/src/graph/directed.rs
index 7e8122d..fafa5ee 100644
--- a/crates/core/src/graph/directed.rs
+++ b/crates/core/src/graph/directed.rs
@@ -1,7 +1,7 @@
use crate::{
edge::{Direction, Edge, EdgeMut},
graph::Graph,
- node::{Node, NodeMut},
+ node::{Node, NodeId, NodeMut},
storage::DirectedGraphStorage,
};
@@ -46,8 +46,8 @@
/// ```
pub fn directed_edges_between(
&self,
- source: S::NodeId,
- target: S::NodeId,
+ source: NodeId,
+ target: NodeId,
) -> impl Iterator<Item = Edge<'_, S>> {
self.storage.directed_edges_between(source, target)
}
@@ -89,8 +89,8 @@
/// ```
pub fn directed_edges_between_mut(
&mut self,
- source: S::NodeId,
- target: S::NodeId,
+ source: NodeId,
+ target: NodeId,
) -> impl Iterator<Item = EdgeMut<'_, S>> {
self.storage.directed_edges_between_mut(source, target)
}
@@ -108,7 +108,7 @@
#[inline]
pub fn neighbors_directed(
&self,
- id: S::NodeId,
+ id: NodeId,
direction: Direction,
) -> impl Iterator<Item = Node<'_, S>> {
self.neighbours_directed(id, direction)
@@ -156,7 +156,7 @@
/// ```
pub fn neighbours_directed(
&self,
- id: S::NodeId,
+ id: NodeId,
direction: Direction,
) -> impl Iterator<Item = Node<'_, S>> {
self.storage.node_directed_neighbours(id, direction)
@@ -176,7 +176,7 @@
#[inline]
pub fn neighbors_directed_mut(
&mut self,
- id: S::NodeId,
+ id: NodeId,
direction: Direction,
) -> impl Iterator<Item = NodeMut<'_, S>> {
self.neighbours_directed_mut(id, direction)
@@ -221,7 +221,7 @@
/// ```
pub fn neighbours_directed_mut(
&mut self,
- id: S::NodeId,
+ id: NodeId,
direction: Direction,
) -> impl Iterator<Item = NodeMut<'_, S>> {
self.storage.node_directed_neighbours_mut(id, direction)
@@ -263,7 +263,7 @@
/// ```
pub fn connections_directed(
&self,
- id: S::NodeId,
+ id: NodeId,
direction: Direction,
) -> impl Iterator<Item = Edge<'_, S>> {
self.storage.node_directed_connections(id, direction)
@@ -310,7 +310,7 @@
/// ```
pub fn connections_directed_mut(
&mut self,
- id: S::NodeId,
+ id: NodeId,
direction: Direction,
) -> impl Iterator<Item = EdgeMut<'_, S>> {
self.storage.node_directed_connections_mut(id, direction)
diff --git a/crates/core/src/graph/insert.rs b/crates/core/src/graph/insert.rs
index 48ce252..c1035b8 100644
--- a/crates/core/src/graph/insert.rs
+++ b/crates/core/src/graph/insert.rs
@@ -1,11 +1,12 @@
+#[cfg(feature = "alloc")]
+use alloc::{vec, vec::Vec};
+
use error_stack::{Result, ResultExt};
use crate::{
- attributes::{Attributes, NoValue},
- edge::EdgeMut,
+ edge::{EdgeId, EdgeMut},
graph::Graph,
- id::{ArbitraryGraphId, GraphId, ManagedGraphId},
- node::NodeMut,
+ node::{NodeId, NodeMut},
storage::GraphStorage,
Error,
};
@@ -35,13 +36,8 @@
/// This may include things like parallel edges or self loops depending on implementation.
///
/// Refer to the documentation of the underlying storage for more information.
- pub fn try_insert_node(
- &mut self,
- attributes: impl Into<Attributes<<S::NodeId as GraphId>::AttributeIndex, S::NodeWeight>>,
- ) -> Result<NodeMut<S>, Error> {
- let Attributes { id, weight } = attributes.into();
-
- let id = self.storage.next_node_id(id);
+ pub fn try_insert_node(&mut self, weight: S::NodeWeight) -> Result<NodeMut<S>, Error> {
+ let id = self.storage.next_node_id();
self.storage.insert_node(id, weight).change_context(Error)
}
@@ -71,7 +67,7 @@
/// The reason is that some storage types might not be able to guarantee that the node can be
/// inserted, but we still want to provide a convenient way to insert a node.
/// Another reason is that this mirrors the API of other libraries and the std, such as the
- /// standard library (through [`alloc::vec::Vec::push`], or
+ /// standard library (through [`Vec::push`], or
/// [`std::collections::HashMap::insert`]).
/// These may also panic and do not return a result.
/// But(!) it is important to note that the constraints and reason why they may panic are quite
@@ -87,11 +83,8 @@
/// This may include things like parallel edges or self loops depending on implementation.
///
/// Refer to the documentation of the underlying storage for more information.
- pub fn insert_node(
- &mut self,
- attributes: impl Into<Attributes<<S::NodeId as GraphId>::AttributeIndex, S::NodeWeight>>,
- ) -> NodeMut<S> {
- self.try_insert_node(attributes)
+ pub fn insert_node(&mut self, weight: S::NodeWeight) -> NodeMut<S> {
+ self.try_insert_node(weight)
.expect("Constraint violation. Try using `try_insert_node` instead.")
}
}
@@ -99,7 +92,6 @@
impl<S> Graph<S>
where
S: GraphStorage,
- S::NodeId: ManagedGraphId,
{
/// Insert a node, where the weight is dependent on the id.
///
@@ -138,9 +130,9 @@
/// Refer to the documentation of the underlying storage for more information.
pub fn try_insert_node_with(
&mut self,
- weight: impl FnOnce(S::NodeId) -> S::NodeWeight,
+ weight: impl FnOnce(NodeId) -> S::NodeWeight,
) -> Result<NodeMut<S>, Error> {
- let id = self.storage.next_node_id(NoValue::new());
+ let id = self.storage.next_node_id();
let weight = weight(id);
self.storage.insert_node(id, weight).change_context(Error)
@@ -178,10 +170,7 @@
/// This may include things like parallel edges or self loops depending on implementation.
///
/// Refer to the documentation of the underlying storage for more information.
- pub fn insert_node_with(
- &mut self,
- weight: impl FnOnce(S::NodeId) -> S::NodeWeight,
- ) -> NodeMut<S> {
+ pub fn insert_node_with(&mut self, weight: impl FnOnce(NodeId) -> S::NodeWeight) -> NodeMut<S> {
self.try_insert_node_with(weight)
.expect("Constraint violation. Try using `try_insert_node_with` instead.")
}
@@ -190,60 +179,6 @@
impl<S> Graph<S>
where
S: GraphStorage,
- S::NodeId: ArbitraryGraphId,
-{
- /// Insert a node, or update the weight of an existing node.
- ///
- /// This is the fallible version of [`Self::upsert_node`].
- // TODO: Example
- /// # Errors
- ///
- /// The same errors as [`Self::try_insert_node`] may occur.
- ///
- /// # Panics
- ///
- /// If the storage is inconsistent.
- /// This should never happen, as this is an implementation error of the underlying
- /// [`GraphStorage`], which must guarantee that if one calls [`GraphStorage::contains_node`]
- /// with the id of the node to check if a node exists, and then calls
- /// [`GraphStorage::node_mut`] with the same id, it must return a node.
- pub fn try_upsert_node(
- &mut self,
- id: S::NodeId,
- weight: S::NodeWeight,
- ) -> Result<NodeMut<S>, Error> {
- // we cannot use `if let` here due to limitations of the borrow checker
- if self.storage.contains_node(id) {
- let mut node = self
- .storage
- .node_mut(id)
- .expect("inconsistent storage, node must exist");
-
- *node.weight_mut() = weight;
-
- Ok(node)
- } else {
- self.storage.insert_node(id, weight).change_context(Error)
- }
- }
-
- /// Insert a node, or update the weight of an existing node.
- ///
- /// This is the infallible version of [`Self::try_upsert_node`], which will panic instead.
- // TODO: Example
- /// # Panics
- ///
- /// The same panics as [`Self::try_upsert_node`] may occur, as well as the ones from
- /// [`Self::insert_node`].
- pub fn upsert_node(&mut self, id: S::NodeId, weight: S::NodeWeight) -> NodeMut<S> {
- self.try_upsert_node(id, weight)
- .expect("Constraint violation. Try using `try_upsert_node` instead.")
- }
-}
-
-impl<S> Graph<S>
-where
- S: GraphStorage,
{
/// Try to insert an edge with the given attributes.
///
@@ -271,13 +206,11 @@
/// Refer to the documentation of the underlying storage for more information.
pub fn try_insert_edge(
&mut self,
- attributes: impl Into<Attributes<<S::EdgeId as GraphId>::AttributeIndex, S::EdgeWeight>>,
- source: S::NodeId,
- target: S::NodeId,
+ weight: S::EdgeWeight,
+ source: NodeId,
+ target: NodeId,
) -> Result<EdgeMut<S>, Error> {
- let Attributes { id, weight } = attributes.into();
-
- let id = self.storage.next_edge_id(id);
+ let id = self.storage.next_edge_id();
self.storage
.insert_edge(id, weight, source, target)
.change_context(Error)
@@ -311,11 +244,11 @@
/// This may include things like parallel edges or self loops depending on implementation.
pub fn insert_edge(
&mut self,
- attributes: impl Into<Attributes<<S::EdgeId as GraphId>::AttributeIndex, S::EdgeWeight>>,
- source: S::NodeId,
- target: S::NodeId,
+ weight: S::EdgeWeight,
+ source: NodeId,
+ target: NodeId,
) -> EdgeMut<S> {
- self.try_insert_edge(attributes, source, target)
+ self.try_insert_edge(weight, source, target)
.expect("Constraint violation. Try using `try_insert_edge` instead.")
}
}
@@ -323,7 +256,6 @@
impl<S> Graph<S>
where
S: GraphStorage,
- S::EdgeId: ManagedGraphId,
{
/// Insert an edge, where the weight is dependent on the id.
///
@@ -361,11 +293,11 @@
/// The same errors as [`Self::try_insert_edge`] may occur.
pub fn try_insert_edge_with(
&mut self,
- weight: impl FnOnce(S::EdgeId) -> S::EdgeWeight,
- source: S::NodeId,
- target: S::NodeId,
+ weight: impl FnOnce(EdgeId) -> S::EdgeWeight,
+ source: NodeId,
+ target: NodeId,
) -> Result<EdgeMut<S>, Error> {
- let id = self.storage.next_edge_id(NoValue::new());
+ let id = self.storage.next_edge_id();
let weight = weight(id);
self.storage
@@ -406,62 +338,63 @@
/// The same panics as [`Self::insert_edge`] may occur.
pub fn insert_edge_with(
&mut self,
- weight: impl FnOnce(S::EdgeId) -> S::EdgeWeight,
- source: S::NodeId,
- target: S::NodeId,
+ weight: impl FnOnce(EdgeId) -> S::EdgeWeight,
+ source: NodeId,
+ target: NodeId,
) -> EdgeMut<S> {
self.try_insert_edge_with(weight, source, target)
.expect("Constraint violation. Try using `try_insert_edge_with` instead.")
}
}
+#[cfg(feature = "alloc")]
impl<S> Graph<S>
where
S: GraphStorage,
- S::EdgeId: ArbitraryGraphId,
+ S::EdgeWeight: Clone,
{
/// Insert an edge, or update the weight of an existing edge, if it exists.
///
+ /// If multiple edges exist between the given nodes, all of them will be updated with the given
+ ///
+ /// Edges are treated as undirected, so the order of the nodes does not matter.
+ ///
+ /// Unlike [`Self::try_upsert_edge`], this will not return the edge, and instead return a list
+ /// containing all affected edge identifiers.
+ ///
/// This is the fallible version of [`Self::upsert_edge`].
// TODO: Example
///
/// # Errors
///
/// The same errors as [`Self::try_insert_edge`] may occur.
- ///
- /// # Panics
- ///
- /// If the storage is inconsistent.
- /// This should never happen, as this is an implementation error of the underlying
- /// [`GraphStorage`], which must guarantee that if one calls [`GraphStorage::contains_edge`]
- /// with the id of the edge to check if an edge exists, and then calls
- /// [`GraphStorage::edge_mut`] with the same id, it must return an edge.
pub fn try_upsert_edge(
&mut self,
- id: S::EdgeId,
weight: S::EdgeWeight,
- source: S::NodeId,
- target: S::NodeId,
- ) -> Result<EdgeMut<S>, Error> {
- if self.storage.contains_edge(id) {
- let mut edge = self
- .storage
- .edge_mut(id)
- .expect("inconsistent storage, edge must exist");
+ source: NodeId,
+ target: NodeId,
+ ) -> Result<Vec<EdgeId>, Error> {
+ let mut affected = vec![];
- *edge.weight_mut() = weight;
-
- Ok(edge)
- } else {
- self.storage
- .insert_edge(id, weight, source, target)
- .change_context(Error)
+ for mut edge in self.storage.edges_between_mut(source, target) {
+ *edge.weight_mut() = weight.clone();
+ affected.push(edge.id());
}
+
+ if !affected.is_empty() {
+ return Ok(affected);
+ }
+
+ self.try_insert_edge(weight, source, target)
+ .map(|edge| vec![edge.id()])
}
/// Insert an edge, or update the weight of an existing edge, if it exists.
///
+ /// If multiple edges exist between the given nodes, all of them will be updated with the given
+ /// value.
+ ///
/// This is the infallible version of [`Self::try_upsert_edge`], which will panic instead.
///
/// # Panics
@@ -470,13 +403,71 @@
/// [`Self::insert_edge`].
pub fn upsert_edge(
&mut self,
- id: S::EdgeId,
weight: S::EdgeWeight,
- source: S::NodeId,
- target: S::NodeId,
- ) -> EdgeMut<S> {
- self.try_upsert_edge(id, weight, source, target)
+ source: NodeId,
+ target: NodeId,
+ ) -> Vec<EdgeId> {
+ self.try_upsert_edge(weight, source, target)
.expect("Constraint violation. Try using `try_upsert_edge` instead.")
}
+
+ /// Insert an edge, or update the weight of an existing edge, if it exists.
+ ///
+ /// If multiple edges exist between the given nodes, all of them will invoke the given closure
+ /// with the edge.
+ ///
+ /// Unlike [`Self::try_upsert_edge_with`], this will not return the edge, and instead return a
+ /// list containing all affected edge identifiers.
+ ///
+ /// This is the fallible version of [`Self::upsert_edge_with`].
+ ///
+ /// # Errors
+ ///
+ /// The same errors as [`Self::try_insert_edge`] may occur.
+ pub fn try_upsert_edge_with(
+ &mut self,
+ mut on_update: impl FnMut(&mut EdgeMut<S>) -> S::EdgeWeight,
+ on_insert: impl FnOnce(EdgeId) -> S::EdgeWeight,
+
+ source: NodeId,
+ target: NodeId,
+ ) -> Result<Vec<EdgeId>, Error> {
+ let mut affected = vec![];
+
+ for mut edge in self.storage.edges_between_mut(source, target) {
+ *edge.weight_mut() = on_update(&mut edge);
+ affected.push(edge.id());
+ }
+
+ if !affected.is_empty() {
+ return Ok(affected);
+ }
+
+ self.try_insert_edge_with(on_insert, source, target)
+ .map(|edge| vec![edge.id()])
+ }
+
+ /// Insert an edge, or update the weight of an existing edge, if it exists.
+ ///
+ /// If multiple edges exist between the given nodes, all of them will invoke the given closure
+ /// with the edge.
+ ///
+ /// This is the infallible version of [`Self::try_upsert_edge_with`], which will panic instead.
+ ///
+ /// # Panics
+ ///
+ /// The same panics as [`Self::try_upsert_edge_with`] may occur, as well as the ones from
+ /// [`Self::insert_edge_with`].
+ pub fn upsert_edge_with(
+ &mut self,
+ on_update: impl FnMut(&mut EdgeMut<S>) -> S::EdgeWeight,
+ on_insert: impl FnOnce(EdgeId) -> S::EdgeWeight,
+
+ source: NodeId,
+ target: NodeId,
+ ) -> Vec<EdgeId> {
+ self.try_upsert_edge_with(on_update, on_insert, source, target)
+ .expect("Constraint violation. Try using `try_upsert_edge_with` instead.")
+ }
}
diff --git a/crates/core/src/graph/mod.rs b/crates/core/src/graph/mod.rs
index 8937b61..3ddc677 100644
--- a/crates/core/src/graph/mod.rs
+++ b/crates/core/src/graph/mod.rs
@@ -3,12 +3,13 @@
mod insert;
mod resize;
mod retain;
+mod reverse;
use error_stack::Result;
use crate::{
- edge::{DetachedEdge, Edge, EdgeMut},
- node::{DetachedNode, Node, NodeMut},
+ edge::{DetachedEdge, Edge, EdgeId, EdgeMut},
+ node::{DetachedNode, Node, NodeId, NodeMut},
storage::GraphStorage,
};
@@ -260,8 +261,8 @@
/// If any of the nodes or edges are invalid, or any of the constraint checks of the underlying
/// implementation fail, an error is returned.
pub fn from_parts(
- nodes: impl IntoIterator<Item = DetachedNode<S::NodeId, S::NodeWeight>>,
- edges: impl IntoIterator<Item = DetachedEdge<S::EdgeId, S::NodeId, S::EdgeWeight>>,
+ nodes: impl IntoIterator<Item = DetachedNode<S::NodeWeight>>,
+ edges: impl IntoIterator<Item = DetachedEdge<S::EdgeWeight>>,
) -> Result<Self, S::Error> {
Ok(Self {
storage: S::from_parts(nodes, edges)?,
@@ -317,8 +318,8 @@
pub fn into_parts(
self,
) -> (
- impl IntoIterator<Item = DetachedNode<S::NodeId, S::NodeWeight>>,
- impl IntoIterator<Item = DetachedEdge<S::EdgeId, S::NodeId, S::EdgeWeight>>,
+ impl IntoIterator<Item = DetachedNode<S::NodeWeight>>,
+ impl IntoIterator<Item = DetachedEdge<S::EdgeWeight>>,
) {
self.storage.into_parts()
}
@@ -338,12 +339,7 @@
// TODO: example
pub fn convert<T>(self) -> Result<Graph<T>, T::Error>
where
- T: GraphStorage<
- NodeId = S::NodeId,
- NodeWeight = S::NodeWeight,
- EdgeId = S::EdgeId,
- EdgeWeight = S::EdgeWeight,
- >,
+ T: GraphStorage<NodeWeight = S::NodeWeight, EdgeWeight = S::EdgeWeight>,
{
let (nodes, edges) = self.storage.into_parts();
@@ -472,7 +468,7 @@
/// None
/// );
/// ```
- pub fn node(&self, id: S::NodeId) -> Option<Node<S>> {
+ pub fn node(&self, id: NodeId) -> Option<Node<S>> {
self.storage.node(id)
}
@@ -502,7 +498,7 @@
///
/// assert!(graph.node_mut(&b).is_none());
/// ```
- pub fn node_mut(&mut self, id: S::NodeId) -> Option<NodeMut<S>> {
+ pub fn node_mut(&mut self, id: NodeId) -> Option<NodeMut<S>> {
self.storage.node_mut(id)
}
@@ -526,7 +522,7 @@
/// assert!(graph.contains_node(&a));
/// assert!(!graph.contains_node(&b));
/// ```
- pub fn contains_node(&self, id: S::NodeId) -> bool {
+ pub fn contains_node(&self, id: NodeId) -> bool {
self.storage.contains_node(id)
}
@@ -555,7 +551,7 @@
/// assert_eq!(graph.remove_node(&a), None);
/// assert_eq!(graph.remove_node(&b), None);
/// ```
- pub fn remove_node(&mut self, id: S::NodeId) -> Option<DetachedNode<S::NodeId, S::NodeWeight>> {
+ pub fn remove_node(&mut self, id: NodeId) -> Option<DetachedNode<S::NodeWeight>> {
self.storage.remove_node(id)
}
@@ -586,7 +582,7 @@
/// );
/// assert!(graph.edge(&bc).is_none());
/// ```
- pub fn edge(&self, id: S::EdgeId) -> Option<Edge<S>> {
+ pub fn edge(&self, id: EdgeId) -> Option<Edge<S>> {
self.storage.edge(id)
}
@@ -618,7 +614,7 @@
/// );
/// assert!(graph.edge_mut(&bc).is_none());
/// ```
- pub fn edge_mut(&mut self, id: S::EdgeId) -> Option<EdgeMut<S>> {
+ pub fn edge_mut(&mut self, id: EdgeId) -> Option<EdgeMut<S>> {
self.storage.edge_mut(id)
}
@@ -643,7 +639,7 @@
/// assert!(graph.contains_edge(&ab));
/// assert!(!graph.contains_edge(&bc));
/// ```
- pub fn contains_edge(&self, id: S::EdgeId) -> bool {
+ pub fn contains_edge(&self, id: EdgeId) -> bool {
self.storage.contains_edge(id)
}
@@ -678,10 +674,7 @@
/// assert_eq!(graph.remove_edge(&ab), None);
/// assert_eq!(graph.remove_edge(&bc), None);
/// ```
- pub fn remove_edge(
- &mut self,
- id: S::EdgeId,
- ) -> Option<DetachedEdge<S::EdgeId, S::NodeId, S::EdgeWeight>> {
+ pub fn remove_edge(&mut self, id: EdgeId) -> Option<DetachedEdge<S::EdgeWeight>> {
self.storage.remove_edge(id)
}
@@ -690,7 +683,7 @@
/// This is an alias for [`Self::neighbours`], as there's a spelling difference between the
/// American and British English.
#[inline]
- pub fn neighbors(&self, id: S::NodeId) -> impl Iterator<Item = Node<'_, S>> {
+ pub fn neighbors(&self, id: NodeId) -> impl Iterator<Item = Node<'_, S>> {
self.neighbours(id)
}
@@ -736,7 +729,7 @@
/// [a, c].into_iter().collect::<HashSet<_>>()
/// );
/// ```
- pub fn neighbours(&self, id: S::NodeId) -> impl Iterator<Item = Node<'_, S>> {
+ pub fn neighbours(&self, id: NodeId) -> impl Iterator<Item = Node<'_, S>> {
self.storage.node_neighbours(id)
}
@@ -745,7 +738,7 @@
/// This is an alias for [`Self::neighbours_mut`], as there's a spelling difference between
/// American and British English.
#[inline]
- pub fn neighbors_mut(&mut self, id: S::NodeId) -> impl Iterator<Item = NodeMut<'_, S>> {
+ pub fn neighbors_mut(&mut self, id: NodeId) -> impl Iterator<Item = NodeMut<'_, S>> {
self.neighbours_mut(id)
}
@@ -793,7 +786,7 @@
/// Some((d, 3))
/// );
/// ```
- pub fn neighbours_mut(&mut self, id: S::NodeId) -> impl Iterator<Item = NodeMut<'_, S>> {
+ pub fn neighbours_mut(&mut self, id: NodeId) -> impl Iterator<Item = NodeMut<'_, S>> {
self.storage.node_neighbours_mut(id)
}
@@ -828,7 +821,7 @@
/// [ab, ca, aa].into_iter().collect::<HashSet<_>>()
/// );
/// ```
- pub fn connections(&self, id: S::NodeId) -> impl Iterator<Item = Edge<'_, S>> {
+ pub fn connections(&self, id: NodeId) -> impl Iterator<Item = Edge<'_, S>> {
self.storage.node_connections(id)
}
@@ -874,7 +867,7 @@
/// Some((bc, u8::MAX - 1))
/// );
/// ```
- pub fn connections_mut(&mut self, id: S::NodeId) -> impl Iterator<Item = EdgeMut<'_, S>> {
+ pub fn connections_mut(&mut self, id: NodeId) -> impl Iterator<Item = EdgeMut<'_, S>> {
self.storage.node_connections_mut(id)
}
@@ -905,7 +898,7 @@
/// assert_eq!(graph.degree(&c), 2);
/// assert_eq!(graph.degree(&d), 0);
/// ```
- pub fn degree(&self, id: S::NodeId) -> usize {
+ pub fn degree(&self, id: NodeId) -> usize {
self.storage.node_degree(id)
}
@@ -939,7 +932,7 @@
/// [ab, ba].into_iter().collect::<HashSet<_>>()
/// );
/// ```
- pub fn edges_between(&self, u: S::NodeId, v: S::NodeId) -> impl Iterator<Item = Edge<'_, S>> {
+ pub fn edges_between(&self, u: NodeId, v: NodeId) -> impl Iterator<Item = Edge<'_, S>> {
self.storage.edges_between(u, v)
}
@@ -979,8 +972,8 @@
/// ```
pub fn edges_between_mut(
&mut self,
- u: S::NodeId,
- v: S::NodeId,
+ u: NodeId,
+ v: NodeId,
) -> impl Iterator<Item = EdgeMut<'_, S>> {
self.storage.edges_between_mut(u, v)
}
diff --git a/crates/core/src/graph/reverse.rs b/crates/core/src/graph/reverse.rs
new file mode 100644
index 0000000..a922564
--- /dev/null
+++ b/crates/core/src/graph/reverse.rs
@@ -0,0 +1,30 @@
+use crate::{storage::reverse::ReverseGraphStorage, Edge, EdgeMut, Graph, Node, NodeMut};
+
+impl<S> Graph<S>
+where
+ S: ReverseGraphStorage,
+{
+ pub fn contains_node_key(&self, key: &S::NodeKey) -> bool {
+ self.storage.contains_node_key(key)
+ }
+
+ pub fn node_by_key(&self, key: &S::NodeKey) -> Option<Node<'_, S>> {
+ self.storage.node_by_key(key)
+ }
+
+ pub fn node_by_key_mut(&mut self, key: &S::NodeKey) -> Option<NodeMut<'_, S>> {
+ self.storage.node_by_key_mut(key)
+ }
+
+ pub fn contains_edge_key(&self, key: &S::EdgeKey) -> bool {
+ self.storage.contains_edge_key(key)
+ }
+
+ pub fn edge_by_key(&self, key: &S::EdgeKey) -> Option<Edge<'_, S>> {
+ self.storage.edge_by_key(key)
+ }
+
+ pub fn edge_by_key_mut(&mut self, key: &S::EdgeKey) -> Option<EdgeMut<'_, S>> {
+ self.storage.edge_by_key_mut(key)
+ }
+}
diff --git a/crates/core/src/id/associative.rs b/crates/core/src/id/associative.rs
deleted file mode 100644
index ed4a95b..0000000
--- a/crates/core/src/id/associative.rs
+++ /dev/null
@@ -1,51 +0,0 @@
-use crate::{GraphId, GraphStorage};
-
-// TODO: Entry API
-
-pub trait AttributeMapper<K, V> {
- type Iter<'a>: Iterator<Item = (K, &'a V)>
- where
- V: 'a,
- Self: 'a;
-
- fn get(&self, id: K) -> Option<&V>;
- fn get_mut(&mut self, id: K) -> Option<&mut V>;
- fn index(&self, id: K) -> &V {
- self.get(id).expect("item")
- }
- fn index_mut(&mut self, id: K) -> &mut V {
- self.get_mut(id).expect("item")
- }
-
- fn set(&mut self, id: K, value: V) -> Option<V>;
- fn remove(&mut self, id: K) -> Option<V>;
-
- fn iter(&self) -> Self::Iter<'_>;
-}
-
-pub trait BooleanMapper<K> {
- fn get(&self, id: K) -> Option<bool>;
- #[inline]
- fn index(&self, id: K) -> bool {
- self.get(id).unwrap_or(false)
- }
-
- fn set(&mut self, id: K, flag: bool) -> Option<bool>;
-}
-
-pub trait AssociativeGraphId<S>: GraphId + Sized
-where
- S: GraphStorage,
-{
- type AttributeMapper<'a, V>: AttributeMapper<Self, V>
- where
- S: 'a;
-
- type BooleanMapper<'a>: BooleanMapper<Self>
- where
- S: 'a;
-
- fn attribute_mapper<V>(storage: &S) -> Self::AttributeMapper<'_, V>;
-
- fn boolean_mapper(storage: &S) -> Self::BooleanMapper<'_>;
-}
diff --git a/crates/core/src/id/mod.rs b/crates/core/src/id/mod.rs
deleted file mode 100644
index 3cb1299..0000000
--- a/crates/core/src/id/mod.rs
+++ /dev/null
@@ -1,58 +0,0 @@
-//! Module for identifiers.
-//!
-//! Identifiers are used to _identify_ edges and nodes.
-//!
-//! Primarily this module defines one type: [`GraphId`], which should not be implemented alone, but
-//! instead be implemented alongside [`ManagedGraphId`] and [`ArbitraryGraphId`], which are mutually
-//! exclusive and define if a node or edge id will be automatically assigned by the graph (are
-//! managed) and a user has no control over their value or are arbitrary, allowing the user to use
-//! _any_ value.
-mod associative;
-
-mod linear;
-
-use core::fmt::Debug;
-
-pub use self::{
- associative::{AssociativeGraphId, AttributeMapper, BooleanMapper},
- linear::{IndexMapper, LinearGraphId},
-};
-use crate::attributes::NoValue;
-
-// The `PartialEq` bound is required for the default implementation, we could in theory remove it,
-// but would need to remove _many_ default implementations that rely on it.
-// Another possibility would've been to use `Hash` for `GraphId`, but `ArbitaryGraphId` needs to be
-// a wrapper type anyway, so it could just require `Hash` for the inner type, and then implement
-// `PartialEq` based on that.
-/// A unique identifier for a node or edge.
-///
-/// This trait is implemented for all types that are used as node or edge identifiers in the graph.
-/// A type should never only implement this trait, but also [`ManagedGraphId`] or
-/// [`ArbitraryGraphId`].
-pub trait GraphId: Debug + Copy + Clone + PartialEq {
- /// The type of value used to index attributes.
- ///
- /// Used to differentiate between [`ManagedGraphId`] and [`ArbitraryGraphId`] and to allow for
- /// inference on weights via the [`Attributes`] type.
- ///
- /// There are essentially two valid values for this type: [`NoValue`] and `Self`.
- ///
- /// [`Attributes`]: crate::attributes::Attributes
- type AttributeIndex;
-}
-
-/// A unique identifier for a node or edge that is managed by the graph.
-///
-/// Marker trait to indicate that the graph manages the identifier of the node or edge, and cannot
-/// be specified by the user itself.
-///
-/// This is analogous to an index in a `Vec`.
-pub trait ManagedGraphId: GraphId<AttributeIndex = NoValue> {}
-
-/// A unique identifier for a node or edge that is not managed by the graph.
-///
-/// Marker trait to indicate that the graph does not manage the identifier of the node or edge, and
-/// must be specified by the user itself.
-///
-/// This is analogous to a key in a `HashMap`.
-pub trait ArbitraryGraphId: GraphId<AttributeIndex = Self> {}
diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs
index ff52808..1d99b02 100644
--- a/crates/core/src/lib.rs
+++ b/crates/core/src/lib.rs
@@ -120,13 +120,11 @@
#[cfg(feature = "alloc")]
extern crate alloc;
-pub mod attributes;
#[deprecated(since = "0.1.0")]
pub mod deprecated;
pub mod edge;
mod error;
pub(crate) mod graph;
-pub mod id;
pub mod node;
pub mod storage;
@@ -134,7 +132,6 @@
edge::{DetachedEdge, Edge, EdgeMut, GraphDirectionality},
error::Error,
graph::Graph,
- id::{ArbitraryGraphId, GraphId, ManagedGraphId},
node::{DetachedNode, Node, NodeMut},
storage::{DirectedGraphStorage, GraphStorage},
};
diff --git a/crates/core/src/node/compat.rs b/crates/core/src/node/compat.rs
index 66759cc..487c97c 100644
--- a/crates/core/src/node/compat.rs
+++ b/crates/core/src/node/compat.rs
@@ -1,18 +1,21 @@
//! Compatability implementations for deprecated graph traits.
#![allow(deprecated)]
-use crate::{deprecated::visit::NodeRef, node::Node, storage::GraphStorage};
+use crate::{
+ deprecated::visit::NodeRef,
+ node::{Node, NodeId},
+ storage::GraphStorage,
+};
impl<S> NodeRef for Node<'_, S>
where
S: GraphStorage,
- S::NodeId: Clone,
{
- type NodeId = S::NodeId;
+ type NodeId = NodeId;
type Weight = S::NodeWeight;
fn id(&self) -> Self::NodeId {
- self.id.clone()
+ self.id
}
fn weight(&self) -> &Self::Weight {
diff --git a/crates/core/src/node/mod.rs b/crates/core/src/node/mod.rs
index 716f6f3..3e1e845 100644
--- a/crates/core/src/node/mod.rs
+++ b/crates/core/src/node/mod.rs
@@ -17,7 +17,7 @@
mod compat;
use core::{
- fmt::{Debug, Formatter},
+ fmt::{Debug, Display, Formatter},
hash::Hash,
};
@@ -26,6 +26,77 @@
storage::{DirectedGraphStorage, GraphStorage},
};
+/// ID of a node in a graph.
+///
+/// This is guaranteed to be unique within the graph, library authors and library consumers **must**
+/// treat this as an opaque value akin to [`TypeId`].
+///
+/// The layout of the type is semver stable, but not part of the public API.
+///
+/// [`GraphStorage`] implementations may uphold additional invariants on the inner value and
+/// code outside of the [`GraphStorage`] should **never** construct a [`NodeId`] directly.
+///
+/// Accessing a [`GraphStorage`] implementation with a [`NodeId`] not returned by an instance itself
+/// is considered undefined behavior.
+///
+/// [`TypeId`]: core::any::TypeId
+#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct NodeId(usize);
+
+impl Display for NodeId {
+ // we could also utilize a VTable here instead, that would allow for custom formatting
+ // but that would be an additional pointer added to the type that must be carried around
+ // that's about ~8 bytes on 64-bit systems
+ fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
+ writeln!(f, "NodeId({})", self.0)
+ }
+}
+
+// TODO: find a better way to gate these functions
+impl NodeId {
+ /// Creates a new [`NodeId`].
+ ///
+ /// # Note
+ ///
+ /// Using this outside of the [`GraphStorage`] implementation is considered undefined behavior.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use petgraph_core::node::NodeId;
+ ///
+ /// let id = NodeId::new(0);
+ /// ```
+ // Hidden so that non-GraphStorage implementors are not tempted to use this.
+ #[doc(hidden)]
+ #[must_use]
+ pub const fn new(id: usize) -> Self {
+ Self(id)
+ }
+
+ /// Returns the inner value of the [`NodeId`].
+ ///
+ /// # Note
+ ///
+ /// Using this outside of the [`GraphStorage`] implementation is considered undefined behavior.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use petgraph_core::node::NodeId;
+ ///
+ /// let id = NodeId::new(0);
+ ///
+ /// assert_eq!(id.into_inner(), 0);
+ /// ```
+ // Hidden so that non-GraphStorage implementors are not tempted to use this.
+ #[doc(hidden)]
+ #[must_use]
+ pub const fn into_inner(self) -> usize {
+ self.0
+ }
+}
+
/// Active node in a graph.
///
/// Node that is part of a graph.
@@ -53,14 +124,13 @@
{
storage: &'a S,
- id: S::NodeId,
+ id: NodeId,
weight: &'a S::NodeWeight,
}
impl<S> PartialEq for Node<'_, S>
where
S: GraphStorage,
- S::NodeId: PartialEq,
S::NodeWeight: PartialEq,
{
fn eq(&self, other: &Node<'_, S>) -> bool {
@@ -71,7 +141,6 @@
impl<S> Eq for Node<'_, S>
where
S: GraphStorage,
- S::NodeId: Eq,
S::NodeWeight: Eq,
{
}
@@ -79,7 +148,6 @@
impl<S> PartialOrd for Node<'_, S>
where
S: GraphStorage,
- S::NodeId: PartialOrd,
S::NodeWeight: PartialOrd,
{
fn partial_cmp(&self, other: &Node<'_, S>) -> Option<core::cmp::Ordering> {
@@ -90,7 +158,6 @@
impl<S> Ord for Node<'_, S>
where
S: GraphStorage,
- S::NodeId: Ord,
S::NodeWeight: Ord,
{
fn cmp(&self, other: &Node<'_, S>) -> core::cmp::Ordering {
@@ -101,7 +168,6 @@
impl<S> Hash for Node<'_, S>
where
S: GraphStorage,
- S::NodeId: Hash,
S::NodeWeight: Hash,
{
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
@@ -168,7 +234,7 @@
/// ```
///
/// [`Graph::node`]: crate::graph::Graph::node
- pub const fn new(storage: &'a S, id: S::NodeId, weight: &'a S::NodeWeight) -> Self {
+ pub const fn new(storage: &'a S, id: NodeId, weight: &'a S::NodeWeight) -> Self {
Self {
storage,
id,
@@ -197,7 +263,7 @@
/// assert_eq!(node.id(), &a);
/// ```
#[must_use]
- pub const fn id(&self) -> S::NodeId {
+ pub const fn id(&self) -> NodeId {
self.id
}
@@ -318,6 +384,28 @@
pub fn degree(&self) -> usize {
self.storage.node_degree(self.id)
}
+
+ /// Change the underlying storage of the node.
+ ///
+ /// Should only be used when layering multiple [`GraphStorage`] implementations on top of each
+ /// other.
+ ///
+ /// # Note
+ ///
+ /// This should not lead to any undefined behaviour, but might have unintended consequences if
+ /// the storage does not recognize the inner id as valid.
+ /// You should only use this if you know what you are doing.
+ #[must_use]
+ pub const fn change_storage_unchecked<T>(self, storage: &'a T) -> Node<'a, T>
+ where
+ T: GraphStorage<NodeWeight = S::NodeWeight>,
+ {
+ Node {
+ storage,
+ id: self.id,
+ weight: self.weight,
+ }
+ }
}
impl<'a, S> Node<'a, S>
@@ -442,7 +530,7 @@
///
/// [`Graph::from_parts`]: crate::graph::Graph::from_parts
#[must_use]
- pub fn detach(self) -> DetachedNode<S::NodeId, S::NodeWeight> {
+ pub fn detach(self) -> DetachedNode<S::NodeWeight> {
DetachedNode::new(self.id, self.weight.clone())
}
}
@@ -474,7 +562,7 @@
where
S: GraphStorage,
{
- id: S::NodeId,
+ id: NodeId,
weight: &'a mut S::NodeWeight,
}
@@ -498,7 +586,7 @@
///
/// [`Graph::node_mut`]: crate::graph::Graph::node_mut
/// [`Graph::insert_node`]: crate::graph::Graph::insert_node
- pub fn new(id: S::NodeId, weight: &'a mut S::NodeWeight) -> Self {
+ pub fn new(id: NodeId, weight: &'a mut S::NodeWeight) -> Self {
Self { id, weight }
}
@@ -523,7 +611,7 @@
/// assert_eq!(node.id(), &a);
/// ```
#[must_use]
- pub const fn id(&self) -> S::NodeId {
+ pub const fn id(&self) -> NodeId {
self.id
}
@@ -569,6 +657,27 @@
pub fn weight_mut(&mut self) -> &mut S::NodeWeight {
self.weight
}
+
+ /// Change the underlying storage of the node.
+ ///
+ /// Should only be used when layering multiple [`GraphStorage`] implementations on top of each
+ /// other.
+ ///
+ /// # Note
+ ///
+ /// This should not lead to any undefined behaviour, but might have unintended consequences if
+ /// the storage does not recognize the inner id as valid.
+ /// You should only use this if you know what you are doing.
+ #[must_use]
+ pub fn change_storage_unchecked<T>(self) -> NodeMut<'a, T>
+ where
+ T: GraphStorage<NodeWeight = S::NodeWeight>,
+ {
+ NodeMut {
+ id: self.id,
+ weight: self.weight,
+ }
+ }
}
impl<S> NodeMut<'_, S>
@@ -607,7 +716,7 @@
///
/// [`Graph::from_parts`]: crate::graph::Graph::from_parts
#[must_use]
- pub fn detach(&self) -> DetachedNode<S::NodeId, S::NodeWeight> {
+ pub fn detach(&self) -> DetachedNode<S::NodeWeight> {
DetachedNode::new(self.id, self.weight.clone())
}
}
@@ -640,15 +749,15 @@
/// [`Graph::into_parts`]: crate::graph::Graph::into_parts
/// [`Graph::from_parts`]: crate::graph::Graph::from_parts
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub struct DetachedNode<N, W> {
+pub struct DetachedNode<W> {
/// The unique id of the node.
- pub id: N,
+ pub id: NodeId,
/// The weight of the node.
pub weight: W,
}
-impl<N, W> DetachedNode<N, W> {
+impl<W> DetachedNode<W> {
/// Creates a new detached node.
///
/// # Example
@@ -658,7 +767,7 @@
///
/// let node = DetachedNode::new(0, "A");
/// ```
- pub const fn new(id: N, weight: W) -> Self {
+ pub const fn new(id: NodeId, weight: W) -> Self {
Self { id, weight }
}
}
diff --git a/crates/core/src/storage/auxiliary.rs b/crates/core/src/storage/auxiliary.rs
new file mode 100644
index 0000000..596aa67
--- /dev/null
+++ b/crates/core/src/storage/auxiliary.rs
@@ -0,0 +1,122 @@
+//! Auxiliary storage for graphs.
+//!
+//! This module provides traits for auxiliary storage for graphs, which can be used to associate
+//! arbitrary additional data with nodes and edges.
+use crate::{edge::EdgeId, node::NodeId};
+
+/// Hints about frequency.
+///
+/// These hints are used to optimize the performance of secondary storage, specifically these are
+/// used to tell how frequently data is expected to be accessed.
+#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub enum FrequencyHint {
+ /// Items are accessed frequently.
+ #[default]
+ Frequent,
+ /// Items are accessed infrequently.
+ Infrequent,
+}
+
+/// Hints about performance.
+///
+/// These hints are used to optimize the performance of secondary storage, specifically these are
+/// used to tell how frequently data is expected to be accessed.
+#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct PerformanceHint {
+ /// Hints about the frequency of reads.
+ pub read: FrequencyHint,
+ /// Hints about the frequency of writes.
+ pub write: FrequencyHint,
+}
+
+/// Hints about occupancy.
+///
+/// These hints are used to optimize the performance of secondary storage, specifically these are
+/// used to tell how much space is expected to be filled with data.
+#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub enum OccupancyHint {
+ /// Storage is expected to be densely populated.
+ #[default]
+ Dense,
+ /// Storage is expected to be sparsely populated.
+ Sparse,
+}
+
+/// Hints for secondary storage.
+///
+/// These hints are used to optimize the performance of secondary storage.
+///
+/// These hints are not guaranteed to be respected, and are used as a best-effort heuristic.
+#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct Hints {
+ /// Hints about performance.
+ pub performance: PerformanceHint,
+ /// Hints about occupancy.
+ pub occupancy: OccupancyHint,
+}
+
+/// Secondary storage for a graph.
+///
+/// This trait is used to provide secondary storage for a graph to associate arbitrary additional
+/// data with nodes and edges.
+///
+/// If you want to only store boolean values for nodes and edges, you can use the
+/// [`BooleanGraphStorage`] trait instead, which usually has a more efficient implementation.
+pub trait SecondaryGraphStorage<K, V> {
+ type Iter<'a>: Iterator<Item = (K, &'a V)>
+ where
+ V: 'a,
+ Self: 'a;
+
+ fn get(&self, id: K) -> Option<&V>;
+ fn get_mut(&mut self, id: K) -> Option<&mut V>;
+
+ fn set(&mut self, id: K, value: V) -> Option<V>;
+ fn remove(&mut self, id: K) -> Option<V>;
+
+ fn iter(&self) -> Self::Iter<'_>;
+}
+
+/// Secondary storage for a graph.
+///
+/// This trait is used to provide secondary storage for a graph to associate boolean values with
+/// nodes and edges.
+pub trait BooleanGraphStorage<K> {
+ fn get(&self, id: K) -> Option<bool>;
+
+ fn set(&mut self, id: K, flag: bool) -> Option<bool>;
+
+ fn fill(&mut self, flag: bool);
+}
+
+/// Auxiliary storage for a graph.
+///
+/// Provides secondary storage for a graph to associate arbitrary additional data with nodes and
+/// edges, as well as boolean values with nodes and edges.
+///
+/// For boolean values prefer [`Self::boolean_edge_storage`] and [`Self::boolean_node_storage`],
+/// as they usually have a more efficient implementation.
+pub trait AuxiliaryGraphStorage {
+ type BooleanEdgeStorage<'graph>: BooleanGraphStorage<EdgeId>
+ where
+ Self: 'graph;
+
+ type BooleanNodeStorage<'graph>: BooleanGraphStorage<NodeId>
+ where
+ Self: 'graph;
+
+ type SecondaryEdgeStorage<'graph, V>: SecondaryGraphStorage<EdgeId, V>
+ where
+ Self: 'graph;
+
+ type SecondaryNodeStorage<'graph, V>: SecondaryGraphStorage<NodeId, V>
+ where
+ Self: 'graph;
+
+ fn secondary_node_storage<V>(&self, hints: Hints) -> Self::SecondaryNodeStorage<'_, V>;
+ fn secondary_edge_storage<V>(&self, hints: Hints) -> Self::SecondaryEdgeStorage<'_, V>;
+
+ fn boolean_node_storage(&self, hints: Hints) -> Self::BooleanNodeStorage<'_>;
+
+ fn boolean_edge_storage(&self, hints: Hints) -> Self::BooleanEdgeStorage<'_>;
+}
diff --git a/crates/core/src/storage/directed.rs b/crates/core/src/storage/directed.rs
index 8837fb6..0a7387f 100644
--- a/crates/core/src/storage/directed.rs
+++ b/crates/core/src/storage/directed.rs
@@ -1,6 +1,6 @@
use crate::{
edge::{Direction, Edge, EdgeMut},
- node::{Node, NodeMut},
+ node::{Node, NodeId, NodeMut},
storage::GraphStorage,
};
@@ -75,8 +75,8 @@
/// Most implementations should be able to provide a more efficient implementation.
fn directed_edges_between(
&self,
- source: Self::NodeId,
- target: Self::NodeId,
+ source: NodeId,
+ target: NodeId,
) -> impl Iterator<Item = Edge<'_, Self>> {
self.node_directed_connections(source, Direction::Outgoing)
.filter(move |edge| edge.target_id() == target)
@@ -130,8 +130,8 @@
/// Most implementations should be able to provide a more efficient implementation.
fn directed_edges_between_mut(
&mut self,
- source: Self::NodeId,
- target: Self::NodeId,
+ source: NodeId,
+ target: NodeId,
) -> impl Iterator<Item = EdgeMut<'_, Self>> {
self.node_directed_connections_mut(source, Direction::Outgoing)
.filter(move |edge| edge.target_id() == target)
@@ -191,7 +191,7 @@
/// ```
fn node_directed_connections(
&self,
- id: Self::NodeId,
+ id: NodeId,
direction: Direction,
) -> impl Iterator<Item = Edge<'_, Self>>;
@@ -245,7 +245,7 @@
/// ```
fn node_directed_connections_mut(
&mut self,
- id: Self::NodeId,
+ id: NodeId,
direction: Direction,
) -> impl Iterator<Item = EdgeMut<'_, Self>>;
@@ -280,7 +280,7 @@
/// assert_eq!(storage.node_directed_degree(&a, Direction::Outgoing), 1);
/// assert_eq!(storage.node_directed_degree(&a, Direction::Incoming), 1);
/// ```
- fn node_directed_degree(&self, id: Self::NodeId, direction: Direction) -> usize {
+ fn node_directed_degree(&self, id: NodeId, direction: Direction) -> usize {
self.node_directed_connections(id, direction).count()
}
@@ -345,7 +345,7 @@
/// and must uphold the contract that the returned iterator does not contain duplicates.
fn node_directed_neighbours(
&self,
- id: Self::NodeId,
+ id: NodeId,
direction: Direction,
) -> impl Iterator<Item = Node<'_, Self>> {
self.node_directed_connections(id, direction)
@@ -414,7 +414,7 @@
// I'd love to provide a default implementation for this, but I just can't get it to work.
fn node_directed_neighbours_mut(
&mut self,
- id: Self::NodeId,
+ id: NodeId,
direction: Direction,
) -> impl Iterator<Item = NodeMut<'_, Self>>;
}
diff --git a/crates/core/src/storage/mod.rs b/crates/core/src/storage/mod.rs
index c7ce1cc..0d1910d 100644
--- a/crates/core/src/storage/mod.rs
+++ b/crates/core/src/storage/mod.rs
@@ -13,6 +13,9 @@
//! to an undirected graph.
//! - [`DirectedGraphStorage`]: A trait for directed graph storage implementations.
//! - [`RetainableGraphStorage`]: A trait for retainable graph storage implementations.
+//! - [`AuxiliaryGraphStorage`]: A trait to access storage for arbitrary additional data.
+//! - [`SequentialGraphStorage`]: A trait for graph storage implementations that allow the mapping
+//! of their internal indices to a set of linear indices.
//!
//! [`GraphStorage`] proposes that [`DirectedGraphStorage`] is simply a specialization of an
//! undirected graph, meaning that the supertrait of [`DirectedGraphStorage`] is also
@@ -21,20 +24,27 @@
//!
//! # Implementation Notes
//!
-//! [`RetainableGraphStorage`] is subject to removal during the alpha period.
+//! * [`RetainableGraphStorage`] is subject to removal during the alpha period.
+//! * [`SequentialGraphStorage`] is subject to removal or rename during the alpha period.
+//! * [`AuxiliaryGraphStorage`] is subject to removal or rename during the alpha period.
//!
//! [`Graph`]: crate::graph::Graph
mod directed;
+pub mod auxiliary;
mod retain;
+pub mod reverse;
+pub mod sequential;
use error_stack::{Context, Result};
-pub use self::{directed::DirectedGraphStorage, retain::RetainableGraphStorage};
+pub use self::{
+ auxiliary::AuxiliaryGraphStorage, directed::DirectedGraphStorage,
+ retain::RetainableGraphStorage, sequential::SequentialGraphStorage,
+};
use crate::{
- edge::{DetachedEdge, Edge, EdgeMut},
- id::GraphId,
- node::{DetachedNode, Node, NodeMut},
+ edge::{DetachedEdge, Edge, EdgeId, EdgeMut},
+ node::{DetachedNode, Node, NodeId, NodeMut},
};
/// A trait for graph storage implementations.
@@ -115,39 +125,9 @@
/// [`Graph::new`]: crate::graph::Graph::new
/// [`Graph::new_in`]: crate::graph::Graph::new_in
/// [`Graph::with_capacity`]: crate::graph::Graph::with_capacity
-pub trait GraphStorage: Sized {
- /// The unique identifier for an edge.
- ///
- /// This is used to identify edges in the graph.
- /// The equivalent in a `HashMap` would be the key.
- /// In contrast to a `HashMap` (or similar data structures) the trait does not enforce any
- /// additional constraints,
- /// implementations are free to choose to limit identifiers to a certain subset if required, or
- /// choose a concrete type.
- ///
- /// Fundamentally, while [`Self::EdgeId`] must be of type [`GraphId`], the chosen
- /// [`Self::EdgeId`] of an implementation should either implement [`ManagedGraphId`] or
- /// [`ArbitraryGraphId`], which work as marker traits.
- ///
- /// [`ManagedGraphId`] indicates that the implementation manages the identifiers, subsequently,
- /// users are not allowed to create identifiers themselves.
- /// [`ArbitraryGraphId`] indicates that the implementation does not manage the identifiers, and
- /// users are allowed to create identifiers themselves.
- /// This is reflected in the API through the [`Attributes`] type, which needs to be supplied
- /// when creating edges, if the implementation does not manage the identifiers, [`Attributes`]
- /// will allow users to specify the identifier and weight of the edge, while if the
- /// implementation manages the identifiers, [`Attributes`] will only allow users to specify the
- /// weight of the edge.
- ///
- /// [`Attributes`]: crate::attributes::Attributes
- /// [`ManagedGraphId`]: crate::id::ManagedGraphId
- /// [`ArbitraryGraphId`]: crate::id::ArbitraryGraphId
- type EdgeId: GraphId;
-
+pub trait GraphStorage: SequentialGraphStorage + AuxiliaryGraphStorage + Sized {
/// The weight of an edge.
///
- /// This works in tandem with [`Self::EdgeId`], and is used to store the weight of an edge.
- /// The equivalent in a `HashMap` would be the value.
/// No constraints are enforced on this type (except that it needs to be `Sized`), but
/// implementations _may_ choose to enforce additional constraints, or limit the type to a
/// specific concrete type.
@@ -171,38 +151,8 @@
/// [`Report`]: error_stack::Report
type Error: Context;
- /// The unique identifier for a node.
- ///
- /// This is used to identify nodes in the graph.
- /// The equivalent in a `HashMap` would be the key.
- /// In contrast to a `HashMap` (or similar data structures) the trait does not enforce any
- /// additional constraints,
- /// implementations are free to choose to limit identifiers to a certain subset if required, or
- /// choose a concrete type.
- ///
- /// Fundamentally, while [`Self::NodeId`] must be of type [`GraphId`], the chosen
- /// [`Self::NodeId`] of an implementation should either implement [`ManagedGraphId`] or
- /// [`ArbitraryGraphId`], which work as marker traits.
- ///
- /// [`ManagedGraphId`] indicates that the implementation manages the identifiers, subsequently,
- /// users are not allowed to create identifiers themselves.
- /// [`ArbitraryGraphId`] indicates that the implementation does not manage the identifiers, and
- /// users are allowed to create identifiers themselves.
- /// This is reflected in the API through the [`Attributes`] type, which needs to be supplied
- /// when creating a node, if the implementation does not manage the identifiers, [`Attributes`]
- /// will allow users to specify the identifier and weight of the node, while if the
- /// implementation manages the identifiers, [`Attributes`] will only allow users to specify the
- /// weight of the node.
- ///
- /// [`Attributes`]: crate::attributes::Attributes
- /// [`ManagedGraphId`]: crate::id::ManagedGraphId
- /// [`ArbitraryGraphId`]: crate::id::ArbitraryGraphId
- type NodeId: GraphId;
-
/// The weight of a node.
///
- /// This works in tandem with [`Self::NodeId`], and is used to store the weight of a node.
- /// The equivalent in a `HashMap` would be the value.
/// No constraints are enforced on this type (except that it needs to be `Sized`), but
/// implementations _may_ choose to enforce additional constraints, or limit the type to a
/// specific concrete type.
@@ -243,13 +193,9 @@
/// This is the reverse operation of [`Self::into_parts`], which converts the current graph
/// storage into an iterable of nodes and edges.
///
- /// The ordering of the nodes and edges in the resulting graph storage is not preserved, if that
- /// is the case in a storage implementation it should be considered an implementation
- /// detail and not be relied upon.
- /// The same applies to identifiers of nodes and edges, which may be changed during
- /// construction.
- /// The only properties that can be relied upon is that all nodes and edges will be present,
- /// their weights will be the same, and that the graph will be structurally identical.
+ /// This process is lossy, neither the node ids or the edge ids are guaranteed to be preserved.
+ /// This function only guarantees that the weights of the nodes and edges are preserved as well
+ /// as the structure of the graph.
///
/// # Example
///
@@ -294,10 +240,12 @@
///
/// Implementations may choose to override this default implementation, but should try to also
/// be fail-slow.
+ // TODO: additionally should return a mapping!
fn from_parts(
- nodes: impl IntoIterator<Item = DetachedNode<Self::NodeId, Self::NodeWeight>>,
- edges: impl IntoIterator<Item = DetachedEdge<Self::EdgeId, Self::NodeId, Self::EdgeWeight>>,
+ nodes: impl IntoIterator<Item = DetachedNode<Self::NodeWeight>>,
+ edges: impl IntoIterator<Item = DetachedEdge<Self::EdgeWeight>>,
) -> Result<Self, Self::Error> {
+ // TODO: rework this!
let nodes = nodes.into_iter();
let edges = edges.into_iter();
@@ -386,8 +334,8 @@
fn into_parts(
self,
) -> (
- impl Iterator<Item = DetachedNode<Self::NodeId, Self::NodeWeight>>,
- impl Iterator<Item = DetachedEdge<Self::EdgeId, Self::NodeId, Self::EdgeWeight>>,
+ impl Iterator<Item = DetachedNode<Self::NodeWeight>>,
+ impl Iterator<Item = DetachedEdge<Self::EdgeWeight>>,
);
/// Returns the number of nodes in the graph.
@@ -465,9 +413,6 @@
/// and is instead used by the [`Graph`] type to generate a new identifier that is then used
/// during [`Graph::insert_node`].
///
- /// This function is only of interest for implementations that manage the identifiers of nodes
- /// (using the [`ManagedGraphId`] marker trait).
- ///
/// # Example
///
/// ```
@@ -500,18 +445,7 @@
///
/// The implementation of this function must also be fast, as it is called every time a new node
/// is inserted and must be pure, meaning that it must not have any side-effects.
- ///
- /// If the [`Self::NodeId`] is a [`ManagedGraphId`], the implementation of this function must
- /// return [`Self::NodeId`] and must not take `attribute` into account (in fact it can't, as the
- /// value is always [`NoValue`]). Should the [`ArbitraryGraphId`] marker trait be implemented,
- /// this function should effectively be a no-op and given `attribute`.
- ///
- /// [`Graph`]: crate::graph::Graph
- /// [`Graph::insert_node`]: crate::graph::Graph::insert_node
- /// [`ManagedGraphId`]: crate::id::ManagedGraphId
- /// [`ArbitraryGraphId`]: crate::id::ArbitraryGraphId
- /// [`NoValue`]: crate::attributes::NoValue
- fn next_node_id(&self, attribute: <Self::NodeId as GraphId>::AttributeIndex) -> Self::NodeId;
+ fn next_node_id(&self) -> NodeId;
/// Inserts a new node into the graph.
///
@@ -537,7 +471,7 @@
/// constraints (depending on the implementation) are violated.
fn insert_node(
&mut self,
- id: Self::NodeId,
+ id: NodeId,
weight: Self::NodeWeight,
) -> Result<NodeMut<Self>, Self::Error>;
@@ -548,9 +482,6 @@
/// and is instead used by the [`Graph`] type to generate a new identifier that is then used
/// during [`Graph::insert_edge`].
///
- /// This function is only of interest for implementations that manage the identifiers of edges
- /// (using the [`ManagedGraphId`] marker trait).
- ///
/// # Example
///
/// ```
@@ -573,8 +504,7 @@
///
/// [`Graph`]: crate::graph::Graph
/// [`Graph::insert_edge`]: crate::graph::Graph::insert_edge
- /// [`ManagedGraphId`]: crate::id::ManagedGraphId
- fn next_edge_id(&self, attribute: <Self::EdgeId as GraphId>::AttributeIndex) -> Self::EdgeId;
+ fn next_edge_id(&self) -> EdgeId;
/// Inserts a new edge into the graph.
///
@@ -611,11 +541,11 @@
/// but some implementations may choose to allow parallel edges.
fn insert_edge(
&mut self,
- id: Self::EdgeId,
+ id: EdgeId,
weight: Self::EdgeWeight,
- u: Self::NodeId,
- v: Self::NodeId,
+ u: NodeId,
+ v: NodeId,
) -> Result<EdgeMut<Self>, Self::Error>;
/// Removes the node with the given identifier from the graph.
@@ -680,10 +610,7 @@
///
/// storage.remove_node(&a).unwrap();
/// ```
- fn remove_node(
- &mut self,
- id: Self::NodeId,
- ) -> Option<DetachedNode<Self::NodeId, Self::NodeWeight>>;
+ fn remove_node(&mut self, id: NodeId) -> Option<DetachedNode<Self::NodeWeight>>;
/// Removes the edge with the given identifier from the graph.
///
@@ -714,10 +641,7 @@
/// #
/// # assert_eq!(storage.num_edges(), 0);
/// ```
- fn remove_edge(
- &mut self,
- id: Self::EdgeId,
- ) -> Option<DetachedEdge<Self::EdgeId, Self::NodeId, Self::EdgeWeight>>;
+ fn remove_edge(&mut self, id: EdgeId) -> Option<DetachedEdge<Self::EdgeWeight>>;
/// Clears the graph, removing all nodes and edges.
///
@@ -773,7 +697,7 @@
/// // This will access the underlying storage, and is equivalent to `storage.neighbours(&a)`.
/// assert_eq!(node.neighbours().count(), 0);
/// ```
- fn node(&self, id: Self::NodeId) -> Option<Node<Self>>;
+ fn node(&self, id: NodeId) -> Option<Node<Self>>;
/// Returns the node, with a mutable weight, with the given identifier.
///
@@ -797,7 +721,7 @@
/// assert_eq!(node.id(), &a);
/// assert_eq!(node.weight(), &mut 1);
/// ```
- fn node_mut(&mut self, id: Self::NodeId) -> Option<NodeMut<Self>>;
+ fn node_mut(&mut self, id: NodeId) -> Option<NodeMut<Self>>;
/// Checks if the node with the given identifier exists.
///
@@ -825,7 +749,7 @@
/// The default implementation simply checks if [`Self::node`] returns [`Some`], but if
/// possible, custom implementations that are able to do this more efficiently should override
/// this.
- fn contains_node(&self, id: Self::NodeId) -> bool {
+ fn contains_node(&self, id: NodeId) -> bool {
self.node(id).is_some()
}
@@ -872,7 +796,7 @@
/// assert_eq!(edge.target().id(), &b);
/// assert_eq!(edge.target().weight(), &2);
/// ```
- fn edge(&self, id: Self::EdgeId) -> Option<Edge<Self>>;
+ fn edge(&self, id: EdgeId) -> Option<Edge<Self>>;
/// Returns the edge, with a mutable weight, with the given identifier, if it exists.
///
@@ -905,7 +829,7 @@
///
/// assert_eq!(storage.edge(&ab).unwrap().weight(), &4);
/// ```
- fn edge_mut(&mut self, id: Self::EdgeId) -> Option<EdgeMut<Self>>;
+ fn edge_mut(&mut self, id: EdgeId) -> Option<EdgeMut<Self>>;
/// Checks if the edge with the given identifier exists.
///
@@ -938,7 +862,7 @@
/// The default implementation simply checks if [`Self::edge`] returns [`Some`], but if
/// possible, custom implementations that are able to do this more efficiently should override
/// this.
- fn contains_edge(&self, id: Self::EdgeId) -> bool {
+ fn contains_edge(&self, id: EdgeId) -> bool {
self.edge(id).is_some()
}
@@ -981,11 +905,7 @@
/// The default implementation simply calls [`Self::node_connections`] on both nodes and then
/// chains those, after filtering for the respective end. Most implementations should be able to
/// provide a more efficient implementation.
- fn edges_between(
- &self,
- u: Self::NodeId,
- v: Self::NodeId,
- ) -> impl Iterator<Item = Edge<'_, Self>> {
+ fn edges_between(&self, u: NodeId, v: NodeId) -> impl Iterator<Item = Edge<'_, Self>> {
// How does this work with a default implementation?
let from_source = self.node_connections(u).filter(move |edge| {
let (edge_u, edge_v) = edge.endpoint_ids();
@@ -1052,8 +972,8 @@
// I'd love to provide a default implementation for this, but I just can't get it to work.
fn edges_between_mut(
&mut self,
- u: Self::NodeId,
- v: Self::NodeId,
+ u: NodeId,
+ v: NodeId,
) -> impl Iterator<Item = EdgeMut<'_, Self>>;
/// Returns an iterator over all edges that are connected to the given node.
@@ -1094,7 +1014,7 @@
/// [ab, ca]
/// );
/// ```
- fn node_connections(&self, id: Self::NodeId) -> impl Iterator<Item = Edge<'_, Self>>;
+ fn node_connections(&self, id: NodeId) -> impl Iterator<Item = Edge<'_, Self>>;
/// Returns an iterator over all edges that are connected to the given node, with mutable
/// weights.
@@ -1139,8 +1059,7 @@
/// [5, 6]
/// );
/// ```
- fn node_connections_mut(&mut self, id: Self::NodeId)
- -> impl Iterator<Item = EdgeMut<'_, Self>>;
+ fn node_connections_mut(&mut self, id: NodeId) -> impl Iterator<Item = EdgeMut<'_, Self>>;
/// Returns the number of edges that are connected to the given node.
///
@@ -1176,7 +1095,7 @@
/// edges.
/// This is unlikely to be the most efficient implementation, so custom implementations should
/// override this.
- fn node_degree(&self, id: Self::NodeId) -> usize {
+ fn node_degree(&self, id: NodeId) -> usize {
self.node_connections(id).count()
}
@@ -1234,7 +1153,7 @@
///
/// Changing the requirement from **SHOULD NOT** to **MUST NOT** may occur in the future, and is
/// to be considered a breaking change.
- fn node_neighbours(&self, id: Self::NodeId) -> impl Iterator<Item = Node<'_, Self>> {
+ fn node_neighbours(&self, id: NodeId) -> impl Iterator<Item = Node<'_, Self>> {
self.node_connections(id)
.filter_map(move |edge: Edge<Self>| {
let (u, v) = edge.endpoint_ids();
@@ -1295,7 +1214,7 @@
///
/// No default implementation is provided, as a mutable iterator based on
/// [`Self::node_connections_mut`] could potentially lead to a double mutable borrow.
- fn node_neighbours_mut(&mut self, id: Self::NodeId) -> impl Iterator<Item = NodeMut<'_, Self>>;
+ fn node_neighbours_mut(&mut self, id: NodeId) -> impl Iterator<Item = NodeMut<'_, Self>>;
/// Returns an iterator over all nodes that do not have any edges connected to them.
///
diff --git a/crates/core/src/storage/reverse.rs b/crates/core/src/storage/reverse.rs
new file mode 100644
index 0000000..11ebb54
--- /dev/null
+++ b/crates/core/src/storage/reverse.rs
@@ -0,0 +1,18 @@
+use crate::{Edge, EdgeMut, GraphStorage, Node, NodeMut};
+
+pub trait ReverseGraphStorage: GraphStorage {
+ type NodeKey;
+ type EdgeKey;
+
+ fn contains_node_key(&self, key: &Self::NodeKey) -> bool {
+ self.node_by_key(key).is_some()
+ }
+ fn node_by_key(&self, key: &Self::NodeKey) -> Option<Node<Self>>;
+ fn node_by_key_mut(&mut self, key: &Self::NodeKey) -> Option<NodeMut<Self>>;
+
+ fn contains_edge_key(&self, key: &Self::EdgeKey) -> bool {
+ self.edge_by_key(key).is_some()
+ }
+ fn edge_by_key(&self, key: &Self::EdgeKey) -> Option<Edge<Self>>;
+ fn edge_by_key_mut(&mut self, key: &Self::EdgeKey) -> Option<EdgeMut<Self>>;
+}
diff --git a/crates/core/src/id/linear.rs b/crates/core/src/storage/sequential.rs
similarity index 78%
rename from crates/core/src/id/linear.rs
rename to crates/core/src/storage/sequential.rs
index f36e349..e64dd04 100644
--- a/crates/core/src/id/linear.rs
+++ b/crates/core/src/storage/sequential.rs
@@ -1,6 +1,4 @@
-use numi::borrow::Moo;
-
-use crate::{id::GraphId, storage::GraphStorage};
+use crate::{edge::EdgeId, node::NodeId};
/// Index mapper for a graph.
///
@@ -16,7 +14,7 @@
/// input value should always map to the same output value.
/// Index lookup should also be (if possible) `O(1)` for `Id -> usize`, but not necessarily for
/// `usize -> Id`.
-pub trait IndexMapper<Id> {
+pub trait GraphIdBijection<Id> {
/// The maximum value that can be mapped to.
///
/// This **must** be equal to the number of nodes in the graph.
@@ -105,14 +103,13 @@
///
/// ```
/// use numi::borrow::Moo;
- /// use petgraph_core::id::{IndexMapper, LinearGraphId};
/// use petgraph_dino::{DiDinoGraph, NodeId};
///
/// let mut graph = DiDinoGraph::new();
///
/// let a = *graph.insert_node("A").id();
/// let b = *graph.insert_node("B").id();
- /// # let ab = graph.insert_edge("A → B", &a, &b);
+ /// # let ab = graph.insert_edge("A → B", a, b);
///
/// let mut mapper = NodeId::index_mapper(graph.storage());
///
@@ -122,34 +119,15 @@
fn reverse(&self, to: usize) -> Option<Id>;
}
-/// Linear graph identifier.
-///
-/// A linear graph identifier is a graph identifier that has a linear mapping to a `usize` value,
-/// that mapping must be continuous .
-pub trait LinearGraphId<S>: GraphId + Sized
-where
- S: GraphStorage,
-{
- /// The index mapper for this graph identifier.
- type Mapper<'graph>: IndexMapper<Self>
+pub trait SequentialGraphStorage {
+ type EdgeIdBijection<'graph>: GraphIdBijection<EdgeId>
where
- S: 'graph;
+ Self: 'graph;
- /// Get the index mapper for this graph identifier.
- ///
- /// # Example
- ///
- /// ```
- /// use petgraph_core::id::{IndexMapper, LinearGraphId};
- /// use petgraph_dino::{DiDinoGraph, NodeId};
- ///
- /// let mut graph = DiDinoGraph::new();
- ///
- /// let a = *graph.insert_node("A").id();
- /// let b = *graph.insert_node("B").id();
- /// # let ab = graph.insert_edge("A → B", &a, &b);
- ///
- /// let mapper = NodeId::index_mapper(graph.storage());
- /// ```
- fn index_mapper(storage: &S) -> Self::Mapper<'_>;
+ type NodeIdBijection<'graph>: GraphIdBijection<NodeId>
+ where
+ Self: 'graph;
+
+ fn node_id_bijection(&self) -> Self::NodeIdBijection<'_>;
+ fn edge_id_bijection(&self) -> Self::EdgeIdBijection<'_>;
}
diff --git a/crates/dino/src/auxiliary.rs b/crates/dino/src/auxiliary.rs
new file mode 100644
index 0000000..82aa1ee
--- /dev/null
+++ b/crates/dino/src/auxiliary.rs
@@ -0,0 +1,37 @@
+use petgraph_core::{
+ edge::EdgeId,
+ node::NodeId,
+ storage::{auxiliary::Hints, AuxiliaryGraphStorage},
+ GraphDirectionality,
+};
+
+use crate::{
+ slab::secondary::{SlabBooleanStorage, SlabSecondaryStorage},
+ DinoStorage,
+};
+
+impl<N, E, D> AuxiliaryGraphStorage for DinoStorage<N, E, D>
+where
+ D: GraphDirectionality,
+{
+ type BooleanEdgeStorage<'graph> = SlabBooleanStorage<'graph, EdgeId> where Self: 'graph;
+ type BooleanNodeStorage<'graph> = SlabBooleanStorage<'graph, NodeId> where Self: 'graph;
+ type SecondaryEdgeStorage<'graph, V> = SlabSecondaryStorage<'graph, EdgeId, V> where Self: 'graph;
+ type SecondaryNodeStorage<'graph, V> = SlabSecondaryStorage<'graph, NodeId, V> where Self: 'graph;
+
+ fn secondary_node_storage<V>(&self, _: Hints) -> Self::SecondaryNodeStorage<'_, V> {
+ SlabSecondaryStorage::new(&self.nodes)
+ }
+
+ fn secondary_edge_storage<V>(&self, _: Hints) -> Self::SecondaryEdgeStorage<'_, V> {
+ SlabSecondaryStorage::new(&self.edges)
+ }
+
+ fn boolean_node_storage(&self, _: Hints) -> Self::BooleanNodeStorage<'_> {
+ SlabBooleanStorage::new(&self.nodes)
+ }
+
+ fn boolean_edge_storage(&self, _: Hints) -> Self::BooleanEdgeStorage<'_> {
+ SlabBooleanStorage::new(&self.edges)
+ }
+}
diff --git a/crates/dino/src/closure/mod.rs b/crates/dino/src/closure/mod.rs
index fa2f55a..d3bb255 100644
--- a/crates/dino/src/closure/mod.rs
+++ b/crates/dino/src/closure/mod.rs
@@ -4,7 +4,7 @@
use crate::{
edge::{Edge, EdgeSlab},
node::{Node, NodeSlab},
- EdgeId, NodeId,
+ NodeId,
};
#[derive(Debug, Copy, Clone, PartialEq)]
@@ -116,13 +116,12 @@
use hashbrown::{HashMap, HashSet};
use petgraph_core::{
- attributes::Attributes, edge::marker::Directed, GraphDirectionality, GraphStorage,
+ edge::{marker::Directed, EdgeId},
+ node::NodeId,
+ GraphDirectionality,
};
- use crate::{
- slab::{EntryId, Key as _},
- DinoGraph, DinoStorage, EdgeId, NodeId,
- };
+ use crate::{DinoGraph, DinoStorage};
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct EvaluatedNodeClosure {
@@ -233,7 +232,7 @@
let mut graph = DinoGraph::<u8, u8, Directed>::new();
let node = graph.try_insert_node(1).unwrap();
- let id = *node.id();
+ let id = node.id();
assert_eq!(isolated(&graph), once(id).collect());
@@ -285,11 +284,11 @@
fn multiple_nodes() {
let mut graph = DinoGraph::<u8, u8, Directed>::new();
- let a = graph.try_insert_node(Attributes::new(1)).unwrap();
- let a = *a.id();
+ let a = graph.try_insert_node(1).unwrap();
+ let a = a.id();
- let b = graph.try_insert_node(Attributes::new(2)).unwrap();
- let b = *b.id();
+ let b = graph.try_insert_node(2).unwrap();
+ let b = b.id();
assert_eq!(isolated(&graph), [a, b].into_iter().collect());
@@ -346,13 +345,13 @@
let mut graph = DinoGraph::<u8, u8, Directed>::new();
let a = graph.try_insert_node(1u8).unwrap();
- let a = *a.id();
+ let a = a.id();
let b = graph.try_insert_node(1u8).unwrap();
- let b = *b.id();
+ let b = b.id();
- let edge = graph.try_insert_edge(1u8, &a, &b).unwrap();
- let edge = *edge.id();
+ let edge = graph.try_insert_edge(1u8, a, b).unwrap();
+ let edge = edge.id();
assert!(isolated(&graph).is_empty());
@@ -411,10 +410,10 @@
let mut graph = DinoGraph::<u8, u8, Directed>::new();
let a = graph.try_insert_node(1u8).unwrap();
- let a = *a.id();
+ let a = a.id();
- let edge = graph.try_insert_edge(1u8, &a, &a).unwrap();
- let edge = *edge.id();
+ let edge = graph.try_insert_edge(1u8, a, a).unwrap();
+ let edge = edge.id();
assert!(isolated(&graph).is_empty());
@@ -469,22 +468,22 @@
let mut graph = DinoGraph::<u8, u8, Directed>::new();
let a = graph.try_insert_node(1u8).unwrap();
- let a = *a.id();
+ let a = a.id();
let b = graph.try_insert_node(1u8).unwrap();
- let b = *b.id();
+ let b = b.id();
let c = graph.try_insert_node(1u8).unwrap();
- let c = *c.id();
+ let c = c.id();
- let ab = graph.try_insert_edge(1u8, &a, &b).unwrap();
- let ab = *ab.id();
+ let ab = graph.try_insert_edge(1u8, a, b).unwrap();
+ let ab = ab.id();
- let bc = graph.try_insert_edge(1u8, &b, &c).unwrap();
- let bc = *bc.id();
+ let bc = graph.try_insert_edge(1u8, b, c).unwrap();
+ let bc = bc.id();
- let ca = graph.try_insert_edge(1u8, &c, &a).unwrap();
- let ca = *ca.id();
+ let ca = graph.try_insert_edge(1u8, c, a).unwrap();
+ let ca = ca.id();
Self {
graph,
@@ -592,16 +591,16 @@
let mut graph = DinoGraph::<u8, u8, Directed>::new();
let a = graph.try_insert_node(1u8).unwrap();
- let a = *a.id();
+ let a = a.id();
let b = graph.try_insert_node(1u8).unwrap();
- let b = *b.id();
+ let b = b.id();
- let ab1 = graph.try_insert_edge(1u8, &a, &b).unwrap();
- let ab1 = *ab1.id();
+ let ab1 = graph.try_insert_edge(1u8, a, b).unwrap();
+ let ab1 = ab1.id();
- let ab2 = graph.try_insert_edge(1u8, &a, &b).unwrap();
- let ab2 = *ab2.id();
+ let ab2 = graph.try_insert_edge(1u8, a, b).unwrap();
+ let ab2 = ab2.id();
assert!(isolated(&graph).is_empty());
@@ -669,7 +668,7 @@
..
} = graph;
- graph.remove_node(&b).unwrap();
+ graph.remove_node(b).unwrap();
assert!(isolated(&graph).is_empty());
@@ -738,7 +737,7 @@
ca,
} = graph;
- graph.remove_edge(&bc).unwrap();
+ graph.remove_edge(bc).unwrap();
assert!(isolated(&graph).is_empty());
diff --git a/crates/dino/src/directed.rs b/crates/dino/src/directed.rs
index 7ae82ac..5759d2c 100644
--- a/crates/dino/src/directed.rs
+++ b/crates/dino/src/directed.rs
@@ -1,14 +1,14 @@
use either::Either;
use petgraph_core::{
- edge::{Direction, Edge, EdgeMut},
- node::{Node, NodeMut},
+ edge::{Direction, Edge, EdgeId, EdgeMut},
+ node::{Node, NodeId, NodeMut},
storage::{DirectedGraphStorage, GraphStorage},
};
use crate::{
iter::directed::NodeDirectedConnectionsIter,
node::{NodeClosures, NodeSlab},
- DinoStorage, Directed, EdgeId, NodeId,
+ DinoStorage, Directed,
};
fn directed_edges_between<N>(
@@ -28,16 +28,16 @@
impl<N, E> DirectedGraphStorage for DinoStorage<N, E, Directed> {
fn directed_edges_between(
&self,
- source: Self::NodeId,
- target: Self::NodeId,
+ source: NodeId,
+ target: NodeId,
) -> impl Iterator<Item = Edge<Self>> {
directed_edges_between(&self.nodes, source, target).filter_map(move |id| self.edge(id))
}
fn directed_edges_between_mut(
&mut self,
- source: Self::NodeId,
- target: Self::NodeId,
+ source: NodeId,
+ target: NodeId,
) -> impl Iterator<Item = EdgeMut<Self>> {
let Self { edges, nodes, .. } = self;
@@ -50,7 +50,7 @@
fn node_directed_connections(
&self,
- id: Self::NodeId,
+ id: NodeId,
direction: Direction,
) -> impl Iterator<Item = Edge<Self>> {
NodeDirectedConnectionsIter {
@@ -64,7 +64,7 @@
fn node_directed_connections_mut(
&mut self,
- id: Self::NodeId,
+ id: NodeId,
direction: Direction,
) -> impl Iterator<Item = EdgeMut<Self>> {
let Self { nodes, edges, .. } = self;
@@ -84,7 +84,7 @@
fn node_directed_neighbours(
&self,
- id: Self::NodeId,
+ id: NodeId,
direction: Direction,
) -> impl Iterator<Item = Node<Self>> {
self.nodes
@@ -99,7 +99,7 @@
fn node_directed_neighbours_mut(
&mut self,
- id: Self::NodeId,
+ id: NodeId,
direction: Direction,
) -> impl Iterator<Item = NodeMut<Self>> {
let Some(node) = self.nodes.get(id) else {
diff --git a/crates/dino/src/edge.rs b/crates/dino/src/edge.rs
index 3442d2c..a9d3bd6 100644
--- a/crates/dino/src/edge.rs
+++ b/crates/dino/src/edge.rs
@@ -1,97 +1,19 @@
-use core::fmt::{Display, Formatter};
+use petgraph_core::{edge::EdgeId, node::NodeId};
-use petgraph_core::{
- attributes::NoValue,
- edge::marker::GraphDirectionality,
- id::{AssociativeGraphId, GraphId, LinearGraphId, ManagedGraphId},
-};
-
-use crate::{
- node::NodeId,
- slab::{
- secondary::{SlabAttributeMapper, SlabBooleanMapper},
- EntryId, Key, SlabIndexMapper,
- },
- DinoStorage,
-};
-
-/// Identifier for an edge in [`DinoStorage`].
-///
-/// [`EdgeId`] is a unique identifier for an edge in a [`DinoStorage`].
-/// It is used to reference edges within the graph.
-///
-/// An [`EdgeId`] is managed, meaning that it is chosen by the graph itself and not by the user.
-///
-/// [`EdgeId`] implements [`GraphId`], [`ManagedGraphId`] and [`LinearGraphId`].
-///
-/// # Example
-///
-/// ```
-/// use petgraph_dino::DiDinoGraph;
-///
-/// let mut graph = DiDinoGraph::new();
-///
-/// let a = *graph.insert_node("A").id();
-/// let b = *graph.insert_node("B").id();
-///
-/// let ab = *graph.insert_edge("A → B", &a, &b).id();
-///
-/// println!("Edge A → B: {ab}");
-/// ```
-#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub struct EdgeId(EntryId);
-
-impl Display for EdgeId {
- fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
- Display::fmt(&self.0, f)
- }
-}
+use crate::slab::{EntryId, Key};
impl Key for EdgeId {
#[inline]
fn from_id(id: EntryId) -> Self {
- Self(id)
+ Self::new(id.into_usize())
}
#[inline]
fn into_id(self) -> EntryId {
- self.0
+ EntryId::new_unchecked(self.into_inner())
}
}
-impl GraphId for EdgeId {
- type AttributeIndex = NoValue;
-}
-
-impl<N, E, D> LinearGraphId<DinoStorage<N, E, D>> for EdgeId
-where
- D: GraphDirectionality,
-{
- type Mapper<'a> = SlabIndexMapper<'a, Self> where Self: 'a, N: 'a, E: 'a;
-
- fn index_mapper(storage: &DinoStorage<N, E, D>) -> Self::Mapper<'_> {
- SlabIndexMapper::new(&storage.edges)
- }
-}
-
-impl<N, E, D> AssociativeGraphId<DinoStorage<N, E, D>> for EdgeId
-where
- D: GraphDirectionality,
-{
- type AttributeMapper<'a, V> = SlabAttributeMapper<'a, Self, V> where DinoStorage<N, E, D>: 'a;
- type BooleanMapper<'a> = SlabBooleanMapper<'a> where DinoStorage<N, E, D>: 'a;
-
- fn attribute_mapper<V>(storage: &DinoStorage<N, E, D>) -> Self::AttributeMapper<'_, V> {
- SlabAttributeMapper::new(&storage.edges)
- }
-
- fn boolean_mapper(storage: &DinoStorage<N, E, D>) -> Self::BooleanMapper<'_> {
- SlabBooleanMapper::new(&storage.edges)
- }
-}
-
-impl ManagedGraphId for EdgeId {}
-
pub(crate) type EdgeSlab<T> = crate::slab::Slab<EdgeId, Edge<T>>;
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
diff --git a/crates/dino/src/iter/closure.rs b/crates/dino/src/iter/closure.rs
index b601da7..4b34c31 100644
--- a/crates/dino/src/iter/closure.rs
+++ b/crates/dino/src/iter/closure.rs
@@ -1,6 +1,8 @@
use core::{cmp::Ordering, iter::Peekable};
-use crate::{node::NodeClosures, EdgeId, NodeId};
+use petgraph_core::{edge::EdgeId, node::NodeId};
+
+use crate::node::NodeClosures;
pub(crate) type NodeIdClosureIter<'a> = core::iter::Copied<core::slice::Iter<'a, NodeId>>;
pub(crate) type EdgeIdClosureIter<'a> = core::iter::Copied<core::slice::Iter<'a, EdgeId>>;
diff --git a/crates/dino/src/iter/directed.rs b/crates/dino/src/iter/directed.rs
index a6a28e8..c237225 100644
--- a/crates/dino/src/iter/directed.rs
+++ b/crates/dino/src/iter/directed.rs
@@ -1,6 +1,6 @@
-use petgraph_core::{Edge, GraphDirectionality};
+use petgraph_core::{edge::EdgeId, Edge, GraphDirectionality};
-use crate::{DinoStorage, EdgeId};
+use crate::DinoStorage;
pub(crate) struct NodeDirectedConnectionsIter<'storage, N, E, D, I>
where
diff --git a/crates/dino/src/lib.rs b/crates/dino/src/lib.rs
index 54e3b30..a33a314 100644
--- a/crates/dino/src/lib.rs
+++ b/crates/dino/src/lib.rs
@@ -111,10 +111,12 @@
extern crate alloc;
+mod auxiliary;
pub(crate) mod closure;
mod directed;
mod edge;
mod iter;
+mod linear;
mod node;
mod retain;
pub(crate) mod slab;
@@ -123,17 +125,14 @@
use core::fmt::{Debug, Display};
-pub use edge::EdgeId;
use either::Either;
use error_stack::{Context, Report, Result};
-pub use node::NodeId;
use petgraph_core::{
edge::{
marker::{Directed, GraphDirectionality, Undirected},
- DetachedEdge, EdgeMut,
+ DetachedEdge, EdgeId, EdgeMut,
},
- id::GraphId,
- node::{DetachedNode, NodeMut},
+ node::{DetachedNode, NodeId, NodeMut},
storage::GraphStorage,
Graph,
};
@@ -334,7 +333,7 @@
///
/// let ab = *graph.insert_edge(Edge, &a, &b).id();
/// ```
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DinoStorage<N, E, D = Directed>
where
D: GraphDirectionality,
@@ -435,10 +434,8 @@
where
D: GraphDirectionality,
{
- type EdgeId = EdgeId;
type EdgeWeight = E;
type Error = Error;
- type NodeId = NodeId;
type NodeWeight = N;
fn with_capacity(node_capacity: Option<usize>, edge_capacity: Option<usize>) -> Self {
@@ -451,46 +448,23 @@
}
fn from_parts(
- nodes: impl IntoIterator<Item = DetachedNode<Self::NodeId, Self::NodeWeight>>,
- edges: impl IntoIterator<Item = DetachedEdge<Self::EdgeId, Self::NodeId, Self::EdgeWeight>>,
+ nodes: impl IntoIterator<Item = DetachedNode<Self::NodeWeight>>,
+ edges: impl IntoIterator<Item = DetachedEdge<Self::EdgeWeight>>,
) -> Result<Self, Self::Error> {
- let mut nodes: Slab<_, _> = nodes
- .into_iter()
- .map(|node: DetachedNode<Self::NodeId, Self::NodeWeight>| {
- (node.id, Node::new(node.id, node.weight))
- })
- .collect();
-
- let edges: Slab<_, _> = edges
- .into_iter()
- .map(
- |edge: DetachedEdge<Self::EdgeId, Self::NodeId, Self::EdgeWeight>| {
- (edge.id, Edge::new(edge.id, edge.weight, edge.u, edge.v))
- },
- )
- .collect();
+ todo!();
// TODO: test-case c:
// TODO: this doesn't work if we remove a node
// TODO: NodeId rename is not of concern for us though
// TODO: what about nodes that are added or edges?
// We don't know their ID yet (need a way to get those -> PartialNode/Edge)
-
- Closures::refresh(&mut nodes, &edges);
-
- Ok(Self {
- nodes,
- edges,
-
- _marker: core::marker::PhantomData,
- })
}
fn into_parts(
self,
) -> (
- impl Iterator<Item = DetachedNode<Self::NodeId, Self::NodeWeight>>,
- impl Iterator<Item = DetachedEdge<Self::EdgeId, Self::NodeId, Self::EdgeWeight>>,
+ impl Iterator<Item = DetachedNode<Self::NodeWeight>>,
+ impl Iterator<Item = DetachedEdge<Self::EdgeWeight>>,
) {
let nodes = self.nodes.into_iter().map(|node| DetachedNode {
id: node.id,
@@ -515,13 +489,13 @@
self.edges.len()
}
- fn next_node_id(&self, _: <Self::NodeId as GraphId>::AttributeIndex) -> Self::NodeId {
+ fn next_node_id(&self) -> NodeId {
self.nodes.next_key()
}
fn insert_node(
&mut self,
- id: Self::NodeId,
+ id: NodeId,
weight: Self::NodeWeight,
) -> Result<NodeMut<Self>, Self::Error> {
let expected = id;
@@ -545,17 +519,17 @@
Ok(NodeMut::new(node.id, &mut node.weight))
}
- fn next_edge_id(&self, _: <Self::EdgeId as GraphId>::AttributeIndex) -> Self::EdgeId {
+ fn next_edge_id(&self) -> EdgeId {
self.edges.next_key()
}
fn insert_edge(
&mut self,
- id: Self::EdgeId,
+ id: EdgeId,
weight: Self::EdgeWeight,
- source: Self::NodeId,
- target: Self::NodeId,
+ source: NodeId,
+ target: NodeId,
) -> Result<EdgeMut<Self>, Self::Error> {
// TODO: option to disallow self-loops and parallel edges
@@ -599,10 +573,7 @@
))
}
- fn remove_node(
- &mut self,
- id: Self::NodeId,
- ) -> Option<DetachedNode<Self::NodeId, Self::NodeWeight>> {
+ fn remove_node(&mut self, id: NodeId) -> Option<DetachedNode<Self::NodeWeight>> {
let node = self.nodes.remove(id)?;
for edge in node.closures.edges() {
@@ -616,10 +587,7 @@
Some(DetachedNode::new(id, weight))
}
- fn remove_edge(
- &mut self,
- id: Self::EdgeId,
- ) -> Option<DetachedEdge<Self::EdgeId, Self::NodeId, Self::EdgeWeight>> {
+ fn remove_edge(&mut self, id: EdgeId) -> Option<DetachedEdge<Self::EdgeWeight>> {
let edge = self.edges.remove(id)?;
Closures::remove_edge(&edge, &mut self.nodes);
@@ -637,42 +605,42 @@
Closures::clear(&mut self.nodes);
}
- fn node(&self, id: Self::NodeId) -> Option<petgraph_core::node::Node<Self>> {
+ fn node(&self, id: NodeId) -> Option<petgraph_core::node::Node<Self>> {
self.nodes
.get(id)
.map(|node| petgraph_core::node::Node::new(self, node.id, &node.weight))
}
- fn node_mut(&mut self, id: Self::NodeId) -> Option<NodeMut<Self>> {
+ fn node_mut(&mut self, id: NodeId) -> Option<NodeMut<Self>> {
self.nodes
.get_mut(id)
.map(|node| NodeMut::new(node.id, &mut node.weight))
}
- fn contains_node(&self, id: Self::NodeId) -> bool {
+ fn contains_node(&self, id: NodeId) -> bool {
self.nodes.contains_key(id)
}
- fn edge(&self, id: Self::EdgeId) -> Option<petgraph_core::edge::Edge<Self>> {
+ fn edge(&self, id: EdgeId) -> Option<petgraph_core::edge::Edge<Self>> {
self.edges.get(id).map(|edge| {
petgraph_core::edge::Edge::new(self, edge.id, &edge.weight, edge.source, edge.target)
})
}
- fn edge_mut(&mut self, id: Self::EdgeId) -> Option<EdgeMut<Self>> {
+ fn edge_mut(&mut self, id: EdgeId) -> Option<EdgeMut<Self>> {
self.edges
.get_mut(id)
.map(|edge| EdgeMut::new(edge.id, &mut edge.weight, edge.source, edge.target))
}
- fn contains_edge(&self, id: Self::EdgeId) -> bool {
+ fn contains_edge(&self, id: EdgeId) -> bool {
self.edges.contains_key(id)
}
fn edges_between(
&self,
- source: Self::NodeId,
- target: Self::NodeId,
+ source: NodeId,
+ target: NodeId,
) -> impl Iterator<Item = petgraph_core::edge::Edge<Self>> {
edges_between_undirected(&self.nodes, source, target)
.filter_map(move |edge| self.edge(edge))
@@ -680,8 +648,8 @@
fn edges_between_mut(
&mut self,
- source: Self::NodeId,
- target: Self::NodeId,
+ source: NodeId,
+ target: NodeId,
) -> impl Iterator<Item = EdgeMut<Self>> {
let available = edges_between_undirected(&self.nodes, source, target);
@@ -692,7 +660,7 @@
fn node_connections(
&self,
- id: Self::NodeId,
+ id: NodeId,
) -> impl Iterator<Item = petgraph_core::edge::Edge<Self>> {
self.nodes
.get(id)
@@ -701,7 +669,7 @@
.filter_map(move |edge| self.edge(edge))
}
- fn node_connections_mut(&mut self, id: Self::NodeId) -> impl Iterator<Item = EdgeMut<Self>> {
+ fn node_connections_mut(&mut self, id: NodeId) -> impl Iterator<Item = EdgeMut<Self>> {
let Self { nodes, edges, .. } = self;
let available = nodes
@@ -714,10 +682,7 @@
.map(move |edge| EdgeMut::new(edge.id, &mut edge.weight, edge.source, edge.target))
}
- fn node_neighbours(
- &self,
- id: Self::NodeId,
- ) -> impl Iterator<Item = petgraph_core::node::Node<Self>> {
+ fn node_neighbours(&self, id: NodeId) -> impl Iterator<Item = petgraph_core::node::Node<Self>> {
self.nodes
.get(id)
.into_iter()
@@ -725,7 +690,7 @@
.filter_map(move |node| self.node(node))
}
- fn node_neighbours_mut(&mut self, id: Self::NodeId) -> impl Iterator<Item = NodeMut<Self>> {
+ fn node_neighbours_mut(&mut self, id: NodeId) -> impl Iterator<Item = NodeMut<Self>> {
let Some(node) = self.nodes.get(id) else {
return Either::Right(core::iter::empty());
};
diff --git a/crates/dino/src/linear.rs b/crates/dino/src/linear.rs
new file mode 100644
index 0000000..a1742b2
--- /dev/null
+++ b/crates/dino/src/linear.rs
@@ -0,0 +1,21 @@
+use petgraph_core::{
+ edge::EdgeId, node::NodeId, storage::sequential::SequentialGraphStorage, GraphDirectionality,
+};
+
+use crate::{slab::SlabIndexMapper, DinoStorage};
+
+impl<N, E, D> SequentialGraphStorage for DinoStorage<N, E, D>
+where
+ D: GraphDirectionality,
+{
+ type EdgeIdBijection<'graph> = SlabIndexMapper<'graph, EdgeId> where Self: 'graph;
+ type NodeIdBijection<'graph> = SlabIndexMapper<'graph, NodeId> where Self: 'graph;
+
+ fn node_id_bijection(&self) -> Self::NodeIdBijection<'_> {
+ SlabIndexMapper::new(&self.nodes)
+ }
+
+ fn edge_id_bijection(&self) -> Self::EdgeIdBijection<'_> {
+ SlabIndexMapper::new(&self.edges)
+ }
+}
diff --git a/crates/dino/src/node.rs b/crates/dino/src/node.rs
index 88818da..abca212 100644
--- a/crates/dino/src/node.rs
+++ b/crates/dino/src/node.rs
@@ -1,10 +1,4 @@
-use core::fmt::{Display, Formatter};
-
-use petgraph_core::{
- attributes::NoValue,
- edge::marker::GraphDirectionality,
- id::{AssociativeGraphId, GraphId, LinearGraphId, ManagedGraphId},
-};
+use petgraph_core::{edge::EdgeId, node::NodeId};
use crate::{
closure::UniqueVec,
@@ -12,87 +6,21 @@
EdgeBetweenIterator, EdgeIdClosureIter, EdgeIntersectionIterator, EdgeIterator,
NeighbourIterator, NodeIdClosureIter,
},
- slab::{
- secondary::{SlabAttributeMapper, SlabBooleanMapper},
- EntryId, Key, SlabIndexMapper,
- },
- DinoStorage, EdgeId,
+ slab::{EntryId, Key},
};
-/// Identifier for a node in [`DinoStorage`].
-///
-/// [`NodeId`] is a unique identifier for a node in a [`DinoStorage`].
-/// It is used to reference nodes within the graph.
-///
-/// A [`NodeId`] is managed, meaning that it is chosen by the graph itself and not by the user.
-///
-/// [`NodeId`] implements [`GraphId`], [`ManagedGraphId`] and [`LinearGraphId`].
-///
-/// # Example
-///
-/// ```
-/// use petgraph_dino::DiDinoGraph;
-///
-/// let mut graph = DiDinoGraph::<_, u8>::new();
-///
-/// let a = *graph.insert_node("A").id();
-///
-/// println!("Node A: {a}");
-/// ```
-#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub struct NodeId(EntryId);
-
-impl Display for NodeId {
- fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
- Display::fmt(&self.0, f)
- }
-}
-
impl Key for NodeId {
#[inline]
fn from_id(id: EntryId) -> Self {
- Self(id)
+ Self::new(id.into_usize())
}
#[inline]
fn into_id(self) -> EntryId {
- self.0
+ EntryId::new_unchecked(self.into_inner())
}
}
-impl GraphId for NodeId {
- type AttributeIndex = NoValue;
-}
-
-impl<N, E, D> LinearGraphId<DinoStorage<N, E, D>> for NodeId
-where
- D: GraphDirectionality,
-{
- type Mapper<'a> = SlabIndexMapper<'a, Self> where Self: 'a, N: 'a, E: 'a;
-
- fn index_mapper(storage: &DinoStorage<N, E, D>) -> Self::Mapper<'_> {
- SlabIndexMapper::new(&storage.nodes)
- }
-}
-
-impl<N, E, D> AssociativeGraphId<DinoStorage<N, E, D>> for NodeId
-where
- D: GraphDirectionality,
-{
- type AttributeMapper<'a, V> = SlabAttributeMapper<'a, Self, V> where DinoStorage<N, E, D>: 'a;
- type BooleanMapper<'a> = SlabBooleanMapper<'a> where DinoStorage<N, E, D>: 'a;
-
- fn attribute_mapper<V>(storage: &DinoStorage<N, E, D>) -> Self::AttributeMapper<'_, V> {
- SlabAttributeMapper::new(&storage.nodes)
- }
-
- fn boolean_mapper(storage: &DinoStorage<N, E, D>) -> Self::BooleanMapper<'_> {
- SlabBooleanMapper::new(&storage.nodes)
- }
-}
-
-impl ManagedGraphId for NodeId {}
-
pub(crate) type NodeSlab<T> = crate::slab::Slab<NodeId, Node<T>>;
#[derive(Debug, Clone)]
diff --git a/crates/dino/src/slab/id.rs b/crates/dino/src/slab/id.rs
index 8f17995..3bbe8fc 100644
--- a/crates/dino/src/slab/id.rs
+++ b/crates/dino/src/slab/id.rs
@@ -68,8 +68,8 @@
})
}
- pub(crate) fn new_unchecked(raw: u32) -> Self {
- Self(NonZeroUsize::new(raw as usize).expect("raw is zero"))
+ pub(crate) fn new_unchecked(raw: usize) -> Self {
+ Self(NonZeroUsize::new(raw).expect("raw is zero"))
}
#[inline]
@@ -88,6 +88,12 @@
#[inline]
#[must_use]
+ pub(crate) const fn into_usize(self) -> usize {
+ self.0.get()
+ }
+
+ #[inline]
+ #[must_use]
#[allow(clippy::cast_possible_truncation)]
pub(crate) fn generation(self) -> Generation {
Generation(
diff --git a/crates/dino/src/slab/mod.rs b/crates/dino/src/slab/mod.rs
index 44eee1d..7a7e6b7 100644
--- a/crates/dino/src/slab/mod.rs
+++ b/crates/dino/src/slab/mod.rs
@@ -8,8 +8,7 @@
use alloc::{vec, vec::Vec};
use core::{fmt::Debug, hash::Hash, marker::PhantomData, ptr};
-use numi::borrow::Moo;
-use petgraph_core::id::IndexMapper;
+use petgraph_core::storage::sequential::GraphIdBijection;
use crate::slab::entry::{Entry, State};
pub(crate) use crate::slab::{generation::Generation, id::EntryId, key::Key};
@@ -428,7 +427,7 @@
}
}
-impl<K> IndexMapper<K> for SlabIndexMapper<'_, K>
+impl<K> GraphIdBijection<K> for SlabIndexMapper<'_, K>
where
K: Key,
{
diff --git a/crates/dino/src/slab/secondary.rs b/crates/dino/src/slab/secondary.rs
index 23d4e86..cdbe91b 100644
--- a/crates/dino/src/slab/secondary.rs
+++ b/crates/dino/src/slab/secondary.rs
@@ -2,64 +2,65 @@
use core::iter;
use bitvec::{boxed::BitBox, prelude::BitVec};
-use numi::borrow::Moo;
-use petgraph_core::id::{AttributeMapper, BooleanMapper};
+use petgraph_core::storage::auxiliary::{BooleanGraphStorage, SecondaryGraphStorage};
use crate::slab::{EntryId, Generation, Key, Slab};
-pub struct SlabBooleanMapper<'a> {
+pub struct SlabBooleanStorage<'a, K> {
flags: BitBox,
- _marker: core::marker::PhantomData<&'a ()>,
+
+ _slab: core::marker::PhantomData<&'a ()>,
+ _key: core::marker::PhantomData<fn() -> *const K>,
}
-impl<'a> SlabBooleanMapper<'a> {
- pub(crate) fn new<K, V>(slab: &'a Slab<K, V>) -> Self
- where
- K: Key,
- {
+impl<'a, K> SlabBooleanStorage<'a, K>
+where
+ K: Key,
+{
+ pub(crate) fn new<V>(slab: &'a Slab<K, V>) -> Self {
let length = slab.total_len();
Self {
flags: BitVec::repeat(false, length).into_boxed_bitslice(),
- _marker: core::marker::PhantomData,
+
+ _slab: core::marker::PhantomData,
+ _key: core::marker::PhantomData,
}
}
}
-impl<Id> BooleanMapper<Id> for SlabBooleanMapper<'_>
+impl<K> BooleanGraphStorage<K> for SlabBooleanStorage<'_, K>
where
- Id: Key,
+ K: Key,
{
#[inline]
- fn get(&self, id: Id) -> Option<bool> {
+ fn get(&self, id: K) -> Option<bool> {
let index = id.into_id().index();
self.flags.get(index).map(|bit| *bit)
}
#[inline]
- fn index(&self, id: Id) -> bool {
- let index = id.into_id().index();
-
- self.flags[index]
- }
-
- #[inline]
- fn set(&mut self, id: Id, flag: bool) -> Option<bool> {
+ fn set(&mut self, id: K, flag: bool) -> Option<bool> {
let index = id.into_id().index();
let value = self.flags.replace(index, flag);
Some(value)
}
+
+ #[inline]
+ fn fill(&mut self, flag: bool) {
+ self.flags.fill(flag);
+ }
}
-pub struct SlabAttributeStorageIter<'a, K, T> {
+pub struct SlabSecondaryStorageIter<'a, K, T> {
iter: iter::Enumerate<core::slice::Iter<'a, Option<(Generation, T)>>>,
_marker: core::marker::PhantomData<&'a K>,
}
-impl<'a, K, T> Iterator for SlabAttributeStorageIter<'a, K, T>
+impl<'a, K, T> Iterator for SlabSecondaryStorageIter<'a, K, T>
where
K: Key,
{
@@ -79,7 +80,7 @@
}
}
-pub struct SlabAttributeMapper<'a, K, T> {
+pub struct SlabSecondaryStorage<'a, K, T> {
// generation is needed for iter
items: Vec<Option<(Generation, T)>>,
@@ -87,7 +88,7 @@
_key: core::marker::PhantomData<fn() -> *const K>,
}
-impl<'a, K, T> SlabAttributeMapper<'a, K, T> {
+impl<'a, K, T> SlabSecondaryStorage<'a, K, T> {
pub(crate) fn new<V>(slab: &'a Slab<K, V>) -> Self
where
K: Key,
@@ -103,11 +104,11 @@
}
}
-impl<K, T> AttributeMapper<K, T> for SlabAttributeMapper<'_, K, T>
+impl<K, T> SecondaryGraphStorage<K, T> for SlabSecondaryStorage<'_, K, T>
where
K: Key,
{
- type Iter<'a> = SlabAttributeStorageIter<'a, K, T> where
+ type Iter<'a> = SlabSecondaryStorageIter<'a, K, T> where
K: 'a,
T: 'a,
Self: 'a,;
@@ -150,7 +151,7 @@
}
fn iter(&self) -> Self::Iter<'_> {
- SlabAttributeStorageIter {
+ SlabSecondaryStorageIter {
iter: self.items.iter().enumerate(),
_marker: core::marker::PhantomData,
}
diff --git a/crates/dino/src/tests.rs b/crates/dino/src/tests.rs
index b042474..a03db3a 100644
--- a/crates/dino/src/tests.rs
+++ b/crates/dino/src/tests.rs
@@ -2,13 +2,12 @@
use hashbrown::HashSet;
use petgraph_core::{
- attributes::NoValue,
- edge::{marker::Directed, DetachedEdge, Direction},
- node::{DetachedNode, Node, NodeMut},
+ edge::{marker::Directed, DetachedEdge, Direction, EdgeId},
+ node::{DetachedNode, Node, NodeId, NodeMut},
storage::GraphStorage,
};
-use crate::{DinoGraph, DinoStorage, EdgeId, NodeId};
+use crate::{DinoGraph, DinoStorage};
// TODO: rework tests to be more encompassing and use test utils!
@@ -43,9 +42,9 @@
let mut graph = DinoGraph::<(), u8, Directed>::new();
let node = graph.try_insert_node(()).unwrap();
- let node = *node.id();
+ let node = node.id();
- let edge = graph.try_insert_edge(2u8, &node, &node).unwrap();
+ let edge = graph.try_insert_edge(2u8, node, node).unwrap();
assert_eq!(edge.weight(), &2u8);
@@ -60,17 +59,17 @@
fn next_node_id_pure() {
let mut storage = DinoStorage::<(), (), Directed>::new();
- let a = storage.next_node_id(NoValue::new());
- let b = storage.next_node_id(NoValue::new());
+ let a = storage.next_node_id();
+ let b = storage.next_node_id();
assert_eq!(a, b);
let node = storage.insert_node(a, ()).unwrap();
- let node = *node.id();
+ let node = node.id();
assert_eq!(node, a);
- let c = storage.next_node_id(NoValue::new());
+ let c = storage.next_node_id();
assert_ne!(a, c);
}
@@ -79,22 +78,20 @@
fn next_edge_id_pure() {
let mut storage = DinoStorage::<(), (), Directed>::new();
- let node = storage
- .insert_node(storage.next_node_id(NoValue::new()), ())
- .unwrap();
- let node = *node.id();
+ let node = storage.insert_node(storage.next_node_id(), ()).unwrap();
+ let node = node.id();
- let a = storage.next_edge_id(NoValue::new());
- let b = storage.next_edge_id(NoValue::new());
+ let a = storage.next_edge_id();
+ let b = storage.next_edge_id();
assert_eq!(a, b);
- let edge = storage.insert_edge(a, (), &node, &node).unwrap();
- let edge = *edge.id();
+ let edge = storage.insert_edge(a, (), node, node).unwrap();
+ let edge = edge.id();
assert_eq!(edge, a);
- let c = storage.next_edge_id(NoValue::new());
+ let c = storage.next_edge_id();
assert_ne!(a, c);
}
@@ -104,9 +101,9 @@
let mut graph = DinoGraph::<u8, (), Directed>::new();
let node = graph.try_insert_node(2u8).unwrap();
- let node = *node.id();
+ let node = node.id();
- assert_eq!(graph.remove_node(&node), Some(DetachedNode::new(node, 2u8)));
+ assert_eq!(graph.remove_node(node), Some(DetachedNode::new(node, 2u8)));
assert_eq!(graph.num_nodes(), 0);
assert_eq!(graph.num_edges(), 0);
@@ -120,13 +117,13 @@
let mut graph = DinoGraph::<(), u8, Directed>::new();
let node = graph.try_insert_node(()).unwrap();
- let node = *node.id();
+ let node = node.id();
- let edge = graph.try_insert_edge(2u8, &node, &node).unwrap();
- let edge = *edge.id();
+ let edge = graph.try_insert_edge(2u8, node, node).unwrap();
+ let edge = edge.id();
assert_eq!(
- graph.remove_edge(&edge),
+ graph.remove_edge(edge),
Some(DetachedEdge::new(edge, 2u8, node, node))
);
@@ -136,32 +133,28 @@
assert_eq!(graph.nodes().count(), 1);
assert_eq!(graph.edges().count(), 0);
- assert_eq!(graph.connections(&node).count(), 0);
- assert_eq!(graph.neighbours(&node).count(), 0);
+ assert_eq!(graph.connections(node).count(), 0);
+ assert_eq!(graph.neighbours(node).count(), 0);
assert_eq!(
graph
- .connections_directed(&node, Direction::Incoming)
+ .connections_directed(node, Direction::Incoming)
.count(),
0
);
assert_eq!(
graph
- .connections_directed(&node, Direction::Outgoing)
+ .connections_directed(node, Direction::Outgoing)
.count(),
0
);
assert_eq!(
- graph
- .neighbours_directed(&node, Direction::Incoming)
- .count(),
+ graph.neighbours_directed(node, Direction::Incoming).count(),
0
);
assert_eq!(
- graph
- .neighbours_directed(&node, Direction::Outgoing)
- .count(),
+ graph.neighbours_directed(node, Direction::Outgoing).count(),
0
);
}
@@ -171,9 +164,9 @@
let mut graph = DinoGraph::<u8, u8, Directed>::new();
let node = graph.try_insert_node(2u8).unwrap();
- let node = *node.id();
+ let node = node.id();
- graph.try_insert_edge(2u8, &node, &node).unwrap();
+ graph.try_insert_edge(2u8, node, node).unwrap();
graph.clear();
@@ -189,11 +182,11 @@
let mut graph = DinoGraph::<u8, (), Directed>::new();
let node = graph.try_insert_node(2u8).unwrap();
- let node = *node.id();
+ let node = node.id();
assert_eq!(
- graph.node(&node),
- Some(Node::new(graph.storage(), &node, &2u8))
+ graph.node(node),
+ Some(Node::new(graph.storage(), node, &2u8))
);
}
@@ -202,17 +195,17 @@
let mut graph = DinoGraph::<u8, (), Directed>::new();
let node = graph.try_insert_node(2u8).unwrap();
- let node = *node.id();
+ let node = node.id();
- assert_eq!(graph.node_mut(&node), Some(NodeMut::new(&node, &mut 2u8)));
+ assert_eq!(graph.node_mut(node), Some(NodeMut::new(node, &mut 2u8)));
- let mut node = graph.node_mut(&node).unwrap();
+ let mut node = graph.node_mut(node).unwrap();
*node.weight_mut() = 3u8;
- let node = *node.id();
+ let node = node.id();
assert_eq!(
- graph.node(&node),
- Some(Node::new(graph.storage(), &node, &3u8))
+ graph.node(node),
+ Some(Node::new(graph.storage(), node, &3u8))
);
}
@@ -221,9 +214,9 @@
let mut graph = DinoGraph::<u8, (), Directed>::new();
let node = graph.try_insert_node(2u8).unwrap();
- let node = *node.id();
+ let node = node.id();
- assert!(graph.storage().contains_node(&node));
+ assert!(graph.storage().contains_node(node));
}
#[test]
@@ -231,19 +224,19 @@
let mut graph = DinoGraph::<(), u8, Directed>::new();
let node = graph.try_insert_node(()).unwrap();
- let node = *node.id();
+ let node = node.id();
- let edge = graph.try_insert_edge(2u8, &node, &node).unwrap();
- let edge = *edge.id();
+ let edge = graph.try_insert_edge(2u8, node, node).unwrap();
+ let edge = edge.id();
assert_eq!(
- graph.edge(&edge),
+ graph.edge(edge),
Some(petgraph_core::edge::Edge::new(
graph.storage(),
- &edge,
+ edge,
&2u8,
- &node,
- &node
+ node,
+ node
))
);
}
@@ -253,30 +246,30 @@
let mut graph = DinoGraph::<(), u8, Directed>::new();
let node = graph.try_insert_node(()).unwrap();
- let node = *node.id();
+ let node = node.id();
- let edge = graph.try_insert_edge(2u8, &node, &node).unwrap();
- let edge = *edge.id();
+ let edge = graph.try_insert_edge(2u8, node, node).unwrap();
+ let edge = edge.id();
assert_eq!(
- graph.edge_mut(&edge),
+ graph.edge_mut(edge),
Some(petgraph_core::edge::EdgeMut::new(
- &edge, &mut 2u8, &node, &node
+ edge, &mut 2u8, node, node
))
);
- let mut edge = graph.edge_mut(&edge).unwrap();
+ let mut edge = graph.edge_mut(edge).unwrap();
*edge.weight_mut() = 3u8;
- let edge = *edge.id();
+ let edge = edge.id();
assert_eq!(
- graph.edge(&edge),
+ graph.edge(edge),
Some(petgraph_core::edge::Edge::new(
graph.storage(),
- &edge,
+ edge,
&3u8,
- &node,
- &node
+ node,
+ node
))
);
}
@@ -286,12 +279,12 @@
let mut graph = DinoGraph::<(), u8, Directed>::new();
let node = graph.try_insert_node(()).unwrap();
- let node = *node.id();
+ let node = node.id();
- let edge = graph.try_insert_edge(2u8, &node, &node).unwrap();
- let edge = *edge.id();
+ let edge = graph.try_insert_edge(2u8, node, node).unwrap();
+ let edge = edge.id();
- assert!(graph.storage().contains_edge(&edge));
+ assert!(graph.storage().contains_edge(edge));
}
struct SimpleGraph {
@@ -314,31 +307,31 @@
let mut graph = DinoGraph::new();
let a = graph.try_insert_node(1u8).unwrap();
- let a = *a.id();
+ let a = a.id();
let b = graph.try_insert_node(2u8).unwrap();
- let b = *b.id();
+ let b = b.id();
let c = graph.try_insert_node(3u8).unwrap();
- let c = *c.id();
+ let c = c.id();
let d = graph.try_insert_node(8u8).unwrap();
- let d = *d.id();
+ let d = d.id();
- let ab = graph.try_insert_edge(4u8, &a, &b).unwrap();
- let ab = *ab.id();
+ let ab = graph.try_insert_edge(4u8, a, b).unwrap();
+ let ab = ab.id();
- let ba = graph.try_insert_edge(5u8, &b, &a).unwrap();
- let ba = *ba.id();
+ let ba = graph.try_insert_edge(5u8, b, a).unwrap();
+ let ba = ba.id();
- let bc = graph.try_insert_edge(6u8, &b, &c).unwrap();
- let bc = *bc.id();
+ let bc = graph.try_insert_edge(6u8, b, c).unwrap();
+ let bc = bc.id();
- let ac = graph.try_insert_edge(7u8, &a, &c).unwrap();
- let ac = *ac.id();
+ let ac = graph.try_insert_edge(7u8, a, c).unwrap();
+ let ac = ac.id();
- let ca = graph.try_insert_edge(8u8, &c, &a).unwrap();
- let ca = *ca.id();
+ let ca = graph.try_insert_edge(8u8, c, a).unwrap();
+ let ca = ca.id();
Self {
graph,
@@ -368,7 +361,7 @@
assert_eq!(
graph
- .edges_between(&a, &b)
+ .edges_between(a, b)
.map(petgraph_core::edge::Edge::detach)
.collect::<HashSet<_>>(),
[
@@ -391,13 +384,13 @@
..
} = SimpleGraph::create();
- for mut edge in graph.edges_between_mut(&a, &b) {
+ for mut edge in graph.edges_between_mut(a, b) {
*edge.weight_mut() += 1;
}
assert_eq!(
graph
- .edges_between(&a, &b)
+ .edges_between(a, b)
.map(petgraph_core::edge::Edge::detach)
.collect::<HashSet<_>>(),
[
@@ -425,7 +418,7 @@
assert_eq!(
graph
- .connections(&a)
+ .connections(a)
.map(petgraph_core::edge::Edge::detach)
.collect::<HashSet<_>>(),
[
@@ -453,13 +446,13 @@
bc,
..
} = SimpleGraph::create();
- for mut connection in graph.connections_mut(&a) {
+ for mut connection in graph.connections_mut(a) {
*connection.weight_mut() += 1;
}
assert_eq!(
graph
- .connections(&a)
+ .connections(a)
.map(petgraph_core::edge::Edge::detach)
.collect::<HashSet<_>>(),
[
@@ -473,13 +466,13 @@
);
assert_eq!(
- graph.edge(&bc),
+ graph.edge(bc),
Some(petgraph_core::edge::Edge::new(
graph.storage(),
- &bc,
+ bc,
&6u8,
- &b,
- &c
+ b,
+ c
))
);
}
@@ -492,7 +485,7 @@
assert_eq!(
graph
- .neighbours(&a)
+ .neighbours(a)
.map(Node::detach)
.collect::<HashSet<_>>(),
[DetachedNode::new(b, 2u8), DetachedNode::new(c, 3u8)]
@@ -502,7 +495,7 @@
assert_eq!(
graph
- .neighbours(&b)
+ .neighbours(b)
.map(Node::detach)
.collect::<HashSet<_>>(),
[DetachedNode::new(a, 1u8), DetachedNode::new(c, 3u8)]
@@ -512,7 +505,7 @@
assert_eq!(
graph
- .neighbours(&c)
+ .neighbours(c)
.map(Node::detach)
.collect::<HashSet<_>>(),
[DetachedNode::new(a, 1u8), DetachedNode::new(b, 2u8)]
@@ -522,7 +515,7 @@
assert_eq!(
graph
- .neighbours(&d)
+ .neighbours(d)
.map(Node::detach)
.collect::<HashSet<_>>(),
HashSet::new()
@@ -540,13 +533,13 @@
..
} = SimpleGraph::create();
- for mut neighbour in graph.neighbours_mut(&a) {
+ for mut neighbour in graph.neighbours_mut(a) {
*neighbour.weight_mut() += 1;
}
assert_eq!(
graph
- .neighbours(&a)
+ .neighbours(a)
.map(Node::detach)
.collect::<HashSet<_>>(),
[DetachedNode::new(b, 3u8), DetachedNode::new(c, 4u8)]
@@ -556,7 +549,7 @@
assert_eq!(
graph
- .neighbours(&b)
+ .neighbours(b)
.map(Node::detach)
.collect::<HashSet<_>>(),
[DetachedNode::new(a, 1u8), DetachedNode::new(c, 4u8)]
@@ -566,7 +559,7 @@
assert_eq!(
graph
- .neighbours(&c)
+ .neighbours(c)
.map(Node::detach)
.collect::<HashSet<_>>(),
[DetachedNode::new(a, 1u8), DetachedNode::new(b, 3u8)]
@@ -576,7 +569,7 @@
assert_eq!(
graph
- .neighbours(&d)
+ .neighbours(d)
.map(Node::detach)
.collect::<HashSet<_>>(),
HashSet::new()
@@ -619,11 +612,11 @@
once(DetachedNode::new(d, 9u8)).collect::<HashSet<_>>()
);
- assert_eq!(graph.node(&a), Some(Node::new(graph.storage(), &a, &1u8)));
+ assert_eq!(graph.node(a), Some(Node::new(graph.storage(), a, &1u8)));
- assert_eq!(graph.node(&b), Some(Node::new(graph.storage(), &b, &2u8)));
+ assert_eq!(graph.node(b), Some(Node::new(graph.storage(), b, &2u8)));
- assert_eq!(graph.node(&c), Some(Node::new(graph.storage(), &c, &3u8)));
+ assert_eq!(graph.node(c), Some(Node::new(graph.storage(), c, &3u8)));
}
#[test]
diff --git a/crates/graphmap/CHANGELOG.md b/crates/entry/CHANGELOG.md
similarity index 100%
rename from crates/graphmap/CHANGELOG.md
rename to crates/entry/CHANGELOG.md
diff --git a/crates/entry/Cargo.toml b/crates/entry/Cargo.toml
new file mode 100644
index 0000000..ef41ae9
--- /dev/null
+++ b/crates/entry/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+name = "petgraph-entry"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+[dependencies]
+petgraph-core = { workspace = true }
+petgraph-dino = { workspace = true }
+
+error-stack = { workspace = true }
+hashbrown = "0.14.3"
+
+[features]
diff --git a/crates/entry/src/auxiliary.rs b/crates/entry/src/auxiliary.rs
new file mode 100644
index 0000000..9ddd8d4
--- /dev/null
+++ b/crates/entry/src/auxiliary.rs
@@ -0,0 +1,36 @@
+use core::hash::Hash;
+
+use petgraph_core::{
+ storage::{auxiliary::Hints, AuxiliaryGraphStorage},
+ GraphDirectionality,
+};
+
+use crate::{Backend, EntryStorage};
+
+impl<NK, NV, EK, EV, D> AuxiliaryGraphStorage for EntryStorage<NK, NV, EK, EV, D>
+where
+ D: GraphDirectionality,
+ NK: Hash,
+ EK: Hash,
+{
+ type BooleanEdgeStorage<'a> = <Backend<NK, NV, EK, EV, D> as AuxiliaryGraphStorage>::BooleanEdgeStorage<'a> where Self: 'a;
+ type BooleanNodeStorage<'a> = <Backend<NK, NV, EK, EV, D> as AuxiliaryGraphStorage>::BooleanNodeStorage<'a> where Self: 'a;
+ type SecondaryEdgeStorage<'a, V> = <Backend<NK, NV, EK, EV, D> as AuxiliaryGraphStorage>::SecondaryEdgeStorage<'a, V> where Self: 'a;
+ type SecondaryNodeStorage<'a, V> = <Backend<NK, NV, EK, EV, D> as AuxiliaryGraphStorage>::SecondaryNodeStorage<'a, V> where Self: 'a;
+
+ fn secondary_node_storage<V>(&self, hints: Hints) -> Self::SecondaryNodeStorage<'_, V> {
+ self.inner.secondary_node_storage(hints)
+ }
+
+ fn secondary_edge_storage<V>(&self, hints: Hints) -> Self::SecondaryEdgeStorage<'_, V> {
+ self.inner.secondary_edge_storage(hints)
+ }
+
+ fn boolean_node_storage(&self, hints: Hints) -> Self::BooleanNodeStorage<'_> {
+ self.inner.boolean_node_storage(hints)
+ }
+
+ fn boolean_edge_storage(&self, hints: Hints) -> Self::BooleanEdgeStorage<'_> {
+ self.inner.boolean_edge_storage(hints)
+ }
+}
diff --git a/crates/entry/src/directed.rs b/crates/entry/src/directed.rs
new file mode 100644
index 0000000..a4f8e9b
--- /dev/null
+++ b/crates/entry/src/directed.rs
@@ -0,0 +1,79 @@
+use core::hash::Hash;
+
+use petgraph_core::{
+ edge::{marker::Directed, Direction},
+ node::NodeId,
+ DirectedGraphStorage, Edge, EdgeMut, Node, NodeMut,
+};
+
+use crate::EntryStorage;
+
+impl<NK, NV, EK, EV> DirectedGraphStorage for EntryStorage<NK, NV, EK, EV, Directed>
+where
+ NK: Hash,
+ EK: Hash,
+{
+ fn directed_edges_between(
+ &self,
+ source: NodeId,
+ target: NodeId,
+ ) -> impl Iterator<Item = Edge<'_, Self>> {
+ self.inner
+ .directed_edges_between(source, target)
+ .map(|edge| edge.change_storage_unchecked(self))
+ }
+
+ fn directed_edges_between_mut(
+ &mut self,
+ source: NodeId,
+ target: NodeId,
+ ) -> impl Iterator<Item = EdgeMut<'_, Self>> {
+ self.inner
+ .directed_edges_between_mut(source, target)
+ .map(EdgeMut::change_storage_unchecked)
+ }
+
+ fn node_directed_connections(
+ &self,
+ id: NodeId,
+ direction: Direction,
+ ) -> impl Iterator<Item = Edge<'_, Self>> {
+ self.inner
+ .node_directed_connections(id, direction)
+ .map(|edge| edge.change_storage_unchecked(self))
+ }
+
+ fn node_directed_connections_mut(
+ &mut self,
+ id: NodeId,
+ direction: Direction,
+ ) -> impl Iterator<Item = EdgeMut<'_, Self>> {
+ self.inner
+ .node_directed_connections_mut(id, direction)
+ .map(EdgeMut::change_storage_unchecked)
+ }
+
+ fn node_directed_degree(&self, id: NodeId, direction: Direction) -> usize {
+ self.inner.node_directed_degree(id, direction)
+ }
+
+ fn node_directed_neighbours(
+ &self,
+ id: NodeId,
+ direction: Direction,
+ ) -> impl Iterator<Item = Node<'_, Self>> {
+ self.inner
+ .node_directed_neighbours(id, direction)
+ .map(|node| node.change_storage_unchecked(self))
+ }
+
+ fn node_directed_neighbours_mut(
+ &mut self,
+ id: NodeId,
+ direction: Direction,
+ ) -> impl Iterator<Item = NodeMut<'_, Self>> {
+ self.inner
+ .node_directed_neighbours_mut(id, direction)
+ .map(NodeMut::change_storage_unchecked)
+ }
+}
diff --git a/crates/entry/src/error.rs b/crates/entry/src/error.rs
new file mode 100644
index 0000000..d36d1b0
--- /dev/null
+++ b/crates/entry/src/error.rs
@@ -0,0 +1,35 @@
+use core::{fmt, fmt::Display};
+
+use error_stack::Context;
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum EntryError {
+ /// An underlying backend error occurred.
+ ///
+ /// Refer to the underlying attached backend error for more information on what went wrong.
+ Backend,
+ /// The node you are trying to insert already exists.
+ ///
+ /// This means that the [`Entry::key`] you are trying to insert already exists in the graph.
+ ///
+ /// [`Entry::key`]: crate::Entry::key
+ NodeAlreadyExists,
+ /// The edge you are trying to insert already exists.
+ ///
+ /// This means that the [`Entry::key`] you are trying to insert already exists in the graph.
+ ///
+ /// [`Entry::key`]: crate::Entry::key
+ EdgeAlreadyExists,
+}
+
+impl Display for EntryError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ Self::Backend => write!(f, "Backend error"),
+ Self::NodeAlreadyExists => write!(f, "Node already exists"),
+ Self::EdgeAlreadyExists => write!(f, "Edge already exists"),
+ }
+ }
+}
+
+impl Context for EntryError {}
diff --git a/crates/entry/src/hash.rs b/crates/entry/src/hash.rs
new file mode 100644
index 0000000..859e6cb
--- /dev/null
+++ b/crates/entry/src/hash.rs
@@ -0,0 +1,47 @@
+use core::hash::{BuildHasher, Hash, Hasher};
+
+pub(crate) struct ValueHash<T> {
+ value: u64,
+ _phantom: core::marker::PhantomData<T>,
+}
+
+impl<T> ValueHash<T> {
+ pub(crate) fn new<S>(hash_builder: &S, value: &T) -> Self
+ where
+ T: Hash,
+ S: BuildHasher,
+ {
+ let hash = {
+ let mut hasher = hash_builder.build_hasher();
+ value.hash(&mut hasher);
+ hasher.finish()
+ };
+
+ Self {
+ value: hash,
+ _phantom: core::marker::PhantomData,
+ }
+ }
+}
+
+impl<T> PartialEq for ValueHash<T> {
+ fn eq(&self, other: &Self) -> bool {
+ self.value == other.value
+ }
+}
+
+impl<T> Eq for ValueHash<T> {}
+
+impl<T> Hash for ValueHash<T> {
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ self.value.hash(state);
+ }
+}
+
+impl<T> Clone for ValueHash<T> {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+
+impl<T> Copy for ValueHash<T> {}
diff --git a/crates/entry/src/lib.rs b/crates/entry/src/lib.rs
new file mode 100644
index 0000000..2342584
--- /dev/null
+++ b/crates/entry/src/lib.rs
@@ -0,0 +1,343 @@
+#![no_std]
+
+mod auxiliary;
+mod directed;
+mod error;
+mod hash;
+mod retain;
+mod reverse;
+mod sequential;
+
+extern crate alloc;
+
+use core::hash::Hash;
+
+use error_stack::{Report, ResultExt};
+use hashbrown::{hash_map::DefaultHashBuilder, HashMap};
+use petgraph_core::{
+ edge::{
+ marker::{Directed, Undirected},
+ EdgeId,
+ },
+ node::NodeId,
+ DetachedEdge, DetachedNode, Edge, EdgeMut, Graph, GraphDirectionality, GraphStorage, Node,
+ NodeMut,
+};
+use petgraph_dino::DinoStorage;
+
+use crate::{error::EntryError, hash::ValueHash};
+
+pub struct Entry<K, V> {
+ key: K,
+ pub value: V,
+}
+
+impl<K, V> Entry<K, V> {
+ #[must_use]
+ pub const fn new(key: K, value: V) -> Self {
+ Self { key, value }
+ }
+
+ pub const fn key(&self) -> &K {
+ &self.key
+ }
+}
+
+pub type EntryGraph<NK, NV, EK, EV, D> = Graph<EntryStorage<NK, NV, EK, EV, D>>;
+pub type UnEntryGraph<NK, NV, EK, EV> =
+ Graph<DinoStorage<Entry<NK, NV>, Entry<EK, EV>, Undirected>>;
+
+pub type DiEntryGraph<NK, NV, EK, EV> = Graph<DinoStorage<Entry<NK, NV>, Entry<EK, EV>, Directed>>;
+
+type Backend<NK, NV, EK, EV, D> = DinoStorage<Entry<NK, NV>, Entry<EK, EV>, D>;
+
+// TODO: better name
+// TODO: reduce generics
+pub struct EntryStorage<NK, NV, EK, EV, D>
+where
+ D: GraphDirectionality,
+ NK: Hash,
+ EK: Hash,
+{
+ inner: Backend<NK, NV, EK, EV, D>,
+
+ nodes: HashMap<ValueHash<NK>, NodeId>,
+ edges: HashMap<ValueHash<EK>, EdgeId>,
+
+ hasher: DefaultHashBuilder,
+}
+
+impl<NK, NV, EK, EV, D> GraphStorage for EntryStorage<NK, NV, EK, EV, D>
+where
+ D: GraphDirectionality,
+ NK: Hash,
+ EK: Hash,
+{
+ type EdgeWeight = Entry<EK, EV>;
+ type Error = EntryError;
+ type NodeWeight = Entry<NK, NV>;
+
+ fn with_capacity(node_capacity: Option<usize>, edge_capacity: Option<usize>) -> Self {
+ Self {
+ inner: DinoStorage::with_capacity(node_capacity, edge_capacity),
+ nodes: HashMap::with_capacity(node_capacity.unwrap_or(0)),
+ edges: HashMap::with_capacity(edge_capacity.unwrap_or(0)),
+ hasher: DefaultHashBuilder::default(),
+ }
+ }
+
+ fn from_parts(
+ nodes: impl IntoIterator<Item = DetachedNode<Self::NodeWeight>>,
+ edges: impl IntoIterator<Item = DetachedEdge<Self::EdgeWeight>>,
+ ) -> error_stack::Result<Self, Self::Error> {
+ todo!()
+ }
+
+ fn into_parts(
+ self,
+ ) -> (
+ impl Iterator<Item = DetachedNode<Self::NodeWeight>>,
+ impl Iterator<Item = DetachedEdge<Self::EdgeWeight>>,
+ ) {
+ self.inner.into_parts()
+ }
+
+ fn num_nodes(&self) -> usize {
+ self.inner.num_nodes()
+ }
+
+ fn num_edges(&self) -> usize {
+ self.inner.num_edges()
+ }
+
+ fn next_node_id(&self) -> NodeId {
+ self.inner.next_node_id()
+ }
+
+ fn insert_node(
+ &mut self,
+ id: NodeId,
+ weight: Self::NodeWeight,
+ ) -> error_stack::Result<NodeMut<Self>, Self::Error> {
+ let hash = ValueHash::new(&self.hasher, &weight.key);
+
+ if self.nodes.contains_key(&hash) {
+ return Err(Report::new(EntryError::NodeAlreadyExists));
+ }
+
+ let node = self
+ .inner
+ .insert_node(id, weight)
+ .change_context(EntryError::Backend)?;
+ self.nodes.insert(hash, id);
+
+ Ok(node.change_storage_unchecked())
+ }
+
+ fn next_edge_id(&self) -> EdgeId {
+ self.inner.next_edge_id()
+ }
+
+ fn insert_edge(
+ &mut self,
+ id: EdgeId,
+ weight: Self::EdgeWeight,
+ u: NodeId,
+ v: NodeId,
+ ) -> error_stack::Result<EdgeMut<Self>, Self::Error> {
+ let hash = ValueHash::new(&self.hasher, &weight.key);
+
+ if self.edges.contains_key(&hash) {
+ return Err(Report::new(EntryError::EdgeAlreadyExists));
+ }
+
+ let edge = self
+ .inner
+ .insert_edge(id, weight, u, v)
+ .change_context(EntryError::Backend)?;
+ self.edges.insert(hash, id);
+
+ Ok(edge.change_storage_unchecked())
+ }
+
+ fn remove_node(&mut self, id: NodeId) -> Option<DetachedNode<Self::NodeWeight>> {
+ let node = self.inner.remove_node(id)?;
+ let hash = ValueHash::new(&self.hasher, &node.weight.key);
+ self.nodes.remove(&hash);
+
+ Some(node)
+ }
+
+ fn remove_edge(&mut self, id: EdgeId) -> Option<DetachedEdge<Self::EdgeWeight>> {
+ let edge = self.inner.remove_edge(id)?;
+ let hash = ValueHash::new(&self.hasher, &edge.weight.key);
+ self.edges.remove(&hash);
+
+ Some(edge)
+ }
+
+ fn clear(&mut self) {
+ self.inner.clear();
+ self.nodes.clear();
+ self.edges.clear();
+ }
+
+ fn node(&self, id: NodeId) -> Option<Node<Self>> {
+ let node = self.inner.node(id)?;
+ Some(node.change_storage_unchecked(self))
+ }
+
+ fn node_mut(&mut self, id: NodeId) -> Option<NodeMut<Self>> {
+ let node = self.inner.node_mut(id)?;
+ Some(node.change_storage_unchecked())
+ }
+
+ fn contains_node(&self, id: NodeId) -> bool {
+ self.inner.contains_node(id)
+ }
+
+ fn edge(&self, id: EdgeId) -> Option<Edge<Self>> {
+ let edge = self.inner.edge(id)?;
+
+ Some(edge.change_storage_unchecked(self))
+ }
+
+ fn edge_mut(&mut self, id: EdgeId) -> Option<EdgeMut<Self>> {
+ let edge = self.inner.edge_mut(id)?;
+
+ Some(edge.change_storage_unchecked())
+ }
+
+ fn contains_edge(&self, id: EdgeId) -> bool {
+ self.inner.contains_edge(id)
+ }
+
+ fn edges_between(&self, u: NodeId, v: NodeId) -> impl Iterator<Item = Edge<'_, Self>> {
+ self.inner
+ .edges_between(u, v)
+ .map(|edge| edge.change_storage_unchecked(self))
+ }
+
+ fn edges_between_mut(
+ &mut self,
+ u: NodeId,
+ v: NodeId,
+ ) -> impl Iterator<Item = EdgeMut<'_, Self>> {
+ self.inner
+ .edges_between_mut(u, v)
+ .map(EdgeMut::change_storage_unchecked)
+ }
+
+ fn node_connections(&self, id: NodeId) -> impl Iterator<Item = Edge<'_, Self>> {
+ self.inner
+ .node_connections(id)
+ .map(|edge| edge.change_storage_unchecked(self))
+ }
+
+ fn node_connections_mut(&mut self, id: NodeId) -> impl Iterator<Item = EdgeMut<'_, Self>> {
+ self.inner
+ .node_connections_mut(id)
+ .map(EdgeMut::change_storage_unchecked)
+ }
+
+ fn node_degree(&self, id: NodeId) -> usize {
+ self.inner.node_degree(id)
+ }
+
+ fn node_neighbours(&self, id: NodeId) -> impl Iterator<Item = Node<'_, Self>> {
+ self.inner
+ .node_neighbours(id)
+ .map(|node| node.change_storage_unchecked(self))
+ }
+
+ fn node_neighbours_mut(&mut self, id: NodeId) -> impl Iterator<Item = NodeMut<'_, Self>> {
+ self.inner
+ .node_neighbours_mut(id)
+ .map(NodeMut::change_storage_unchecked)
+ }
+
+ fn isolated_nodes(&self) -> impl Iterator<Item = Node<Self>> {
+ self.inner
+ .isolated_nodes()
+ .map(|node| node.change_storage_unchecked(self))
+ }
+
+ fn isolated_nodes_mut(&mut self) -> impl Iterator<Item = NodeMut<Self>> {
+ self.inner
+ .isolated_nodes_mut()
+ .map(NodeMut::change_storage_unchecked)
+ }
+
+ fn nodes(&self) -> impl Iterator<Item = Node<Self>> {
+ self.inner
+ .nodes()
+ .map(|node| node.change_storage_unchecked(self))
+ }
+
+ fn nodes_mut(&mut self) -> impl Iterator<Item = NodeMut<Self>> {
+ self.inner
+ .nodes_mut()
+ .map(NodeMut::change_storage_unchecked)
+ }
+
+ fn edges(&self) -> impl Iterator<Item = Edge<Self>> {
+ self.inner
+ .edges()
+ .map(|edge| edge.change_storage_unchecked(self))
+ }
+
+ fn edges_mut(&mut self) -> impl Iterator<Item = EdgeMut<Self>> {
+ self.inner
+ .edges_mut()
+ .map(EdgeMut::change_storage_unchecked)
+ }
+
+ fn reserve(&mut self, additional_nodes: usize, additional_edges: usize) {
+ self.inner.reserve(additional_nodes, additional_edges);
+ self.nodes.reserve(additional_nodes);
+ self.edges.reserve(additional_edges);
+ }
+
+ fn reserve_nodes(&mut self, additional: usize) {
+ self.inner.reserve_nodes(additional);
+ self.nodes.reserve(additional);
+ }
+
+ fn reserve_edges(&mut self, additional: usize) {
+ self.inner.reserve_edges(additional);
+ self.edges.reserve(additional);
+ }
+
+ fn reserve_exact(&mut self, additional_nodes: usize, additional_edges: usize) {
+ self.inner.reserve_exact(additional_nodes, additional_edges);
+
+ self.nodes.reserve(additional_nodes);
+ self.edges.reserve(additional_edges);
+ }
+
+ fn reserve_exact_nodes(&mut self, additional: usize) {
+ self.inner.reserve_exact_nodes(additional);
+ self.nodes.reserve(additional);
+ }
+
+ fn reserve_exact_edges(&mut self, additional: usize) {
+ self.inner.reserve_exact_edges(additional);
+ self.edges.reserve(additional);
+ }
+
+ fn shrink_to_fit(&mut self) {
+ self.inner.shrink_to_fit();
+ self.nodes.shrink_to_fit();
+ self.edges.shrink_to_fit();
+ }
+
+ fn shrink_to_fit_nodes(&mut self) {
+ self.inner.shrink_to_fit_nodes();
+ self.nodes.shrink_to_fit();
+ }
+
+ fn shrink_to_fit_edges(&mut self) {
+ self.inner.shrink_to_fit_edges();
+ self.edges.shrink_to_fit();
+ }
+}
diff --git a/crates/entry/src/retain.rs b/crates/entry/src/retain.rs
new file mode 100644
index 0000000..aae7492
--- /dev/null
+++ b/crates/entry/src/retain.rs
@@ -0,0 +1,48 @@
+use alloc::vec;
+use core::hash::Hash;
+
+use petgraph_core::{
+ storage::RetainableGraphStorage, EdgeMut, GraphDirectionality, GraphStorage, NodeMut,
+};
+
+use crate::EntryStorage;
+
+impl<NK, NV, EK, EV, D> RetainableGraphStorage for EntryStorage<NK, NV, EK, EV, D>
+where
+ D: GraphDirectionality,
+ NK: Hash,
+ EK: Hash,
+{
+ fn retain_nodes(&mut self, mut f: impl FnMut(NodeMut<'_, Self>) -> bool) {
+ let mut remove = vec![];
+
+ for node in self.inner.nodes_mut() {
+ let node = node.change_storage_unchecked();
+ let id = node.id();
+
+ if !f(node) {
+ remove.push(id);
+ }
+ }
+
+ for id in remove {
+ self.remove_node(id);
+ }
+ }
+
+ fn retain_edges(&mut self, mut f: impl FnMut(EdgeMut<'_, Self>) -> bool) {
+ let mut remove = vec![];
+ for edge in self.inner.edges_mut() {
+ let edge = edge.change_storage_unchecked();
+ let id = edge.id();
+
+ if !f(edge) {
+ remove.push(id);
+ }
+ }
+
+ for id in remove {
+ self.remove_edge(id);
+ }
+ }
+}
diff --git a/crates/entry/src/reverse.rs b/crates/entry/src/reverse.rs
new file mode 100644
index 0000000..642dee3
--- /dev/null
+++ b/crates/entry/src/reverse.rs
@@ -0,0 +1,57 @@
+use core::hash::Hash;
+
+use petgraph_core::{
+ storage::reverse::ReverseGraphStorage, Edge, EdgeMut, GraphDirectionality, GraphStorage, Node,
+ NodeMut,
+};
+
+use crate::{hash::ValueHash, EntryStorage};
+
+impl<NK, NV, EK, EV, D> ReverseGraphStorage for EntryStorage<NK, NV, EK, EV, D>
+where
+ D: GraphDirectionality,
+ NK: Hash,
+ EK: Hash,
+{
+ type EdgeKey = EK;
+ type NodeKey = NK;
+
+ fn contains_node_key(&self, key: &Self::NodeKey) -> bool {
+ let hash = ValueHash::new(&self.hasher, key);
+ self.nodes.contains_key(&hash)
+ }
+
+ fn node_by_key(&self, key: &Self::NodeKey) -> Option<Node<Self>> {
+ let hash = ValueHash::new(&self.hasher, key);
+
+ self.nodes.get(&hash).and_then(|id| self.node(*id))
+ }
+
+ fn node_by_key_mut(&mut self, key: &Self::NodeKey) -> Option<NodeMut<Self>> {
+ let hash = ValueHash::new(&self.hasher, key);
+
+ let &node = self.nodes.get(&hash)?;
+
+ self.node_mut(node)
+ }
+
+ fn contains_edge_key(&self, key: &Self::EdgeKey) -> bool {
+ let hash = ValueHash::new(&self.hasher, key);
+
+ self.edges.contains_key(&hash)
+ }
+
+ fn edge_by_key(&self, key: &Self::EdgeKey) -> Option<Edge<Self>> {
+ let hash = ValueHash::new(&self.hasher, key);
+
+ self.edges.get(&hash).and_then(|id| self.edge(*id))
+ }
+
+ fn edge_by_key_mut(&mut self, key: &Self::EdgeKey) -> Option<EdgeMut<Self>> {
+ let hash = ValueHash::new(&self.hasher, key);
+
+ let &edge = self.edges.get(&hash)?;
+
+ self.edge_mut(edge)
+ }
+}
diff --git a/crates/entry/src/sequential.rs b/crates/entry/src/sequential.rs
new file mode 100644
index 0000000..4c52145
--- /dev/null
+++ b/crates/entry/src/sequential.rs
@@ -0,0 +1,23 @@
+use core::hash::Hash;
+
+use petgraph_core::{storage::SequentialGraphStorage, GraphDirectionality};
+
+use crate::{Backend, EntryStorage};
+
+impl<NK, NV, EK, EV, D> SequentialGraphStorage for EntryStorage<NK, NV, EK, EV, D>
+where
+ D: GraphDirectionality,
+ NK: Hash,
+ EK: Hash,
+{
+ type EdgeIdBijection<'a> = <Backend<NK, NV, EK, EV, D> as SequentialGraphStorage>::EdgeIdBijection<'a> where Self: 'a;
+ type NodeIdBijection<'a> = <Backend<NK, NV, EK, EV, D> as SequentialGraphStorage>::NodeIdBijection<'a> where Self: 'a;
+
+ fn node_id_bijection(&self) -> Self::NodeIdBijection<'_> {
+ self.inner.node_id_bijection()
+ }
+
+ fn edge_id_bijection(&self) -> Self::EdgeIdBijection<'_> {
+ self.inner.edge_id_bijection()
+ }
+}
diff --git a/crates/graphmap/tests/snapshots/test_serde__serialize.snap b/crates/entry/tests/snapshots/test_serde__serialize.snap
similarity index 100%
rename from crates/graphmap/tests/snapshots/test_serde__serialize.snap
rename to crates/entry/tests/snapshots/test_serde__serialize.snap
diff --git a/crates/graphmap/tests/snapshots/test_serde__serialize_struct.snap b/crates/entry/tests/snapshots/test_serde__serialize_struct.snap
similarity index 100%
rename from crates/graphmap/tests/snapshots/test_serde__serialize_struct.snap
rename to crates/entry/tests/snapshots/test_serde__serialize_struct.snap
diff --git a/crates/graphmap/tests/test_directed.rs b/crates/entry/tests/test_directed.BAK
similarity index 92%
rename from crates/graphmap/tests/test_directed.rs
rename to crates/entry/tests/test_directed.BAK
index 7797984..9f18d45 100644
--- a/crates/graphmap/tests/test_directed.rs
+++ b/crates/entry/tests/test_directed.BAK
@@ -5,7 +5,7 @@
};
#[cfg(feature = "convert")]
use petgraph_graph::{Graph, NodeIndex};
-use petgraph_graphmap::{DiGraphMap, GraphMap};
+use petgraph_graphmap::{DiGraphMap, EntryStorage};
#[test]
fn self_loop() {
@@ -97,11 +97,10 @@
assert_eq!(graphmap.edges(1).collect::<Vec<_>>(), vec![(1, 2, &-2)]);
assert_eq!(graphmap.edges(2).collect::<Vec<_>>(), vec![(2, 0, &-3)]);
- assert_eq!(graphmap.all_edges().collect::<Vec<_>>(), vec![
- (0, 1, &-1),
- (1, 2, &-2),
- (2, 0, &-3),
- ]);
+ assert_eq!(
+ graphmap.all_edges().collect::<Vec<_>>(),
+ vec![(0, 1, &-1), (1, 2, &-2), (2, 0, &-3),]
+ );
}
#[test]
@@ -121,12 +120,15 @@
assert_eq!(graph.node_count(), 4);
assert_eq!(graph.edge_count(), 3);
- assert_eq!(graph.node_references().collect::<Vec<_>>(), vec![
- (NodeIndex::new(0), &0),
- (NodeIndex::new(1), &1),
- (NodeIndex::new(2), &2),
- (NodeIndex::new(3), &3),
- ]);
+ assert_eq!(
+ graph.node_references().collect::<Vec<_>>(),
+ vec![
+ (NodeIndex::new(0), &0),
+ (NodeIndex::new(1), &1),
+ (NodeIndex::new(2), &2),
+ (NodeIndex::new(3), &3),
+ ]
+ );
assert_eq!(
graph
diff --git a/crates/graphmap/tests/test_proptest.rs b/crates/entry/tests/test_proptest.BAK
similarity index 85%
rename from crates/graphmap/tests/test_proptest.rs
rename to crates/entry/tests/test_proptest.BAK
index 57872d7..ffd0de2 100644
--- a/crates/graphmap/tests/test_proptest.rs
+++ b/crates/entry/tests/test_proptest.BAK
@@ -7,10 +7,10 @@
edge::{Directed, EdgeType, Undirected},
visit::IntoNodeIdentifiers,
};
-use petgraph_graphmap::{GraphMap, NodeTrait};
+use petgraph_graphmap::{EntryStorage, NodeTrait};
use proptest::prelude::*;
-fn assert_graphmap_consistent<N, E, Ty>(g: &GraphMap<N, E, Ty>)
+fn assert_graphmap_consistent<N, E, Ty>(g: &EntryStorage<N, E, Ty>)
where
Ty: EdgeType,
N: NodeTrait + fmt::Debug,
@@ -36,7 +36,7 @@
}
}
-fn remove_node<Ty>(graph: &mut GraphMap<i8, (), Ty>, node: i8)
+fn remove_node<Ty>(graph: &mut EntryStorage<i8, (), Ty>, node: i8)
where
Ty: EdgeType,
{
@@ -49,7 +49,7 @@
assert!(!graph.contains_node(node));
}
-fn remove_edge<Ty>(graph: &mut GraphMap<i8, (), Ty>, a: i8, b: i8)
+fn remove_edge<Ty>(graph: &mut EntryStorage<i8, (), Ty>, a: i8, b: i8)
where
Ty: EdgeType,
{
@@ -64,7 +64,7 @@
assert!(!graph.neighbors(a).any(|x| x == b));
}
-fn add_remove_edge<Ty>(graph: &mut GraphMap<i8, (), Ty>, a: i8, b: i8)
+fn add_remove_edge<Ty>(graph: &mut EntryStorage<i8, (), Ty>, a: i8, b: i8)
where
Ty: EdgeType,
{
@@ -90,7 +90,7 @@
}
}
-fn find_free_edge<Ty>(graph: &GraphMap<i8, (), Ty>) -> (i8, i8)
+fn find_free_edge<Ty>(graph: &EntryStorage<i8, (), Ty>) -> (i8, i8)
where
Ty: EdgeType,
{
@@ -107,11 +107,11 @@
panic!("no free edge found");
}
-fn graph_and_node<Ty>() -> impl Strategy<Value = (GraphMap<i8, (), Ty>, i8)>
+fn graph_and_node<Ty>() -> impl Strategy<Value = (EntryStorage<i8, (), Ty>, i8)>
where
Ty: EdgeType + Clone + 'static,
{
- any::<GraphMap<i8, (), Ty>>()
+ any::<EntryStorage<i8, (), Ty>>()
.prop_filter("graph must have nodes", |graph| graph.node_count() > 0)
.prop_flat_map(|graph| {
let nodes = graph.node_count();
@@ -125,11 +125,11 @@
})
}
-fn graph_and_edge<Ty>() -> impl Strategy<Value = (GraphMap<i8, (), Ty>, (i8, i8))>
+fn graph_and_edge<Ty>() -> impl Strategy<Value = (EntryStorage<i8, (), Ty>, (i8, i8))>
where
Ty: EdgeType + Clone + 'static,
{
- any::<GraphMap<i8, (), Ty>>()
+ any::<EntryStorage<i8, (), Ty>>()
.prop_filter("graph must have edges", |graph| graph.edge_count() > 0)
.prop_flat_map(|graph| {
let edges = graph.edge_count();
@@ -143,11 +143,11 @@
})
}
-fn at_least_one_free_edge<Ty>() -> impl Strategy<Value = GraphMap<i8, (), Ty>>
+fn at_least_one_free_edge<Ty>() -> impl Strategy<Value = EntryStorage<i8, (), Ty>>
where
Ty: EdgeType + 'static,
{
- any::<GraphMap<i8, (), Ty>>()
+ any::<EntryStorage<i8, (), Ty>>()
.prop_filter("graph must have at least one node", |graph| {
graph.node_count() > 0
})
diff --git a/crates/graphmap/tests/test_serde.rs b/crates/entry/tests/test_serde.BAK
similarity index 81%
rename from crates/graphmap/tests/test_serde.rs
rename to crates/entry/tests/test_serde.BAK
index 0486560..4f16001 100644
--- a/crates/graphmap/tests/test_serde.rs
+++ b/crates/entry/tests/test_serde.BAK
@@ -5,15 +5,15 @@
use insta::assert_json_snapshot;
use petgraph_core::edge::{Directed, EdgeType};
use petgraph_graph::Graph;
-use petgraph_graphmap::{GraphMap, NodeTrait};
+use petgraph_graphmap::{EntryStorage, NodeTrait};
#[cfg(feature = "proptest")]
use proptest::prelude::*;
use serde::{Deserialize, Serialize};
-fn example() -> GraphMap<i32, u32, Directed> {
+fn example() -> EntryStorage<i32, u32, Directed> {
let [a, b, c, d, e, f] = [0, 1, 2, 3, 4, 5];
- let mut graph = GraphMap::from_edges([
+ let mut graph = EntryStorage::from_edges([
(a, b, 7),
(c, a, 9),
(a, d, 14),
@@ -31,7 +31,7 @@
graph
}
-fn assert_graph_eq<N, E, Ty>(graph1: &GraphMap<N, E, Ty>, graph2: &GraphMap<N, E, Ty>)
+fn assert_graph_eq<N, E, Ty>(graph1: &EntryStorage<N, E, Ty>, graph2: &EntryStorage<N, E, Ty>)
where
N: NodeTrait + PartialEq + Debug,
E: PartialEq + Debug,
@@ -60,7 +60,8 @@
fn deserialize() {
let graph = example();
let serialized = serde_value::to_value(&graph).unwrap();
- let deserialized: GraphMap<i32, u32, Directed> = GraphMap::deserialize(serialized).unwrap();
+ let deserialized: EntryStorage<i32, u32, Directed> =
+ EntryStorage::deserialize(serialized).unwrap();
assert_graph_eq(&graph, &deserialized);
}
@@ -71,14 +72,14 @@
pub y: i32,
}
-fn example_struct() -> GraphMap<Point, i32, Directed> {
+fn example_struct() -> EntryStorage<Point, i32, Directed> {
let a = Point { x: 0, y: 0 };
let b = Point { x: 1, y: 1 };
let c = Point { x: 2, y: 2 };
let d = Point { x: 2, y: 1 };
- let mut graph = GraphMap::from_edges([
+ let mut graph = EntryStorage::from_edges([
(a, b, 1), //
(b, c, 2),
(c, a, 3),
@@ -102,7 +103,8 @@
let graph = example_struct();
let serialized = serde_value::to_value(&graph).unwrap();
- let deserialized: GraphMap<Point, i32, Directed> = GraphMap::deserialize(serialized).unwrap();
+ let deserialized: EntryStorage<Point, i32, Directed> =
+ EntryStorage::deserialize(serialized).unwrap();
assert_graph_eq(&graph, &deserialized);
}
diff --git a/crates/graphmap/tests/test_undirected.rs b/crates/entry/tests/test_undirected.BAK
similarity index 100%
rename from crates/graphmap/tests/test_undirected.rs
rename to crates/entry/tests/test_undirected.BAK
diff --git a/crates/graphmap/Cargo.toml b/crates/graphmap/Cargo.toml
deleted file mode 100644
index 3e271bc..0000000
--- a/crates/graphmap/Cargo.toml
+++ /dev/null
@@ -1,28 +0,0 @@
-[package]
-name = "petgraph-graphmap"
-version = "0.1.0"
-edition = "2021"
-
-# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
-[dependencies]
-fixedbitset.workspace = true
-funty.workspace = true
-indexmap.workspace = true
-petgraph-core = { workspace = true, features = ["alloc", "indexmap"] }
-petgraph-graph = { workspace = true, optional = true }
-serde = { workspace = true, optional = true, default-features = false, features = ["alloc"] }
-petgraph-proptest = { workspace = true, default-features = false, optional = true }
-proptest = { workspace = true, default-features = false, optional = true }
-fxhash = "0.2.1"
-
-[dev-dependencies]
-serde = { version = "1.0.130", features = ["derive"] }
-serde-value = "0.7.0"
-insta = { version = "1.29.0", features = ['serde', 'json'] }
-
-[features]
-convert = ["petgraph-graph"]
-default = ["std"]
-serde = ["dep:serde", "convert", "petgraph-graph?/serde"]
-std = ["petgraph-core/std", "indexmap/std", "serde?/std"]
-proptest = ["dep:proptest", "dep:petgraph-proptest"]
diff --git a/crates/graphmap/src/lib.rs b/crates/graphmap/src/lib.rs
deleted file mode 100644
index e6f2526..0000000
--- a/crates/graphmap/src/lib.rs
+++ /dev/null
@@ -1,1241 +0,0 @@
-#![cfg_attr(not(feature = "std"), no_std)]
-//! `GraphMap<N, E, Ty>` is a graph datastructure where node values are mapping
-//! keys.
-
-#[cfg(feature = "proptest")]
-mod proptest;
-
-extern crate alloc;
-extern crate core;
-
-use alloc::vec::Vec;
-use core::{
- cmp::Ordering,
- fmt, hash,
- hash::Hash,
- iter,
- marker::PhantomData,
- mem,
- ops::{Deref, Index, IndexMut},
- slice,
-};
-
-use fxhash::FxBuildHasher;
-use indexmap::map::{Iter as IndexMapIter, IterMut as IndexMapIterMut, Keys};
-use petgraph_core::{
- deprecated::{
- edge::{Directed, EdgeType, Undirected},
- index::IndexType,
- visit, IntoWeightedEdge,
- },
- edge::Direction,
- iterator_wrap,
-};
-
-type IndexMap<K, V> = indexmap::IndexMap<K, V, FxBuildHasher>;
-type IndexSet<K> = indexmap::IndexSet<K, FxBuildHasher>;
-
-#[cfg(feature = "convert")]
-use petgraph_graph::{node_index, Graph};
-
-/// A `GraphMap` with undirected edges.
-///
-/// For example, an edge between *1* and *2* is equivalent to an edge between
-/// *2* and *1*.
-pub type UnGraphMap<N, E> = GraphMap<N, E, Undirected>;
-
-/// A `GraphMap` with directed edges.
-///
-/// For example, an edge from *1* to *2* is distinct from an edge from *2* to
-/// *1*.
-pub type DiGraphMap<N, E> = GraphMap<N, E, Directed>;
-
-/// `GraphMap<N, E, Ty>` is a graph datastructure using an associative array
-/// of its node weights `N`.
-///
-/// It uses an combined adjacency list and sparse adjacency matrix
-/// representation, using **O(|V| + |E|)** space, and allows testing for edge
-/// existence in constant time.
-///
-/// `GraphMap` is parameterized over:
-///
-/// - Associated data `N` for nodes and `E` for edges, called *weights*.
-/// - The node weight `N` must implement `Copy` and will be used as node
-/// identifier, duplicated into several places in the data structure.
-/// It must be suitable as a hash table key (implementing `Eq + Hash`).
-/// The node type must also implement `Ord` so that the implementation can
-/// order the pair (`a`, `b`) for an edge connecting any two nodes `a` and `b`.
-/// - `E` can be of arbitrary type.
-/// - Edge type `Ty` that determines whether the graph edges are directed or
-/// undirected.
-///
-/// You can use the type aliases `UnGraphMap` and `DiGraphMap` for convenience.
-///
-/// `GraphMap` does not allow parallel edges, but self loops are allowed.
-///
-/// Depends on crate feature `graphmap` (default).
-#[derive(Clone)]
-pub struct GraphMap<N, E, Ty> {
- nodes: IndexMap<N, Vec<(N, Direction)>>,
- edges: IndexMap<(N, N), E>,
- ty: PhantomData<Ty>,
-}
-
-impl<N: fmt::Debug, E: fmt::Debug, Ty: EdgeType> fmt::Debug for GraphMap<N, E, Ty> {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- fmt::Debug::fmt(&self.nodes, f)
- }
-}
-
-/// A trait group for `GraphMap`'s node identifier.
-pub trait NodeTrait: Copy + Ord + Hash {}
-impl<N> NodeTrait for N where N: Copy + Ord + Hash {}
-
-#[cfg(feature = "serde")]
-impl<N, E, Ty> serde::Serialize for GraphMap<N, E, Ty>
-where
- Ty: EdgeType,
- N: NodeTrait + serde::Serialize,
- E: serde::Serialize,
- GraphMap<N, E, Ty>: Clone,
-{
- /// Serializes the given `GraphMap` into the same format as the standard
- /// `Graph`. Needs feature `serde-1`.
- ///
- /// Note: the graph has to be `Clone` for this to work.
- fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
- where
- S: serde::Serializer,
- {
- let cloned_graph: GraphMap<N, E, Ty> = GraphMap::clone(self);
- let equivalent_graph: Graph<N, E, Ty, u32> = cloned_graph.into_graph();
- equivalent_graph.serialize(serializer)
- }
-}
-
-#[cfg(feature = "serde")]
-impl<'de, N, E, Ty> serde::Deserialize<'de> for GraphMap<N, E, Ty>
-where
- Ty: EdgeType,
- N: NodeTrait + serde::Deserialize<'de>,
- E: Clone + serde::Deserialize<'de>,
-{
- /// Deserializes into a new `GraphMap` from the same format as the standard
- /// `Graph`. Needs feature `serde-1`.
- ///
- /// **Warning**: When deseralizing a graph that was not originally a `GraphMap`,
- /// the restrictions from [`from_graph`](#method.from_graph) apply.
- ///
- /// Note: The edge weights have to be `Clone` for this to work.
- fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
- where
- D: serde::Deserializer<'de>,
- {
- let equivalent_graph: Graph<N, E, Ty, u32> = Graph::deserialize(deserializer)?;
- Ok(GraphMap::from_graph(equivalent_graph))
- }
-}
-
-impl<N, E, Ty> GraphMap<N, E, Ty>
-where
- N: NodeTrait,
- Ty: EdgeType,
-{
- /// Create a new `GraphMap`
- pub fn new() -> Self {
- Self::default()
- }
-
- /// Create a new `GraphMap` with estimated capacity.
- pub fn with_capacity(nodes: usize, edges: usize) -> Self {
- GraphMap {
- nodes: IndexMap::with_capacity_and_hasher(nodes, FxBuildHasher::default()),
- edges: IndexMap::with_capacity_and_hasher(edges, FxBuildHasher::default()),
- ty: PhantomData,
- }
- }
-
- /// Return the current node and edge capacity of the graph.
- pub fn capacity(&self) -> (usize, usize) {
- (self.nodes.capacity(), self.edges.capacity())
- }
-
- /// Use their natural order to map the node pair (a, b) to a canonical edge id.
- #[inline]
- fn edge_key(a: N, b: N) -> (N, N) {
- if Ty::is_directed() || a <= b {
- (a, b)
- } else {
- (b, a)
- }
- }
-
- /// Whether the graph has directed edges.
- pub fn is_directed(&self) -> bool {
- Ty::is_directed()
- }
-
- /// Create a new `GraphMap` from an iterable of edges.
- ///
- /// Node values are taken directly from the list.
- /// Edge weights `E` may either be specified in the list,
- /// or they are filled with default values.
- ///
- /// Nodes are inserted automatically to match the edges.
- ///
- /// ```
- /// use petgraph_graphmap::UnGraphMap;
- ///
- /// // Create a new undirected GraphMap.
- /// // Use a type hint to have `()` be the edge weight type.
- /// let gr = UnGraphMap::<_, ()>::from_edges(&[(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)]);
- /// ```
- pub fn from_edges<I>(iterable: I) -> Self
- where
- I: IntoIterator,
- I::Item: IntoWeightedEdge<E, NodeId = N>,
- {
- Self::from_iter(iterable)
- }
-
- /// Return the number of nodes in the graph.
- pub fn node_count(&self) -> usize {
- self.nodes.len()
- }
-
- /// Return the number of edges in the graph.
- pub fn edge_count(&self) -> usize {
- self.edges.len()
- }
-
- /// Remove all nodes and edges
- pub fn clear(&mut self) {
- self.nodes.clear();
- self.edges.clear();
- }
-
- /// Add node `n` to the graph.
- pub fn add_node(&mut self, n: N) -> N {
- self.nodes.entry(n).or_insert(Vec::new());
- n
- }
-
- /// Return `true` if node `n` was removed.
- ///
- /// Computes in **O(V)** time, due to the removal of edges with other nodes.
- pub fn remove_node(&mut self, n: N) -> bool {
- let links = match self.nodes.swap_remove(&n) {
- None => return false,
- Some(sus) => sus,
- };
- for (succ, dir) in links {
- let edge = if dir == Direction::Outgoing {
- Self::edge_key(n, succ)
- } else {
- Self::edge_key(succ, n)
- };
- // remove all successor links
- self.remove_single_edge(&succ, &n, dir.reverse());
- // Remove all edge values
- self.edges.swap_remove(&edge);
- }
- true
- }
-
- /// Return `true` if the node is contained in the graph.
- pub fn contains_node(&self, n: N) -> bool {
- self.nodes.contains_key(&n)
- }
-
- /// Add an edge connecting `a` and `b` to the graph, with associated
- /// data `weight`. For a directed graph, the edge is directed from `a`
- /// to `b`.
- ///
- /// Inserts nodes `a` and/or `b` if they aren't already part of the graph.
- ///
- /// Return `None` if the edge did not previously exist, otherwise,
- /// the associated data is updated and the old value is returned
- /// as `Some(old_weight)`.
- ///
- /// ```
- /// // Create a GraphMap with directed edges, and add one edge to it
- /// use petgraph_graphmap::DiGraphMap;
- ///
- /// let mut g = DiGraphMap::new();
- /// g.add_edge("x", "y", -1);
- /// assert_eq!(g.node_count(), 2);
- /// assert_eq!(g.edge_count(), 1);
- /// assert!(g.contains_edge("x", "y"));
- /// assert!(!g.contains_edge("y", "x"));
- /// ```
- pub fn add_edge(&mut self, a: N, b: N, weight: E) -> Option<E> {
- if let old @ Some(_) = self.edges.insert(Self::edge_key(a, b), weight) {
- old
- } else {
- // insert in the adjacency list if it's a new edge
- self.nodes
- .entry(a)
- .or_insert_with(|| Vec::with_capacity(1))
- .push((b, Direction::Outgoing));
- if a != b {
- // self loops don't have the Incoming entry
- self.nodes
- .entry(b)
- .or_insert_with(|| Vec::with_capacity(1))
- .push((a, Direction::Incoming));
- }
- None
- }
- }
-
- /// Remove edge relation from a to b
- ///
- /// Return `true` if it did exist.
- fn remove_single_edge(&mut self, a: &N, b: &N, dir: Direction) -> bool {
- match self.nodes.get_mut(a) {
- None => false,
- Some(sus) => {
- if Ty::is_directed() {
- match sus.iter().position(|elt| elt == &(*b, dir)) {
- Some(index) => {
- sus.swap_remove(index);
- true
- }
- None => false,
- }
- } else {
- match sus.iter().position(|elt| &elt.0 == b) {
- Some(index) => {
- sus.swap_remove(index);
- true
- }
- None => false,
- }
- }
- }
- }
- }
-
- /// Remove edge from `a` to `b` from the graph and return the edge weight.
- ///
- /// Return `None` if the edge didn't exist.
- ///
- /// ```
- /// // Create a GraphMap with undirected edges, and add and remove an edge.
- /// use petgraph_graphmap::UnGraphMap;
- ///
- /// let mut g = UnGraphMap::new();
- /// g.add_edge("x", "y", -1);
- ///
- /// let edge_data = g.remove_edge("y", "x");
- /// assert_eq!(edge_data, Some(-1));
- /// assert_eq!(g.edge_count(), 0);
- /// ```
- pub fn remove_edge(&mut self, a: N, b: N) -> Option<E> {
- let exist1 = self.remove_single_edge(&a, &b, Direction::Outgoing);
- let exist2 = if a != b {
- self.remove_single_edge(&b, &a, Direction::Incoming)
- } else {
- exist1
- };
- let weight = self.edges.remove(&Self::edge_key(a, b));
- debug_assert!(exist1 == exist2 && exist1 == weight.is_some());
- weight
- }
-
- /// Return `true` if the edge connecting `a` with `b` is contained in the graph.
- pub fn contains_edge(&self, a: N, b: N) -> bool {
- self.edges.contains_key(&Self::edge_key(a, b))
- }
-
- /// Return an iterator over the nodes of the graph.
- ///
- /// Iterator element type is `N`.
- pub fn nodes(&self) -> Nodes<N> {
- Nodes {
- iter: self.nodes.keys().cloned(),
- }
- }
-
- /// Return an iterator of all nodes with an edge starting from `a`.
- ///
- /// - `Directed`: Outgoing edges from `a`.
- /// - `Undirected`: All edges from or to `a`.
- ///
- /// Produces an empty iterator if the node doesn't exist.<br>
- /// Iterator element type is `N`.
- pub fn neighbors(&self, a: N) -> Neighbors<N, Ty> {
- Neighbors {
- iter: match self.nodes.get(&a) {
- Some(neigh) => neigh.iter(),
- None => [].iter(),
- },
- ty: self.ty,
- }
- }
-
- /// Return an iterator of all neighbors that have an edge between them and
- /// `a`, in the specified direction.
- /// If the graph's edges are undirected, this is equivalent to *.neighbors(a)*.
- ///
- /// - `Directed`, `Outgoing`: All edges from `a`.
- /// - `Directed`, `Incoming`: All edges to `a`.
- /// - `Undirected`: All edges from or to `a`.
- ///
- /// Produces an empty iterator if the node doesn't exist.<br>
- /// Iterator element type is `N`.
- pub fn neighbors_directed(&self, a: N, dir: Direction) -> NeighborsDirected<N, Ty> {
- NeighborsDirected {
- iter: match self.nodes.get(&a) {
- Some(neigh) => neigh.iter(),
- None => [].iter(),
- },
- start_node: a,
- dir,
- ty: self.ty,
- }
- }
-
- /// Return an iterator of target nodes with an edge starting from `a`,
- /// paired with their respective edge weights.
- ///
- /// - `Directed`: Outgoing edges from `a`.
- /// - `Undirected`: All edges from or to `a`.
- ///
- /// Produces an empty iterator if the node doesn't exist.<br>
- /// Iterator element type is `(N, N, &E)`.
- pub fn edges(&self, a: N) -> Edges<N, E, Ty> {
- Edges {
- from: a,
- iter: self.neighbors(a),
- edges: &self.edges,
- }
- }
-
- /// Return an iterator of target nodes with an edge starting from `a`,
- /// paired with their respective edge weights.
- ///
- /// - `Directed`, `Outgoing`: All edges from `a`.
- /// - `Directed`, `Incoming`: All edges to `a`.
- /// - `Undirected`, `Outgoing`: All edges connected to `a`, with `a` being the source of each
- /// edge.
- /// - `Undirected`, `Incoming`: All edges connected to `a`, with `a` being the target of each
- /// edge.
- ///
- /// Produces an empty iterator if the node doesn't exist.<br>
- /// Iterator element type is `(N, N, &E)`.
- pub fn edges_directed(&self, a: N, dir: Direction) -> EdgesDirected<N, E, Ty> {
- EdgesDirected {
- from: a,
- iter: self.neighbors_directed(a, dir),
- dir,
- edges: &self.edges,
- }
- }
-
- /// Return a reference to the edge weight connecting `a` with `b`, or
- /// `None` if the edge does not exist in the graph.
- pub fn edge_weight(&self, a: N, b: N) -> Option<&E> {
- self.edges.get(&Self::edge_key(a, b))
- }
-
- /// Return a mutable reference to the edge weight connecting `a` with `b`, or
- /// `None` if the edge does not exist in the graph.
- pub fn edge_weight_mut(&mut self, a: N, b: N) -> Option<&mut E> {
- self.edges.get_mut(&Self::edge_key(a, b))
- }
-
- /// Return an iterator over all edges of the graph with their weight in arbitrary order.
- ///
- /// Iterator element type is `(N, N, &E)`
- pub fn all_edges(&self) -> AllEdges<N, E, Ty> {
- AllEdges {
- inner: self.edges.iter(),
- ty: self.ty,
- }
- }
-
- /// Return an iterator over all edges of the graph in arbitrary order, with a mutable reference
- /// to their weight.
- ///
- /// Iterator element type is `(N, N, &mut E)`
- pub fn all_edges_mut(&mut self) -> AllEdgesMut<N, E, Ty> {
- AllEdgesMut {
- inner: self.edges.iter_mut(),
- ty: self.ty,
- }
- }
-
- /// Return a `Graph` that corresponds to this `GraphMap`.
- ///
- /// 1. Note that node and edge indices in the `Graph` have nothing in common with the
- /// `GraphMap`s node weights `N`. The node weights `N` are used as node weights in the
- /// resulting `Graph`, too.
- /// 2. Note that the index type is user-chosen.
- ///
- /// Computes in **O(|V| + |E|)** time (average).
- ///
- /// **Panics** if the number of nodes or edges does not fit with
- /// the resulting graph's index type.
- #[cfg(feature = "convert")]
- pub fn into_graph<Ix>(self) -> Graph<N, E, Ty, Ix>
- where
- Ix: IndexType,
- {
- // assuming two successive iterations of the same hashmap produce the same order
- let mut gr = Graph::with_capacity(self.node_count(), self.edge_count());
- for (&node, _) in &self.nodes {
- gr.add_node(node);
- }
- for ((a, b), edge_weight) in self.edges {
- let (ai, ..) = self.nodes.get_full(&a).unwrap();
- let (bi, ..) = self.nodes.get_full(&b).unwrap();
- gr.add_edge(node_index(ai), node_index(bi), edge_weight);
- }
- gr
- }
-
- /// Creates a `GraphMap` that corresponds to the given `Graph`.
- ///
- /// **Warning**: Nodes with the same weight are merged and only the last parallel edge
- /// is kept. Node and edge indices of the `Graph` are lost. Only use this function
- /// if the node weights are distinct and there are no parallel edges.
- ///
- /// Computes in **O(|V| + |E|)** time (average).
- #[cfg(feature = "convert")]
- pub fn from_graph<Ix>(graph: Graph<N, E, Ty, Ix>) -> Self
- where
- Ix: IndexType,
- E: Clone,
- {
- let mut new_graph: GraphMap<N, E, Ty> =
- GraphMap::with_capacity(graph.node_count(), graph.edge_count());
-
- for node in graph.raw_nodes() {
- new_graph.add_node(node.weight);
- }
-
- for edge in graph.edge_indices() {
- let (a, b) = graph.edge_endpoints(edge).unwrap();
- new_graph.add_edge(
- *graph.node_weight(a).unwrap(),
- *graph.node_weight(b).unwrap(),
- graph.edge_weight(edge).unwrap().clone(),
- );
- }
-
- new_graph
- }
-}
-
-/// Create a new `GraphMap` from an iterable of edges.
-impl<N, E, Ty, Item> FromIterator<Item> for GraphMap<N, E, Ty>
-where
- Item: IntoWeightedEdge<E, NodeId = N>,
- N: NodeTrait,
- Ty: EdgeType,
-{
- fn from_iter<I>(iterable: I) -> Self
- where
- I: IntoIterator<Item = Item>,
- {
- let iter = iterable.into_iter();
- let (low, _) = iter.size_hint();
- let mut g = Self::with_capacity(0, low);
- g.extend(iter);
- g
- }
-}
-
-/// Extend the graph from an iterable of edges.
-///
-/// Nodes are inserted automatically to match the edges.
-impl<N, E, Ty, Item> Extend<Item> for GraphMap<N, E, Ty>
-where
- Item: IntoWeightedEdge<E, NodeId = N>,
- N: NodeTrait,
- Ty: EdgeType,
-{
- fn extend<I>(&mut self, iterable: I)
- where
- I: IntoIterator<Item = Item>,
- {
- let iter = iterable.into_iter();
- let (low, _) = iter.size_hint();
- self.edges.reserve(low);
-
- for elt in iter {
- let (source, target, weight) = elt.into_weighted_edge();
- self.add_edge(source, target, weight);
- }
- }
-}
-
-iterator_wrap! {
- impl (Iterator DoubleEndedIterator ExactSizeIterator) for
- #[derive(Debug, Clone)]
- struct Nodes <'a, N> where { N: 'a + NodeTrait }
- item: N,
- iter: iter::Cloned<Keys<'a, N, Vec<(N, Direction)>>>,
-}
-
-#[derive(Debug, Clone)]
-pub struct Neighbors<'a, N, Ty = Undirected>
-where
- N: 'a,
- Ty: EdgeType,
-{
- iter: slice::Iter<'a, (N, Direction)>,
- ty: PhantomData<Ty>,
-}
-
-impl<'a, N, Ty> Iterator for Neighbors<'a, N, Ty>
-where
- N: NodeTrait,
- Ty: EdgeType,
-{
- type Item = N;
-
- fn next(&mut self) -> Option<N> {
- if Ty::is_directed() {
- (&mut self.iter)
- .filter_map(|&(n, dir)| {
- if dir == Direction::Outgoing {
- Some(n)
- } else {
- None
- }
- })
- .next()
- } else {
- self.iter.next().map(|&(n, _)| n)
- }
- }
-
- fn size_hint(&self) -> (usize, Option<usize>) {
- let (lower, upper) = self.iter.size_hint();
- if Ty::is_directed() {
- (0, upper)
- } else {
- (lower, upper)
- }
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct NeighborsDirected<'a, N, Ty>
-where
- N: 'a,
- Ty: EdgeType,
-{
- iter: slice::Iter<'a, (N, Direction)>,
- start_node: N,
- dir: Direction,
- ty: PhantomData<Ty>,
-}
-
-impl<'a, N, Ty> Iterator for NeighborsDirected<'a, N, Ty>
-where
- N: NodeTrait,
- Ty: EdgeType,
-{
- type Item = N;
-
- fn next(&mut self) -> Option<N> {
- if Ty::is_directed() {
- let self_dir = self.dir;
- let start_node = self.start_node;
- (&mut self.iter)
- .filter_map(move |&(n, dir)| {
- if dir == self_dir || n == start_node {
- Some(n)
- } else {
- None
- }
- })
- .next()
- } else {
- self.iter.next().map(|&(n, _)| n)
- }
- }
-
- fn size_hint(&self) -> (usize, Option<usize>) {
- let (lower, upper) = self.iter.size_hint();
- if Ty::is_directed() {
- (0, upper)
- } else {
- (lower, upper)
- }
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Edges<'a, N, E: 'a, Ty>
-where
- N: 'a + NodeTrait,
- Ty: EdgeType,
-{
- from: N,
- edges: &'a IndexMap<(N, N), E>,
- iter: Neighbors<'a, N, Ty>,
-}
-
-impl<'a, N, E, Ty> Iterator for Edges<'a, N, E, Ty>
-where
- N: 'a + NodeTrait,
- E: 'a,
- Ty: EdgeType,
-{
- type Item = (N, N, &'a E);
-
- fn next(&mut self) -> Option<Self::Item> {
- self.iter.next().map(|b| {
- let a = self.from;
- match self.edges.get(&GraphMap::<N, E, Ty>::edge_key(a, b)) {
- None => unreachable!(),
- Some(edge) => (a, b, edge),
- }
- })
- }
-
- fn size_hint(&self) -> (usize, Option<usize>) {
- self.iter.size_hint()
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct EdgesDirected<'a, N, E: 'a, Ty>
-where
- N: 'a + NodeTrait,
- Ty: EdgeType,
-{
- from: N,
- dir: Direction,
- edges: &'a IndexMap<(N, N), E>,
- iter: NeighborsDirected<'a, N, Ty>,
-}
-
-impl<'a, N, E, Ty> Iterator for EdgesDirected<'a, N, E, Ty>
-where
- N: 'a + NodeTrait,
- E: 'a,
- Ty: EdgeType,
-{
- type Item = (N, N, &'a E);
-
- fn next(&mut self) -> Option<Self::Item> {
- self.iter.next().map(|mut b| {
- let mut a = self.from;
- if self.dir == Direction::Incoming {
- mem::swap(&mut a, &mut b);
- }
- match self.edges.get(&GraphMap::<N, E, Ty>::edge_key(a, b)) {
- None => unreachable!(),
- Some(edge) => (a, b, edge),
- }
- })
- }
-
- fn size_hint(&self) -> (usize, Option<usize>) {
- self.iter.size_hint()
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct AllEdges<'a, N, E: 'a, Ty>
-where
- N: 'a + NodeTrait,
-{
- inner: IndexMapIter<'a, (N, N), E>,
- ty: PhantomData<Ty>,
-}
-
-impl<'a, N, E, Ty> Iterator for AllEdges<'a, N, E, Ty>
-where
- N: 'a + NodeTrait,
- E: 'a,
- Ty: EdgeType,
-{
- type Item = (N, N, &'a E);
-
- fn next(&mut self) -> Option<Self::Item> {
- self.inner.next().map(|(&(a, b), v)| (a, b, v))
- }
-
- fn size_hint(&self) -> (usize, Option<usize>) {
- self.inner.size_hint()
- }
-
- fn count(self) -> usize {
- self.inner.count()
- }
-
- fn nth(&mut self, n: usize) -> Option<Self::Item> {
- self.inner
- .nth(n)
- .map(|(&(n1, n2), weight)| (n1, n2, weight))
- }
-
- fn last(self) -> Option<Self::Item> {
- self.inner
- .last()
- .map(|(&(n1, n2), weight)| (n1, n2, weight))
- }
-}
-
-impl<'a, N, E, Ty> DoubleEndedIterator for AllEdges<'a, N, E, Ty>
-where
- N: 'a + NodeTrait,
- E: 'a,
- Ty: EdgeType,
-{
- fn next_back(&mut self) -> Option<Self::Item> {
- self.inner
- .next_back()
- .map(|(&(n1, n2), weight)| (n1, n2, weight))
- }
-}
-
-pub struct AllEdgesMut<'a, N, E: 'a, Ty>
-where
- N: 'a + NodeTrait,
-{
- inner: IndexMapIterMut<'a, (N, N), E>, /* TODO: change to something that implements Debug +
- * Clone? */
- ty: PhantomData<Ty>,
-}
-
-impl<'a, N, E, Ty> Iterator for AllEdgesMut<'a, N, E, Ty>
-where
- N: 'a + NodeTrait,
- E: 'a,
- Ty: EdgeType,
-{
- type Item = (N, N, &'a mut E);
-
- fn next(&mut self) -> Option<Self::Item> {
- self.inner
- .next()
- .map(|(&(n1, n2), weight)| (n1, n2, weight))
- }
-
- fn size_hint(&self) -> (usize, Option<usize>) {
- self.inner.size_hint()
- }
-
- fn count(self) -> usize {
- self.inner.count()
- }
-
- fn nth(&mut self, n: usize) -> Option<Self::Item> {
- self.inner
- .nth(n)
- .map(|(&(n1, n2), weight)| (n1, n2, weight))
- }
-
- fn last(self) -> Option<Self::Item> {
- self.inner
- .last()
- .map(|(&(n1, n2), weight)| (n1, n2, weight))
- }
-}
-
-impl<'a, N, E, Ty> DoubleEndedIterator for AllEdgesMut<'a, N, E, Ty>
-where
- N: 'a + NodeTrait,
- E: 'a,
- Ty: EdgeType,
-{
- fn next_back(&mut self) -> Option<Self::Item> {
- self.inner
- .next_back()
- .map(|(&(n1, n2), weight)| (n1, n2, weight))
- }
-}
-
-/// Index `GraphMap` by node pairs to access edge weights.
-impl<N, E, Ty> Index<(N, N)> for GraphMap<N, E, Ty>
-where
- N: NodeTrait,
- Ty: EdgeType,
-{
- type Output = E;
-
- fn index(&self, index: (N, N)) -> &E {
- let index = Self::edge_key(index.0, index.1);
- self.edge_weight(index.0, index.1)
- .expect("GraphMap::index: no such edge")
- }
-}
-
-/// Index `GraphMap` by node pairs to access edge weights.
-impl<N, E, Ty> IndexMut<(N, N)> for GraphMap<N, E, Ty>
-where
- N: NodeTrait,
- Ty: EdgeType,
-{
- fn index_mut(&mut self, index: (N, N)) -> &mut E {
- let index = Self::edge_key(index.0, index.1);
- self.edge_weight_mut(index.0, index.1)
- .expect("GraphMap::index: no such edge")
- }
-}
-
-/// Create a new empty `GraphMap`.
-impl<N, E, Ty> Default for GraphMap<N, E, Ty>
-where
- N: NodeTrait,
- Ty: EdgeType,
-{
- fn default() -> Self {
- GraphMap::with_capacity(0, 0)
- }
-}
-
-/// A reference that is hashed and compared by its pointer value.
-///
-/// `Ptr` is used for certain configurations of `GraphMap`,
-/// in particular in the combination where the node type for
-/// `GraphMap` is something of type for example `Ptr(&Cell<T>)`,
-/// with the `Cell<T>` being `TypedArena` allocated.
-pub struct Ptr<'b, T: 'b>(pub &'b T);
-
-impl<'b, T> Copy for Ptr<'b, T> {}
-impl<'b, T> Clone for Ptr<'b, T> {
- fn clone(&self) -> Self {
- *self
- }
-}
-
-fn ptr_eq<T>(a: *const T, b: *const T) -> bool {
- a == b
-}
-
-impl<'b, T> PartialEq for Ptr<'b, T> {
- /// Ptr compares by pointer equality, i.e if they point to the same value
- fn eq(&self, other: &Ptr<'b, T>) -> bool {
- ptr_eq(self.0, other.0)
- }
-}
-
-impl<'b, T> PartialOrd for Ptr<'b, T> {
- fn partial_cmp(&self, other: &Ptr<'b, T>) -> Option<Ordering> {
- Some(self.cmp(other))
- }
-}
-
-impl<'b, T> Ord for Ptr<'b, T> {
- /// Ptr is ordered by pointer value, i.e. an arbitrary but stable and total order.
- fn cmp(&self, other: &Ptr<'b, T>) -> Ordering {
- let a: *const T = self.0;
- let b: *const T = other.0;
- a.cmp(&b)
- }
-}
-
-impl<'b, T> Deref for Ptr<'b, T> {
- type Target = T;
-
- fn deref(&self) -> &T {
- self.0
- }
-}
-
-impl<'b, T> Eq for Ptr<'b, T> {}
-
-impl<'b, T> Hash for Ptr<'b, T> {
- fn hash<H: hash::Hasher>(&self, st: &mut H) {
- let ptr = (self.0) as *const T;
- ptr.hash(st)
- }
-}
-
-impl<'b, T: fmt::Debug> fmt::Debug for Ptr<'b, T> {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- self.0.fmt(f)
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct NodeIdentifiers<'a, N, E: 'a, Ty>
-where
- N: 'a + NodeTrait,
-{
- iter: IndexMapIter<'a, N, Vec<(N, Direction)>>,
- ty: PhantomData<Ty>,
- edge_ty: PhantomData<E>,
-}
-
-impl<'a, N, E, Ty> Iterator for NodeIdentifiers<'a, N, E, Ty>
-where
- N: 'a + NodeTrait,
- E: 'a,
- Ty: EdgeType,
-{
- type Item = N;
-
- fn next(&mut self) -> Option<Self::Item> {
- self.iter.next().map(|(&n, _)| n)
- }
-
- fn size_hint(&self) -> (usize, Option<usize>) {
- self.iter.size_hint()
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct NodeReferences<'a, N, E: 'a, Ty>
-where
- N: 'a + NodeTrait,
-{
- iter: IndexMapIter<'a, N, Vec<(N, Direction)>>,
- ty: PhantomData<Ty>,
- edge_ty: PhantomData<E>,
-}
-
-impl<'a, N, E, Ty> Iterator for NodeReferences<'a, N, E, Ty>
-where
- N: 'a + NodeTrait,
- E: 'a,
- Ty: EdgeType,
-{
- type Item = (N, &'a N);
-
- fn next(&mut self) -> Option<Self::Item> {
- self.iter.next().map(|(n, _)| (*n, n))
- }
-
- fn size_hint(&self) -> (usize, Option<usize>) {
- self.iter.size_hint()
- }
-}
-
-impl<N, E, Ty> visit::GraphBase for GraphMap<N, E, Ty>
-where
- N: Copy + PartialEq,
-{
- type EdgeId = (N, N);
- type NodeId = N;
-}
-
-impl<N, E, Ty> visit::Data for GraphMap<N, E, Ty>
-where
- N: Copy + PartialEq,
- Ty: EdgeType,
-{
- type EdgeWeight = E;
- type NodeWeight = N;
-}
-
-impl<N, E, Ty> visit::Visitable for GraphMap<N, E, Ty>
-where
- N: Copy + Ord + Hash,
- Ty: EdgeType,
-{
- type Map = IndexSet<N>;
-
- fn visit_map(&self) -> IndexSet<N> {
- IndexSet::with_capacity_and_hasher(self.node_count(), FxBuildHasher::default())
- }
-
- fn reset_map(&self, map: &mut Self::Map) {
- map.clear();
- }
-}
-
-impl<N, E, Ty> visit::GraphProp for GraphMap<N, E, Ty>
-where
- N: NodeTrait,
- Ty: EdgeType,
-{
- type EdgeType = Ty;
-}
-
-impl<'a, N, E, Ty> visit::IntoNodeReferences for &'a GraphMap<N, E, Ty>
-where
- N: NodeTrait,
- Ty: EdgeType,
-{
- type NodeRef = (N, &'a N);
- type NodeReferences = NodeReferences<'a, N, E, Ty>;
-
- fn node_references(self) -> Self::NodeReferences {
- NodeReferences {
- iter: self.nodes.iter(),
- ty: self.ty,
- edge_ty: PhantomData,
- }
- }
-}
-
-impl<'a, N, E: 'a, Ty> visit::IntoNodeIdentifiers for &'a GraphMap<N, E, Ty>
-where
- N: NodeTrait,
- Ty: EdgeType,
-{
- type NodeIdentifiers = NodeIdentifiers<'a, N, E, Ty>;
-
- fn node_identifiers(self) -> Self::NodeIdentifiers {
- NodeIdentifiers {
- iter: self.nodes.iter(),
- ty: self.ty,
- edge_ty: PhantomData,
- }
- }
-}
-
-impl<N, E, Ty> visit::NodeCount for GraphMap<N, E, Ty>
-where
- N: NodeTrait,
- Ty: EdgeType,
-{
- fn node_count(&self) -> usize {
- (*self).node_count()
- }
-}
-
-impl<N, E, Ty> visit::NodeIndexable for GraphMap<N, E, Ty>
-where
- N: NodeTrait,
- Ty: EdgeType,
-{
- fn node_bound(&self) -> usize {
- self.node_count()
- }
-
- fn to_index(&self, ix: Self::NodeId) -> usize {
- let (i, ..) = self.nodes.get_full(&ix).unwrap();
- i
- }
-
- fn from_index(&self, ix: usize) -> Self::NodeId {
- assert!(
- ix < self.nodes.len(),
- "The requested index {} is out-of-bounds.",
- ix
- );
- let (&key, _) = self.nodes.get_index(ix).unwrap();
- key
- }
-}
-
-impl<N, E, Ty> visit::NodeCompactIndexable for GraphMap<N, E, Ty>
-where
- N: NodeTrait,
- Ty: EdgeType,
-{
-}
-
-impl<'a, N: 'a, E, Ty> visit::IntoNeighbors for &'a GraphMap<N, E, Ty>
-where
- N: Copy + Ord + Hash,
- Ty: EdgeType,
-{
- type Neighbors = Neighbors<'a, N, Ty>;
-
- fn neighbors(self, n: Self::NodeId) -> Self::Neighbors {
- self.neighbors(n)
- }
-}
-
-impl<'a, N: 'a, E, Ty> visit::IntoNeighborsDirected for &'a GraphMap<N, E, Ty>
-where
- N: Copy + Ord + Hash,
- Ty: EdgeType,
-{
- type NeighborsDirected = NeighborsDirected<'a, N, Ty>;
-
- fn neighbors_directed(self, n: N, dir: Direction) -> Self::NeighborsDirected {
- self.neighbors_directed(n, dir)
- }
-}
-
-impl<N, E, Ty> visit::EdgeIndexable for GraphMap<N, E, Ty>
-where
- N: NodeTrait,
- Ty: EdgeType,
-{
- fn edge_bound(&self) -> usize {
- self.edge_count()
- }
-
- fn to_index(&self, ix: Self::EdgeId) -> usize {
- let (i, ..) = self.edges.get_full(&ix).unwrap();
- i
- }
-
- fn from_index(&self, ix: usize) -> Self::EdgeId {
- assert!(
- ix < self.edges.len(),
- "The requested index {} is out-of-bounds.",
- ix
- );
- let (&key, _) = self.edges.get_index(ix).unwrap();
- key
- }
-}
-
-impl<'a, N: 'a, E: 'a, Ty> visit::IntoEdges for &'a GraphMap<N, E, Ty>
-where
- N: NodeTrait,
- Ty: EdgeType,
-{
- type Edges = Edges<'a, N, E, Ty>;
-
- fn edges(self, a: Self::NodeId) -> Self::Edges {
- self.edges(a)
- }
-}
-
-impl<'a, N: 'a, E: 'a, Ty> visit::IntoEdgesDirected for &'a GraphMap<N, E, Ty>
-where
- N: NodeTrait,
- Ty: EdgeType,
-{
- type EdgesDirected = EdgesDirected<'a, N, E, Ty>;
-
- fn edges_directed(self, a: Self::NodeId, dir: Direction) -> Self::EdgesDirected {
- self.edges_directed(a, dir)
- }
-}
-
-impl<'a, N: 'a, E: 'a, Ty> visit::IntoEdgeReferences for &'a GraphMap<N, E, Ty>
-where
- N: NodeTrait,
- Ty: EdgeType,
-{
- type EdgeRef = (N, N, &'a E);
- type EdgeReferences = AllEdges<'a, N, E, Ty>;
-
- fn edge_references(self) -> Self::EdgeReferences {
- self.all_edges()
- }
-}
-
-impl<N, E, Ty> visit::EdgeCount for GraphMap<N, E, Ty>
-where
- N: NodeTrait,
- Ty: EdgeType,
-{
- #[inline]
- fn edge_count(&self) -> usize {
- self.edge_count()
- }
-}
-
-/// The `GraphMap` keeps an adjacency matrix internally.
-impl<N, E, Ty> visit::GetAdjacencyMatrix for GraphMap<N, E, Ty>
-where
- N: Copy + Ord + Hash,
- Ty: EdgeType,
-{
- type AdjMatrix = ();
-
- #[inline]
- fn adjacency_matrix(&self) {}
-
- #[inline]
- fn is_adjacent(&self, _: &(), a: N, b: N) -> bool {
- self.contains_edge(a, b)
- }
-}
diff --git a/crates/graphmap/src/proptest.rs b/crates/graphmap/src/proptest.rs
deleted file mode 100644
index 12f3e9b..0000000
--- a/crates/graphmap/src/proptest.rs
+++ /dev/null
@@ -1,57 +0,0 @@
-use alloc::sync::Arc;
-use core::fmt::Debug;
-
-use petgraph_core::deprecated::edge::EdgeType;
-use petgraph_proptest::{default::graph_strategy_from_vtable, vtable::VTable};
-use proptest::{
- arbitrary::Arbitrary, collection::SizeRange, prelude::BoxedStrategy, strategy::Strategy,
-};
-
-use crate::{GraphMap, NodeTrait};
-
-fn add_edge_no_return<N, E, Ty>(graph: &mut GraphMap<N, E, Ty>, a: N, b: N, weight: E)
-where
- N: NodeTrait,
- Ty: EdgeType,
-{
- graph.add_edge(a, b, weight);
-}
-
-// GraphMap does not implement `Create` or `Build`, so we need to use the vtable.
-fn create_vtable<N, E, Ty>() -> VTable<GraphMap<N, E, Ty>, N, N, E>
-where
- N: NodeTrait,
- Ty: EdgeType,
-{
- VTable {
- with_capacity: GraphMap::with_capacity,
- add_node: GraphMap::add_node,
- add_edge: add_edge_no_return::<N, E, Ty>,
- }
-}
-
-impl<N, E, Ty> Arbitrary for GraphMap<N, E, Ty>
-where
- N: NodeTrait + Arbitrary + Clone + Debug + 'static,
- E: Arbitrary + Debug + 'static,
- Ty: EdgeType + 'static,
-{
- type Parameters = ();
- // impl Strategy<Value = Self> is nightly, and therefore not usable here.
- // TODO: revisit once impl_trait_in_assoc_type is stable. (https://github.com/rust-lang/rust/issues/63063)
- type Strategy = BoxedStrategy<Self>;
-
- fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
- // creating a graphmap with usize::MAX is a bit excessive, so we use u8::MAX instead.
- graph_strategy_from_vtable(
- create_vtable::<N, E, Ty>(),
- true,
- false,
- 0..=(u8::MAX as usize),
- Some(Arc::new(|max| {
- SizeRange::new(0..=usize::min(max.pow(2), u8::MAX as usize))
- })),
- )
- .boxed()
- }
-}
diff --git a/crates/numi/src/cast.rs b/crates/numi/src/cast.rs
index 66b562f..ad4c8d7 100644
--- a/crates/numi/src/cast.rs
+++ b/crates/numi/src/cast.rs
@@ -168,5 +168,5 @@
assert_impl_all!(Saturating<u16>: CastTo<Wrapping<u32>>);
#[test]
- fn compile() {}
+ const fn compile() {}
}
diff --git a/crates/utils/src/lib.rs b/crates/utils/src/lib.rs
index bd87a13..77a39ea 100644
--- a/crates/utils/src/lib.rs
+++ b/crates/utils/src/lib.rs
@@ -27,8 +27,8 @@
(@collection: node $name:ident[$($id:ident : $attr:expr),* $(,)?]) => {
#[allow(unreachable_pub)]
- pub struct $name<T> {
- $(pub $id: T,)*
+ pub struct $name {
+ $(pub $id: petgraph_core::node::NodeId,)*
}
};
@@ -45,8 +45,8 @@
$name:ident[$($id:ident : $source:ident $(->)? $(--)? $target:ident : $attr:expr),* $(,)?]
) => {
#[allow(unreachable_pub)]
- pub struct $name<T> {
- $(pub $id: T,)*
+ pub struct $name {
+ $(pub $id: petgraph_core::edge::EdgeId,)*
}
};
@@ -55,8 +55,8 @@
$name:ident[$($id:ident : $source:ident $(->)? $(--)? $target:ident : @{$attr:expr}),* $(,)?]
) => {
#[allow(unreachable_pub)]
- pub struct $name<T> {
- $(pub $id: T,)*
+ pub struct $name {
+ $(pub $id: petgraph_core::edge::EdgeId,)*
}
};
@@ -104,7 +104,7 @@
};
};
- ($(#[$meta:meta])* $vis:vis factory($name:ident) => $graph:ty; [$($nodes:tt)*] as $nty:ty, [$($edges:tt)*] as $ety:ty) => {
+ ($(#[$meta:meta])* $vis:vis factory($name:ident) => $graph:ty; [$($nodes:tt)*], [$($edges:tt)*]) => {
$(#[$meta])*
$vis mod $name {
use super::*;
@@ -114,7 +114,7 @@
pub type Graph = $graph;
- pub fn create() -> $crate::GraphCollection<$graph, NodeCollection<$nty>, EdgeCollection<$ety>> {
+ pub fn create() -> $crate::GraphCollection<$graph, NodeCollection, EdgeCollection> {
let mut graph = <$graph>::new();
$crate::graph!(@insert: node graph; nodes; NodeCollection[$($nodes)*]);
diff --git a/src/lib.rs b/src/lib.rs
index eae9b5f..3ff101d 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -155,7 +155,7 @@
pub use petgraph_csr::*;
}
-#[cfg(feature = "graphmap")]
+#[cfg(feature = "entry")]
pub mod graphmap {
pub use petgraph_graphmap::*;
}
diff --git a/src/prelude.rs b/src/prelude.rs
index 3d9ccbe..2d60704 100644
--- a/src/prelude.rs
+++ b/src/prelude.rs
@@ -20,6 +20,6 @@
#[doc(no_inline)]
pub use petgraph_graph::{DiGraph, EdgeIndex, Graph, NodeIndex, UnGraph};
-#[cfg(feature = "graphmap")]
+#[cfg(feature = "entry")]
#[doc(no_inline)]
pub use crate::graphmap::{DiGraphMap, GraphMap, UnGraphMap};