diff --git a/benches/Cargo.toml b/benches/Cargo.toml index f060d83ebfda8..c302a50a01afd 100644 --- a/benches/Cargo.toml +++ b/benches/Cargo.toml @@ -13,6 +13,7 @@ rand_chacha = "0.3" criterion = { version = "0.3", features = ["html_reports"] } bevy_app = { path = "../crates/bevy_app" } bevy_ecs = { path = "../crates/bevy_ecs", features = ["multi-threaded"] } +bevy_graph = { path = "../crates/bevy_graph" } bevy_reflect = { path = "../crates/bevy_reflect" } bevy_tasks = { path = "../crates/bevy_tasks" } bevy_utils = { path = "../crates/bevy_utils" } diff --git a/crates/bevy_graph/Cargo.toml b/crates/bevy_graph/Cargo.toml new file mode 100644 index 0000000000000..227ebdbb5147a --- /dev/null +++ b/crates/bevy_graph/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "bevy_graph" +version = "0.9.0" +edition = "2021" +description = "Graph data structures, as used by the Bevy game engine" +homepage = "https://bevyengine.org" +repository = "https://github.com/bevyengine/bevy" +license = "MIT OR Apache-2.0" +keywords = ["bevy"] + +[dependencies] +hashbrown = "0.13.1" +slotmap = "1.0.6" +thiserror = "1.0.38" diff --git a/crates/bevy_graph/src/algos/bfs.rs b/crates/bevy_graph/src/algos/bfs.rs new file mode 100644 index 0000000000000..93706418ec3a2 --- /dev/null +++ b/crates/bevy_graph/src/algos/bfs.rs @@ -0,0 +1,151 @@ +use std::{collections::VecDeque, marker::PhantomData}; + +use hashbrown::HashSet; + +use crate::{ + graphs::{edge::EdgeRef, keys::NodeIdx, Graph}, + iters, +}; + +/// Implementation of the [`BFS` algorithm](https://www.geeksforgeeks.org/breadth-first-search-or-bfs-for-a-graph/) +/// +/// when `d` is the distance between a node and the startnode, +/// it will evaluate every node with `d=1`, then continue with `d=2` and so on. +pub struct BreadthFirstSearch<'g, N, E: 'g, G: Graph, I: Iterator>> { + graph: &'g G, + queue: VecDeque, + visited: HashSet, + visitor: fn(&'g G, NodeIdx) -> I, + phantom: PhantomData<(N, E)>, +} + +impl<'g, N, E: 'g, G: Graph, I: Iterator>> + BreadthFirstSearch<'g, N, E, G, I> +{ + /// Creates a new `BreadthFirstSearch` with a start node and a custom visitor + pub fn custom(graph: &'g G, start: NodeIdx, visitor: fn(&'g G, NodeIdx) -> I) -> Self { + let node_count = graph.node_count(); + let mut queue = VecDeque::with_capacity(node_count); + let mut visited = HashSet::with_capacity(node_count); + + visited.insert(start); + queue.push_back(start); + + Self { + graph, + queue, + visited, + visitor, + phantom: PhantomData, + } + } + + /// Creates a new `BreadthFirstSearch` with a start node and a custom visitor wrapped inside an `NodesByIdx` iterator + #[inline] + pub fn custom_ref( + graph: &'g G, + start: NodeIdx, + visitor: fn(&'g G, NodeIdx) -> I, + ) -> iters::NodesByIdx<'g, N, NodeIdx, Self> { + let inner = Self::custom(graph, start, visitor); + iters::NodesByIdx::from_graph(inner, graph) + } + + /// Creates a new `BreadthFirstSearch` with a start node and a custom visitor wrapped inside an `NodesByIdxMut` iterator + #[inline] + pub fn custom_mut( + graph: &'g mut G, + start: NodeIdx, + visitor: fn(&'g G, NodeIdx) -> I, + ) -> iters::NodesByIdxMut<'g, N, NodeIdx, Self> { + unsafe { + // SAFETY: `BreadthFirstSearch` assueres that every node gets visited only once + let ptr: *mut G = &mut *graph; + let inner = Self::custom(&*ptr, start, visitor); + + iters::NodesByIdxMut::from_graph(inner, graph) + } + } +} + +impl<'g, N, E: 'g, G: Graph> BreadthFirstSearch<'g, N, E, G, G::OutgoingEdgesOf<'g>> { + /// Creates a new `BreadthFirstSearch` with a start node and the default visitor of `outgoing` + #[inline] + pub fn new(graph: &'g G, start: NodeIdx) -> Self { + Self::custom(graph, start, |graph, index| graph.outgoing_edges_of(index)) + } + + /// Creates a new `BreadthFirstSearch` with a start node and the default visitor of `outgoing` wrapped inside an `NodesByIdx` iterator + #[inline] + pub fn new_ref(graph: &'g G, start: NodeIdx) -> iters::NodesByIdx<'g, N, NodeIdx, Self> { + let inner = Self::new(graph, start); + iters::NodesByIdx::from_graph(inner, graph) + } + + /// Creates a new `BreadthFirstSearch` with a start node and the default visitor of `outgoing` wrapped inside an `NodesByIdxMut` iterator + #[inline] + pub fn new_mut(graph: &'g mut G, start: NodeIdx) -> iters::NodesByIdxMut<'g, N, NodeIdx, Self> { + unsafe { + // SAFETY: `BreadthFirstSearch` assueres that every node gets visited only once + let ptr: *mut G = &mut *graph; + let inner = Self::new(&*ptr, start); + + iters::NodesByIdxMut::from_graph(inner, graph) + } + } +} + +impl<'g, N, E: 'g, G: Graph, I: Iterator>> Iterator + for BreadthFirstSearch<'g, N, E, G, I> +{ + type Item = NodeIdx; + + fn next(&mut self) -> Option { + if let Some(node) = self.queue.pop_front() { + for EdgeRef(_, dst, _) in (self.visitor)(self.graph, node) { + if !self.visited.contains(&dst) { + self.visited.insert(dst); + self.queue.push_back(dst); + } + } + Some(node) + } else { + None + } + } +} + +#[cfg(test)] +mod test { + use crate::{ + algos::bfs::BreadthFirstSearch, + graphs::{map::SimpleMapGraph, Graph}, + }; + + #[test] + fn basic_imperative_bfs() { + let mut graph = SimpleMapGraph::::new(); + + let zero = graph.add_node(0); + let one = graph.add_node(1); + let two = graph.add_node(2); + let three = graph.add_node(3); + + graph.add_edge(zero, one, ()); + graph.add_edge(zero, two, ()); + graph.add_edge(one, two, ()); + graph.add_edge(two, zero, ()); + graph.add_edge(two, three, ()); + + let elements = vec![0, 2, 1, 3]; + + let mut counted_elements = Vec::with_capacity(4); + + let bfs = BreadthFirstSearch::new_ref(&graph, zero); + for node in bfs { + counted_elements.push(*node); + } + + assert_eq!(elements, counted_elements); + } +} diff --git a/crates/bevy_graph/src/algos/dfs.rs b/crates/bevy_graph/src/algos/dfs.rs new file mode 100644 index 0000000000000..4327717142477 --- /dev/null +++ b/crates/bevy_graph/src/algos/dfs.rs @@ -0,0 +1,148 @@ +use std::marker::PhantomData; + +use hashbrown::HashSet; + +use crate::{ + graphs::{edge::EdgeRef, keys::NodeIdx, Graph}, + iters, +}; + +/// Implementation of the [`DFS` algorithm](https://www.geeksforgeeks.org/depth-first-search-or-dfs-for-a-graph/) +/// +/// it will evaluate every node from the start as deep as it can and then continue at the next sibling node from the top. +pub struct DepthFirstSearch<'g, N, E: 'g, G: Graph, I: Iterator>> { + graph: &'g G, + stack: Vec, + visited: HashSet, + visitor: fn(&'g G, NodeIdx) -> I, + phantom: PhantomData<(N, E)>, +} + +impl<'g, N, E: 'g, G: Graph, I: Iterator>> + DepthFirstSearch<'g, N, E, G, I> +{ + /// Creates a new `DepthFirstSearch` with a start node and a custom visitor + pub fn custom(graph: &'g G, start: NodeIdx, visitor: fn(&'g G, NodeIdx) -> I) -> Self { + let node_count = graph.node_count(); + let mut stack = Vec::with_capacity(node_count); + let mut visited = HashSet::with_capacity(node_count); + + visited.insert(start); + stack.push(start); + + Self { + graph, + stack, + visited, + visitor, + phantom: PhantomData, + } + } + + /// Creates a new `DepthFirstSearch` with a start node and a custom visitor wrapped inside an `NodesByIdx` iterator + #[inline] + pub fn custom_ref( + graph: &'g G, + start: NodeIdx, + visitor: fn(&'g G, NodeIdx) -> I, + ) -> iters::NodesByIdx<'g, N, NodeIdx, Self> { + let inner = Self::custom(graph, start, visitor); + iters::NodesByIdx::from_graph(inner, graph) + } + + /// Creates a new `DepthFirstSearch` with a start node and a custom visitor wrapped inside an `NodesByIdxMut` iterator + #[inline] + pub fn custom_mut( + graph: &'g mut G, + start: NodeIdx, + visitor: fn(&'g G, NodeIdx) -> I, + ) -> iters::NodesByIdxMut<'g, N, NodeIdx, Self> { + unsafe { + // SAFETY: `DepthFirstSearch` assueres that every node gets visited only once + let ptr: *mut G = &mut *graph; + let inner = Self::custom(&*ptr, start, visitor); + + iters::NodesByIdxMut::from_graph(inner, graph) + } + } +} + +impl<'g, N, E: 'g, G: Graph> DepthFirstSearch<'g, N, E, G, G::OutgoingEdgesOf<'g>> { + /// Creates a new `DepthFirstSearch` with a start node and the default visitor of `outgoing` + #[inline] + pub fn new(graph: &'g G, start: NodeIdx) -> Self { + Self::custom(graph, start, |graph, index| graph.outgoing_edges_of(index)) + } + + /// Creates a new `DepthFirstSearch` with a start node and the default visitor of `outgoing` wrapped inside an `NodesByIdx` iterator + pub fn new_ref(graph: &'g G, start: NodeIdx) -> iters::NodesByIdx<'g, N, NodeIdx, Self> { + let inner = Self::new(graph, start); + iters::NodesByIdx::from_graph(inner, graph) + } + + /// Creates a new `DepthFirstSearch` with a start node and the default visitor of `outgoing` wrapped inside an `NodesByIdxMut` iterator + pub fn new_mut(graph: &'g mut G, start: NodeIdx) -> iters::NodesByIdxMut<'g, N, NodeIdx, Self> { + unsafe { + // SAFETY: `DepthFirstSearch` assueres that every node gets visited only once + let ptr: *mut G = &mut *graph; + let inner = Self::new(&*ptr, start); + + iters::NodesByIdxMut::from_graph(inner, graph) + } + } +} + +impl<'g, N, E: 'g, G: Graph, I: Iterator>> Iterator + for DepthFirstSearch<'g, N, E, G, I> +{ + type Item = NodeIdx; + + fn next(&mut self) -> Option { + if let Some(node) = self.stack.pop() { + for EdgeRef(_, dst, _) in (self.visitor)(self.graph, node) { + if !self.visited.contains(&dst) { + self.visited.insert(dst); + self.stack.push(dst); + } + } + Some(node) + } else { + None + } + } +} + +#[cfg(test)] +mod test { + use crate::{ + algos::dfs::DepthFirstSearch, + graphs::{map::SimpleMapGraph, Graph}, + }; + + #[test] + fn basic_imperative_dfs() { + let mut graph = SimpleMapGraph::::new(); + + let zero = graph.add_node(0); + let one = graph.add_node(1); + let two = graph.add_node(2); + let three = graph.add_node(3); + + graph.add_edge(zero, one, ()); + graph.add_edge(zero, two, ()); + graph.add_edge(one, two, ()); + graph.add_edge(two, zero, ()); + graph.add_edge(two, three, ()); + + let elements = vec![0, 1, 2, 3]; + + let mut counted_elements = Vec::with_capacity(4); + + let dfs = DepthFirstSearch::new_ref(&graph, zero); + for node in dfs { + counted_elements.push(*node); + } + + assert_eq!(elements, counted_elements); + } +} diff --git a/crates/bevy_graph/src/algos/mod.rs b/crates/bevy_graph/src/algos/mod.rs new file mode 100644 index 0000000000000..cde0fa3130c06 --- /dev/null +++ b/crates/bevy_graph/src/algos/mod.rs @@ -0,0 +1,4 @@ +/// Implementation of the [`BFS` algorythm](https://www.geeksforgeeks.org/breadth-first-search-or-bfs-for-a-graph/) +pub mod bfs; +/// Implementation of the [`DFS` algorythm](https://www.geeksforgeeks.org/depth-first-search-or-dfs-for-a-graph/) +pub mod dfs; diff --git a/crates/bevy_graph/src/error.rs b/crates/bevy_graph/src/error.rs new file mode 100644 index 0000000000000..729b3c583accc --- /dev/null +++ b/crates/bevy_graph/src/error.rs @@ -0,0 +1,17 @@ +use thiserror::Error; + +use crate::graphs::keys::NodeIdx; + +/// An error that can occur when traversing or manipulating a graph data structure +#[derive(Debug, Error)] +pub enum GraphError { + /// the given `NodeIdx` is not preset in the graph + #[error("the given `{0:?}` isn't preset in the graph")] + NodeNotFound(NodeIdx), + /// there is already an edge between those nodes (not allowed in `SimpleGraph`) + #[error("there is already an edge between those nodes (not allowed in `SimpleGraph`)")] + Loop, + /// the `src` and `dst` nodes are equal, the edge would be a loop (not allowed in `SimpleGraph`) + #[error("the `src` and `dst` nodes are equal, the edge would be a loop (not allowed in `SimpleGraph`)")] + ContainsEdgeBetween, +} diff --git a/crates/bevy_graph/src/graphs/adjacency_storage.rs b/crates/bevy_graph/src/graphs/adjacency_storage.rs new file mode 100644 index 0000000000000..5c19fa865434b --- /dev/null +++ b/crates/bevy_graph/src/graphs/adjacency_storage.rs @@ -0,0 +1,60 @@ +/// Adjacency storage enum helper: `Directed` or `Undirected` +#[derive(Clone)] +pub enum AdjacencyStorage { + /// Undirected graphs share one storage (`S`) for incoming and outgoing edges + Undirected(S), + /// Directed graphs have two storages (`S`) for incoming and outgoing edges + Directed(S, S), +} + +impl AdjacencyStorage { + /// Returns an immutable reference to the incoming adjacency storage + #[inline] + pub const fn incoming(&self) -> &S { + match self { + AdjacencyStorage::Undirected(storage) => storage, + AdjacencyStorage::Directed(incoming, _) => incoming, + } + } + + /// Returns a mutable reference to the incoming adjacency storage + #[inline] + pub fn incoming_mut(&mut self) -> &mut S { + match self { + AdjacencyStorage::Undirected(storage) => storage, + AdjacencyStorage::Directed(incoming, _) => incoming, + } + } + + /// Returns an immutable reference to the outgoing adjacency storage + #[inline] + pub fn outgoing(&self) -> &S { + match self { + AdjacencyStorage::Undirected(storage) => storage, + AdjacencyStorage::Directed(_, outgoing) => outgoing, + } + } + + /// Returns a mutable reference to the outgoing adjacency storage + #[inline] + pub fn outgoing_mut(&mut self) -> &mut S { + match self { + AdjacencyStorage::Undirected(storage) => storage, + AdjacencyStorage::Directed(_, outgoing) => outgoing, + } + } + + /// Executes a function for each storage + #[inline] + pub fn for_each_mut(&mut self, f: fn(&mut S)) { + match self { + AdjacencyStorage::Undirected(storage) => { + f(storage); + } + AdjacencyStorage::Directed(incoming, outgoing) => { + f(incoming); + f(outgoing); + } + } + } +} diff --git a/crates/bevy_graph/src/graphs/edge.rs b/crates/bevy_graph/src/graphs/edge.rs new file mode 100644 index 0000000000000..968e526af609a --- /dev/null +++ b/crates/bevy_graph/src/graphs/edge.rs @@ -0,0 +1,49 @@ +use std::ops::{Deref, DerefMut}; + +use super::keys::NodeIdx; + +/// An edge between nodes that store data of type `E`. +#[derive(Clone)] +pub struct Edge(pub NodeIdx, pub NodeIdx, pub E); + +impl Edge { + /// Returns a [`EdgeRef`] of this edge + #[inline] + pub const fn as_ref_edge(&self) -> EdgeRef { + EdgeRef(self.0, self.1, &self.2) + } + + /// Returns a [`EdgeMut`] of this edge + #[inline] + pub fn as_mut_edge(&mut self) -> EdgeMut { + EdgeMut(self.0, self.1, &mut self.2) + } +} + +/// An util container which holds `Edge` data with an immutable reference to the edge value +pub struct EdgeRef<'v, E>(pub NodeIdx, pub NodeIdx, pub &'v E); + +impl<'v, E> Deref for EdgeRef<'v, E> { + type Target = E; + + fn deref(&self) -> &Self::Target { + self.2 + } +} + +/// An util container which holds `Edge` data with a mutable reference to the edge value +pub struct EdgeMut<'v, E>(pub NodeIdx, pub NodeIdx, pub &'v mut E); + +impl<'v, E> Deref for EdgeMut<'v, E> { + type Target = E; + + fn deref(&self) -> &Self::Target { + self.2 + } +} + +impl<'v, E> DerefMut for EdgeMut<'v, E> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.2 + } +} diff --git a/crates/bevy_graph/src/graphs/keys.rs b/crates/bevy_graph/src/graphs/keys.rs new file mode 100644 index 0000000000000..046a10a24e98b --- /dev/null +++ b/crates/bevy_graph/src/graphs/keys.rs @@ -0,0 +1,8 @@ +use slotmap::new_key_type; + +new_key_type! { + /// A key that holds an index to a node in a graph. + pub struct NodeIdx; + /// A key that holds an index to an edge in a graph. + pub struct EdgeIdx; +} diff --git a/crates/bevy_graph/src/graphs/list/mod.rs b/crates/bevy_graph/src/graphs/list/mod.rs new file mode 100644 index 0000000000000..3d7e0b9b6cf89 --- /dev/null +++ b/crates/bevy_graph/src/graphs/list/mod.rs @@ -0,0 +1,5 @@ +mod simple; +pub use simple::*; + +mod multi; +pub use multi::*; diff --git a/crates/bevy_graph/src/graphs/list/multi.rs b/crates/bevy_graph/src/graphs/list/multi.rs new file mode 100644 index 0000000000000..98e7cb3091ff8 --- /dev/null +++ b/crates/bevy_graph/src/graphs/list/multi.rs @@ -0,0 +1,591 @@ +use slotmap::{HopSlotMap, SecondaryMap}; + +use crate::{ + algos::dfs::DepthFirstSearch, + error::GraphError, + graphs::{ + adjacency_storage::AdjacencyStorage, + edge::{Edge, EdgeMut, EdgeRef}, + keys::{EdgeIdx, NodeIdx}, + DirectedGraph, Graph, + }, + iters, + utils::{ + iter_choice::IterChoice, vecmap::VecMap, vecset::VecSet, + wrapped_indices_iterator::WrappedIndicesIterator, + }, +}; + +type MultiListStorage = Vec<(NodeIdx, Vec)>; + +/// Implementation of a `MultiGraph` which uses `Vec<(NodeIdx, Vec)>` for adjacencies +/// +/// `MultiGraph`s can hold multiple edges between two nodes and edges between the same node +#[derive(Clone)] +pub struct MultiListGraph { + nodes: HopSlotMap, + edges: HopSlotMap>, + adjacencies: SecondaryMap>, +} + +impl Graph for MultiListGraph { + fn new() -> Self { + Self { + nodes: HopSlotMap::with_key(), + edges: HopSlotMap::with_key(), + adjacencies: SecondaryMap::new(), + } + } + + fn with_capacity(node_capacity: usize, edge_capacity: usize) -> Self { + Self { + nodes: HopSlotMap::with_capacity_and_key(node_capacity), + edges: HopSlotMap::with_capacity_and_key(edge_capacity), + adjacencies: SecondaryMap::with_capacity(node_capacity), + } + } + + #[inline] + fn capacity(&self) -> (usize, usize) { + (self.nodes.capacity(), self.edges.capacity()) + } + + #[inline] + fn node_capacity(&self) -> usize { + self.nodes.capacity() + } + + #[inline] + fn edge_capacity(&self) -> usize { + self.edges.capacity() + } + + #[inline] + fn reserve_nodes(&mut self, additional: usize) { + self.nodes.reserve(additional); + } + + #[inline] + fn reserve_edges(&mut self, additional: usize) { + self.edges.reserve(additional); + } + + #[inline] + fn is_directed(&self) -> bool { + DIRECTED + } + + #[inline] + fn is_multigraph(&self) -> bool { + true + } + #[inline] + fn node_count(&self) -> usize { + self.nodes.len() + } + + #[inline] + fn edge_count(&self) -> usize { + self.edges.len() + } + + fn add_node(&mut self, node: N) -> NodeIdx { + let idx = self.nodes.insert(node); + let storage = if DIRECTED { + AdjacencyStorage::Directed(Vec::new(), Vec::new()) + } else { + AdjacencyStorage::Undirected(Vec::new()) + }; + self.adjacencies.insert(idx, storage); + idx + } + + fn try_add_edge( + &mut self, + src: NodeIdx, + dst: NodeIdx, + value: E, + ) -> Result { + if !self.contains_node(src) { + Err(GraphError::NodeNotFound(src)) + } else if !self.contains_node(dst) { + Err(GraphError::NodeNotFound(dst)) + } else { + unsafe { + let idx = self.edges.insert(Edge(src, dst, value)); + self.adjacencies + .get_unchecked_mut(src) + .outgoing_mut() + .get_value_or_default_mut(dst) + .push(idx); + self.adjacencies + .get_unchecked_mut(dst) + .incoming_mut() + .get_value_or_default_mut(src) + .push(idx); + Ok(idx) + } + } + } + + #[inline] + fn contains_node(&self, node: NodeIdx) -> bool { + self.nodes.contains_key(node) + } + + fn contains_edge_between(&self, src: NodeIdx, dst: NodeIdx) -> bool { + if let Some(a1) = self.adjacencies.get(src) { + if let Some(a2) = a1.outgoing().get_value(dst) { + !a2.is_empty() + } else { + false + } + } else { + false + } + } + + fn remove_node(&mut self, index: NodeIdx) -> Option { + if self.contains_node(index) { + let edges_to_remove = self + .edges_of(index) + .into_indices() + .collect::>(); + for edge_idx in edges_to_remove { + unsafe { + let Edge(src, dst, _) = self.edges.remove(edge_idx).unwrap_unchecked(); + self.adjacencies + .get_unchecked_mut(src) + .outgoing_mut() + .get_value_mut(dst) + .unwrap() + .remove_by_value(&edge_idx); + self.adjacencies + .get_unchecked_mut(dst) + .incoming_mut() + .get_value_mut(src) + .unwrap() + .remove_by_value(&edge_idx); + } + } + unsafe { + self.adjacencies.remove(index).unwrap_unchecked(); + } + self.nodes.remove(index) + } else { + None + } + } + + fn remove_edge(&mut self, index: EdgeIdx) -> Option { + if let Some(Edge(src, dst, value)) = self.edges.remove(index) { + unsafe { + self.adjacencies + .get_unchecked_mut(src) + .outgoing_mut() + .get_value_mut(dst) + .unwrap() + .remove_by_value(&index); + self.adjacencies + .get_unchecked_mut(dst) + .incoming_mut() + .get_value_mut(src) + .unwrap() + .remove_by_value(&index); + } + Some(value) + } else { + None + } + } + + fn clear_edges(&mut self) { + self.adjacencies + .values_mut() + .for_each(|list| list.for_each_mut(Vec::clear)); + self.edges.clear(); + } + + fn clear(&mut self) { + self.adjacencies.clear(); + self.edges.clear(); + self.nodes.clear(); + } + + #[inline] + fn get_node(&self, index: NodeIdx) -> Option<&N> { + self.nodes.get(index) + } + + #[inline] + fn get_node_mut(&mut self, index: NodeIdx) -> Option<&mut N> { + self.nodes.get_mut(index) + } + + fn find_node(&self, value: &N) -> Option + where + N: PartialEq, + { + self.nodes + .iter() + .find_map(|(key, val)| if val == value { Some(key) } else { None }) + } + + #[inline] + unsafe fn get_edge_raw(&mut self, index: EdgeIdx) -> Option<&mut Edge> { + self.edges.get_mut(index) + } + + #[inline] + fn get_edge(&self, index: EdgeIdx) -> Option> { + self.edges.get(index).map(|edge| edge.as_ref_edge()) + } + + #[inline] + fn get_edge_mut(&mut self, index: EdgeIdx) -> Option> { + self.edges.get_mut(index).map(|edge| edge.as_mut_edge()) + } + + fn find_edge(&self, value: &E) -> Option + where + E: PartialEq, + { + self.edges + .iter() + .find_map(|(key, val)| if &val.2 == value { Some(key) } else { None }) + } + + fn degree(&self, index: NodeIdx) -> usize { + if DIRECTED { + self.in_degree(index) + self.out_degree(index) + } else { + self.adjacencies[index] + .incoming() + .iter() + .map(|(_, vec)| vec.len()) + .sum() + } + } + + type NodeIndices<'n> = slotmap::hop::Keys<'n, NodeIdx, N> where Self: 'n; + fn node_indices(&self) -> Self::NodeIndices<'_> { + self.nodes.keys() + } + + unsafe fn nodes_raw(&self) -> &slotmap::HopSlotMap { + &self.nodes + } + + type Nodes<'n> = slotmap::hop::Values<'n, NodeIdx, N> where Self: 'n; + fn nodes(&self) -> Self::Nodes<'_> { + self.nodes.values() + } + + unsafe fn nodes_mut_raw(&mut self) -> &mut HopSlotMap { + &mut self.nodes + } + + type NodesMut<'n> = slotmap::hop::ValuesMut<'n, NodeIdx, N> where Self: 'n; + fn nodes_mut(&mut self) -> Self::NodesMut<'_> { + self.nodes.values_mut() + } + + type EdgeIndices<'e> = slotmap::hop::Keys<'e, EdgeIdx, Edge> where Self: 'e; + fn edge_indices(&self) -> Self::EdgeIndices<'_> { + self.edges.keys() + } + + unsafe fn edges_raw(&self) -> &HopSlotMap> { + &self.edges + } + + type Edges<'e> = iters::EdgesRef<'e, E, slotmap::hop::Values<'e, EdgeIdx, Edge>> where Self: 'e; + fn edges(&self) -> Self::Edges<'_> { + iters::EdgesRef::new(self.edges.values()) + } + + unsafe fn edges_mut_raw(&mut self) -> &mut HopSlotMap> { + &mut self.edges + } + + type EdgesMut<'e> = iters::EdgesMut<'e, E, slotmap::hop::ValuesMut<'e, EdgeIdx, Edge>> where Self: 'e; + fn edges_mut(&mut self) -> Self::EdgesMut<'_> { + iters::EdgesMut::new(self.edges.values_mut()) + } + + type EdgesOf<'e> = iters::EdgesByIdx<'e, E, &'e EdgeIdx, IterChoice<&'e EdgeIdx, std::iter::Flatten, std::iter::Chain>, iters::SkipIndexIter<'e, &'e Vec, crate::utils::vecmap::TupleIter<'e, NodeIdx, Vec>>>, false>>, iters::LoopSafetyIter<'e, E, &'e EdgeIdx, std::iter::Flatten, crate::utils::vecmap::TupleIter<'e, NodeIdx, Vec>, false>>>>> where Self: 'e; + fn edges_of(&self, index: NodeIdx) -> Self::EdgesOf<'_> { + let inner = if DIRECTED { + IterChoice::new_first( + iters::TupleExtract::new_second( + self.adjacencies[index].incoming().tuple_iter().chain( + iters::SkipIndexIter::new( + self.adjacencies[index].outgoing().tuple_iter(), + index, + ), // the outgoing will skip self to ensure loops will only come once (from incoming) + ), + ) + .flatten(), + ) + } else { + IterChoice::new_second(iters::LoopSafetyIter::new( + iters::TupleExtract::new_second(self.adjacencies[index].incoming().tuple_iter()) + .flatten(), + &self.edges, + )) + }; + iters::EdgesByIdx::new(inner, &self.edges) + } + + type EdgesOfMut<'e> = iters::EdgesByIdxMut<'e, E, &'e EdgeIdx, IterChoice<&'e EdgeIdx, std::iter::Flatten, std::iter::Chain>, iters::SkipIndexIter<'e, &'e Vec, crate::utils::vecmap::TupleIter<'e, NodeIdx, Vec>>>, false>>, iters::LoopSafetyIter<'e, E, &'e EdgeIdx, std::iter::Flatten, crate::utils::vecmap::TupleIter<'e, NodeIdx, Vec>, false>>>>> where Self: 'e; + fn edges_of_mut(&mut self, index: NodeIdx) -> Self::EdgesOfMut<'_> { + let inner = if DIRECTED { + IterChoice::new_first( + iters::TupleExtract::new_second( + self.adjacencies[index].incoming().tuple_iter().chain( + iters::SkipIndexIter::new( + self.adjacencies[index].outgoing().tuple_iter(), + index, + ), // the outgoing will skip self to ensure loops will only come once (from incoming) + ), + ) + .flatten(), + ) + } else { + unsafe { + // SAFETY: it should be ok - no data will be removed + let ptr: *mut HopSlotMap> = &mut self.edges; + IterChoice::new_second(iters::LoopSafetyIter::new( + iters::TupleExtract::new_second( + self.adjacencies[index].incoming().tuple_iter(), + ) + .flatten(), + &*ptr, + )) + } + }; + iters::EdgesByIdxMut::new(inner, &mut self.edges) + } + + type Neighbors<'n> = iters::NodesByIdx<'n, N, &'n NodeIdx, iters::NodeJustOnceIter<&'n NodeIdx, iters::TupleExtract<&'n NodeIdx, &'n Vec, IterChoice<(&'n NodeIdx, &'n Vec), std::iter::Chain>, crate::utils::vecmap::TupleIter<'n, NodeIdx, Vec>>, crate::utils::vecmap::TupleIter<'n, NodeIdx, Vec>>, true>>> where Self: 'n; + fn neighbors(&self, index: NodeIdx) -> Self::Neighbors<'_> { + let inner = if DIRECTED { + IterChoice::new_first( + self.adjacencies[index] + .incoming() + .tuple_iter() + .chain(self.adjacencies[index].outgoing().tuple_iter()), + ) + } else { + IterChoice::new_second(self.adjacencies[index].incoming().tuple_iter()) + }; + iters::NodesByIdx::new( + iters::NodeJustOnceIter::new(iters::TupleExtract::new_first(inner)), + &self.nodes, + ) + } + + type NeighborsMut<'n> = iters::NodesByIdxMut<'n, N, &'n NodeIdx, iters::NodeJustOnceIter<&'n NodeIdx, iters::TupleExtract<&'n NodeIdx, &'n Vec, IterChoice<(&'n NodeIdx, &'n Vec), std::iter::Chain>, crate::utils::vecmap::TupleIter<'n, NodeIdx, Vec>>, crate::utils::vecmap::TupleIter<'n, NodeIdx, Vec>>, true>>> where Self: 'n; + fn neighbors_mut(&mut self, index: NodeIdx) -> Self::NeighborsMut<'_> { + let inner = if DIRECTED { + IterChoice::new_first( + self.adjacencies[index] + .incoming() + .tuple_iter() + .chain(self.adjacencies[index].outgoing().tuple_iter()), + ) + } else { + IterChoice::new_second(self.adjacencies[index].incoming().tuple_iter()) + }; + iters::NodesByIdxMut::new( + iters::NodeJustOnceIter::new(iters::TupleExtract::new_first(inner)), + &mut self.nodes, + ) + } + + type Isolated<'n> = iters::Isolated<&'n N, iters::ZipDegree<'n, MultiListStorage, &'n N, slotmap::hop::Iter<'n, NodeIdx, N>, DIRECTED>> where Self: 'n; + fn isolated(&self) -> Self::Isolated<'_> { + iters::Isolated::new(iters::ZipDegree::new(self.nodes.iter(), &self.adjacencies)) + } + + type IsolatedMut<'n> = iters::Isolated<&'n mut N, iters::ZipDegree<'n, MultiListStorage, &'n mut N, slotmap::hop::IterMut<'n, NodeIdx, N>, DIRECTED>> where Self: 'n; + fn isolated_mut(&mut self) -> Self::IsolatedMut<'_> { + iters::Isolated::new(iters::ZipDegree::new( + self.nodes.iter_mut(), + &self.adjacencies, + )) + } + + #[inline] + fn in_degree(&self, index: NodeIdx) -> usize { + self.adjacencies[index] + .incoming() + .iter() + .map(|(_, vec)| vec.len()) + .sum() + } + + #[inline] + fn out_degree(&self, index: NodeIdx) -> usize { + self.adjacencies[index] + .outgoing() + .iter() + .map(|(_, vec)| vec.len()) + .sum() + } + + type IncomingEdgesOf<'e> = iters::EdgesByIdx<'e, E, &'e EdgeIdx, std::iter::Flatten>>> where Self: 'e; + fn incoming_edges_of(&self, index: NodeIdx) -> Self::IncomingEdgesOf<'_> { + iters::EdgesByIdx::new( + self.adjacencies[index].incoming().values().flatten(), + &self.edges, + ) + } + + type IncomingEdgesOfMut<'e> = iters::EdgesByIdxMut<'e, E, &'e EdgeIdx, std::iter::Flatten>>> where Self: 'e; + fn incoming_edges_of_mut(&mut self, index: NodeIdx) -> Self::IncomingEdgesOfMut<'_> { + iters::EdgesByIdxMut::new( + self.adjacencies[index].incoming().values().flatten(), + &mut self.edges, + ) + } + + type OutgoingEdgesOf<'e> = iters::EdgesByIdx<'e, E, &'e EdgeIdx, std::iter::Flatten>>> where Self: 'e; + fn outgoing_edges_of(&self, index: NodeIdx) -> Self::IncomingEdgesOf<'_> { + iters::EdgesByIdx::new( + self.adjacencies[index].outgoing().values().flatten(), + &self.edges, + ) + } + + type OutgoingEdgesOfMut<'e> = iters::EdgesByIdxMut<'e, E, &'e EdgeIdx, std::iter::Flatten>>> where Self: 'e; + fn outgoing_edges_of_mut(&mut self, index: NodeIdx) -> Self::IncomingEdgesOfMut<'_> { + iters::EdgesByIdxMut::new( + self.adjacencies[index].outgoing().values().flatten(), + &mut self.edges, + ) + } + + type InNeighbors<'n> = iters::NodesByIdx<'n, N, &'n NodeIdx, crate::utils::vecmap::Keys<'n, NodeIdx, Vec>> where Self: 'n; + fn in_neighbors(&self, index: NodeIdx) -> Self::InNeighbors<'_> { + iters::NodesByIdx::new(self.adjacencies[index].incoming().keys(), &self.nodes) + } + + type InNeighborsMut<'n> = iters::NodesByIdxMut<'n, N, &'n NodeIdx, crate::utils::vecmap::Keys<'n, NodeIdx, Vec>> where Self: 'n; + fn in_neighbors_mut(&mut self, index: NodeIdx) -> Self::InNeighborsMut<'_> { + iters::NodesByIdxMut::new(self.adjacencies[index].incoming().keys(), &mut self.nodes) + } + + type OutNeighbors<'n> = iters::NodesByIdx<'n, N, &'n NodeIdx, crate::utils::vecmap::Keys<'n, NodeIdx, Vec>> where Self: 'n; + fn out_neighbors(&self, index: NodeIdx) -> Self::OutNeighbors<'_> { + iters::NodesByIdx::new(self.adjacencies[index].outgoing().keys(), &self.nodes) + } + + type OutNeighborsMut<'n> = iters::NodesByIdxMut<'n, N, &'n NodeIdx, crate::utils::vecmap::Keys<'n, NodeIdx, Vec>> where Self: 'n; + fn out_neighbors_mut(&mut self, index: NodeIdx) -> Self::OutNeighborsMut<'_> { + iters::NodesByIdxMut::new( + self.adjacencies[index].outgoing_mut().keys(), + &mut self.nodes, + ) + } + + type Sources<'n> = iters::Isolated<&'n N, iters::ZipInDegree<'n, MultiListStorage, &'n N, slotmap::hop::Iter<'n, NodeIdx, N>>> where Self: 'n; + fn sources(&self) -> Self::Sources<'_> { + iters::Isolated::new(iters::ZipInDegree::new( + self.nodes.iter(), + &self.adjacencies, + )) + } + + type SourcesMut<'n> = iters::Isolated<&'n mut N, iters::ZipInDegree<'n, MultiListStorage, &'n mut N, slotmap::hop::IterMut<'n, NodeIdx, N>>> where Self: 'n; + fn sources_mut(&mut self) -> Self::SourcesMut<'_> { + iters::Isolated::new(iters::ZipInDegree::new( + self.nodes.iter_mut(), + &self.adjacencies, + )) + } + + type Sinks<'n> = iters::Isolated<&'n N, iters::ZipOutDegree<'n, MultiListStorage, &'n N, slotmap::hop::Iter<'n, NodeIdx, N>>> where Self: 'n; + fn sinks(&self) -> Self::Sinks<'_> { + iters::Isolated::new(iters::ZipOutDegree::new( + self.nodes.iter(), + &self.adjacencies, + )) + } + + type SinksMut<'n> = iters::Isolated<&'n mut N, iters::ZipOutDegree<'n, MultiListStorage, &'n mut N, slotmap::hop::IterMut<'n, NodeIdx, N>>> where Self: 'n; + fn sinks_mut(&mut self) -> Self::SinksMut<'_> { + iters::Isolated::new(iters::ZipOutDegree::new( + self.nodes.iter_mut(), + &self.adjacencies, + )) + } +} + +impl DirectedGraph for MultiListGraph { + fn reverse(&mut self) { + self.adjacencies + .values_mut() + .for_each(|list| list.for_each_mut(Vec::clear)); + + for (index, Edge(src, dst, _)) in &mut self.edges { + std::mem::swap(src, dst); // *dst is now *src + self.adjacencies[*src] + .outgoing_mut() + .get_value_or_default_mut(*dst) + .push(index); + self.adjacencies[*dst] + .incoming_mut() + .get_value_or_default_mut(*src) + .push(index); + } + } + + fn reverse_edge(&mut self, index: EdgeIdx) { + if let Some(Edge(src, dst, _)) = self.edges.get_mut(index) { + self.adjacencies[*src] + .outgoing_mut() + .get_value_mut(*dst) + .unwrap() + .remove_by_value(&index); + self.adjacencies[*dst] + .incoming_mut() + .get_value_mut(*src) + .unwrap() + .remove_by_value(&index); + std::mem::swap(src, dst); // *dst is now *src + self.adjacencies[*src] + .outgoing_mut() + .get_value_or_default_mut(*dst) + .push(index); + self.adjacencies[*dst] + .incoming_mut() + .get_value_or_default_mut(*src) + .push(index); + } + } + + type Ancestors<'n> = iters::NodesByIdx<'n, N, NodeIdx, DepthFirstSearch<'n, N, E, Self, Self::IncomingEdgesOf<'n>>> where Self: 'n; + fn ancestors(&self, index: NodeIdx) -> Self::Ancestors<'_> { + // use DFS here, Vec should be faster than VecDeque + DepthFirstSearch::custom_ref(self, index, |graph, node| graph.incoming_edges_of(node)) + } + + type AncestorsMut<'n> = iters::NodesByIdxMut<'n, N, NodeIdx, DepthFirstSearch<'n, N, E, Self, Self::IncomingEdgesOf<'n>>> where Self: 'n; + fn ancestors_mut(&mut self, index: NodeIdx) -> Self::AncestorsMut<'_> { + // use DFS here, Vec should be faster than VecDeque + DepthFirstSearch::custom_mut(self, index, |graph, node| graph.incoming_edges_of(node)) + } + + type Descendants<'n> = iters::NodesByIdx<'n, N, NodeIdx, DepthFirstSearch<'n, N, E, Self, Self::OutgoingEdgesOf<'n>>> where Self: 'n; + fn descendants(&self, index: NodeIdx) -> Self::Descendants<'_> { + // use DFS here, Vec should be faster than VecDeque + DepthFirstSearch::custom_ref(self, index, |graph, node| graph.outgoing_edges_of(node)) + } + + type DescendantsMut<'n> = iters::NodesByIdxMut<'n, N, NodeIdx, DepthFirstSearch<'n, N, E, Self, Self::OutgoingEdgesOf<'n>>> where Self: 'n; + fn descendants_mut(&mut self, index: NodeIdx) -> Self::DescendantsMut<'_> { + // use DFS here, Vec should be faster than VecDeque + DepthFirstSearch::custom_mut(self, index, |graph, node| graph.outgoing_edges_of(node)) + } +} diff --git a/crates/bevy_graph/src/graphs/list/simple.rs b/crates/bevy_graph/src/graphs/list/simple.rs new file mode 100644 index 0000000000000..8778ad494d351 --- /dev/null +++ b/crates/bevy_graph/src/graphs/list/simple.rs @@ -0,0 +1,507 @@ +use slotmap::{HopSlotMap, SecondaryMap}; + +use crate::{ + algos::dfs::DepthFirstSearch, + error::GraphError, + graphs::{ + adjacency_storage::AdjacencyStorage, + edge::{Edge, EdgeMut, EdgeRef}, + keys::{EdgeIdx, NodeIdx}, + DirectedGraph, Graph, + }, + iters, + utils::{ + iter_choice::IterChoice, vecmap::VecMap, wrapped_indices_iterator::WrappedIndicesIterator, + }, +}; + +type SimpleListStorage = Vec<(NodeIdx, EdgeIdx)>; + +/// Implementation of a `SimpleGraph` which uses `Vec<(NodeIdx, EdgeIdx)>` for adjacencies +/// +/// `SimpleGraph`s can only hold one edge between two nodes and can't have edges between the same node +#[derive(Clone)] +pub struct SimpleListGraph { + nodes: HopSlotMap, + edges: HopSlotMap>, + adjacencies: SecondaryMap>, +} + +impl Graph for SimpleListGraph { + fn new() -> Self { + Self { + nodes: HopSlotMap::with_key(), + edges: HopSlotMap::with_key(), + adjacencies: SecondaryMap::new(), + } + } + + fn with_capacity(node_capacity: usize, edge_capacity: usize) -> Self { + Self { + nodes: HopSlotMap::with_capacity_and_key(node_capacity), + edges: HopSlotMap::with_capacity_and_key(edge_capacity), + adjacencies: SecondaryMap::with_capacity(node_capacity), + } + } + + #[inline] + fn capacity(&self) -> (usize, usize) { + (self.nodes.capacity(), self.edges.capacity()) + } + + #[inline] + fn node_capacity(&self) -> usize { + self.nodes.capacity() + } + + #[inline] + fn edge_capacity(&self) -> usize { + self.edges.capacity() + } + + #[inline] + fn reserve_nodes(&mut self, additional: usize) { + self.nodes.reserve(additional); + } + + #[inline] + fn reserve_edges(&mut self, additional: usize) { + self.edges.reserve(additional); + } + + #[inline] + fn is_directed(&self) -> bool { + DIRECTED + } + + #[inline] + fn is_multigraph(&self) -> bool { + false + } + + #[inline] + fn node_count(&self) -> usize { + self.nodes.len() + } + + #[inline] + fn edge_count(&self) -> usize { + self.edges.len() + } + + fn add_node(&mut self, node: N) -> NodeIdx { + let idx = self.nodes.insert(node); + let storage = if DIRECTED { + AdjacencyStorage::Directed(Vec::new(), Vec::new()) + } else { + AdjacencyStorage::Undirected(Vec::new()) + }; + self.adjacencies.insert(idx, storage); + idx + } + + fn try_add_edge( + &mut self, + src: NodeIdx, + dst: NodeIdx, + value: E, + ) -> Result { + if !self.contains_node(src) { + Err(GraphError::NodeNotFound(src)) + } else if !self.contains_node(dst) { + Err(GraphError::NodeNotFound(dst)) + } else if self.contains_edge_between(src, dst) { + Err(GraphError::ContainsEdgeBetween) + } else if src == dst { + Err(GraphError::Loop) + } else { + unsafe { + let idx = self.edges.insert(Edge(src, dst, value)); + self.adjacencies + .get_unchecked_mut(src) + .outgoing_mut() + .push((dst, idx)); + self.adjacencies + .get_unchecked_mut(dst) + .incoming_mut() + .push((src, idx)); + Ok(idx) + } + } + } + + #[inline] + fn contains_node(&self, node: NodeIdx) -> bool { + self.nodes.contains_key(node) + } + + fn contains_edge_between(&self, src: NodeIdx, dst: NodeIdx) -> bool { + if let Some(a) = self.adjacencies.get(src) { + a.outgoing().contains_key(dst) + } else { + false + } + } + + fn remove_node(&mut self, index: NodeIdx) -> Option { + if self.contains_node(index) { + let edges_to_remove = self + .edges_of(index) + .into_indices() + .collect::>(); + for edge_idx in edges_to_remove { + unsafe { + let Edge(src, dst, _) = self.edges.remove(edge_idx).unwrap_unchecked(); + self.adjacencies + .get_unchecked_mut(src) + .outgoing_mut() + .remove_by_key(dst); + self.adjacencies + .get_unchecked_mut(dst) + .incoming_mut() + .remove_by_key(src); + } + } + unsafe { + self.adjacencies.remove(index).unwrap_unchecked(); + } + self.nodes.remove(index) + } else { + None + } + } + + fn remove_edge(&mut self, index: EdgeIdx) -> Option { + if let Some(Edge(src, dst, value)) = self.edges.remove(index) { + unsafe { + self.adjacencies + .get_unchecked_mut(src) + .outgoing_mut() + .remove_by_key(dst); + self.adjacencies + .get_unchecked_mut(dst) + .incoming_mut() + .remove_by_key(src); + } + Some(value) + } else { + None + } + } + + fn clear_edges(&mut self) { + self.adjacencies + .values_mut() + .for_each(|list| list.for_each_mut(Vec::clear)); + self.edges.clear(); + } + + fn clear(&mut self) { + self.adjacencies.clear(); + self.edges.clear(); + self.nodes.clear(); + } + + #[inline] + fn get_node(&self, index: NodeIdx) -> Option<&N> { + self.nodes.get(index) + } + + #[inline] + fn get_node_mut(&mut self, index: NodeIdx) -> Option<&mut N> { + self.nodes.get_mut(index) + } + + fn find_node(&self, value: &N) -> Option + where + N: PartialEq, + { + self.nodes + .iter() + .find_map(|(key, val)| if val == value { Some(key) } else { None }) + } + + #[inline] + unsafe fn get_edge_raw(&mut self, index: EdgeIdx) -> Option<&mut Edge> { + self.edges.get_mut(index) + } + + #[inline] + fn get_edge(&self, index: EdgeIdx) -> Option> { + self.edges.get(index).map(|edge| edge.as_ref_edge()) + } + + #[inline] + fn get_edge_mut(&mut self, index: EdgeIdx) -> Option> { + self.edges.get_mut(index).map(|edge| edge.as_mut_edge()) + } + + fn find_edge(&self, value: &E) -> Option + where + E: PartialEq, + { + self.edges + .iter() + .find_map(|(key, val)| if &val.2 == value { Some(key) } else { None }) + } + + fn degree(&self, index: NodeIdx) -> usize { + if DIRECTED { + self.in_degree(index) + self.out_degree(index) + } else { + self.adjacencies[index].incoming().len() + } + } + + type NodeIndices<'n> = slotmap::hop::Keys<'n, NodeIdx, N> where Self: 'n; + fn node_indices(&self) -> Self::NodeIndices<'_> { + self.nodes.keys() + } + + unsafe fn nodes_raw(&self) -> &slotmap::HopSlotMap { + &self.nodes + } + + type Nodes<'n> = slotmap::hop::Values<'n, NodeIdx, N> where Self: 'n; + fn nodes(&self) -> Self::Nodes<'_> { + self.nodes.values() + } + + unsafe fn nodes_mut_raw(&mut self) -> &mut HopSlotMap { + &mut self.nodes + } + + type NodesMut<'n> = slotmap::hop::ValuesMut<'n, NodeIdx, N> where Self: 'n; + fn nodes_mut(&mut self) -> Self::NodesMut<'_> { + self.nodes.values_mut() + } + + type EdgeIndices<'e> = slotmap::hop::Keys<'e, EdgeIdx, Edge> where Self: 'e; + fn edge_indices(&self) -> Self::EdgeIndices<'_> { + self.edges.keys() + } + + unsafe fn edges_raw(&self) -> &HopSlotMap> { + &self.edges + } + + type Edges<'e> = iters::EdgesRef<'e, E, slotmap::hop::Values<'e, EdgeIdx, Edge>> where Self: 'e; + fn edges(&self) -> Self::Edges<'_> { + iters::EdgesRef::new(self.edges.values()) + } + + unsafe fn edges_mut_raw(&mut self) -> &mut HopSlotMap> { + &mut self.edges + } + + type EdgesMut<'e> = iters::EdgesMut<'e, E, slotmap::hop::ValuesMut<'e, EdgeIdx, Edge>> where Self: 'e; + fn edges_mut(&mut self) -> Self::EdgesMut<'_> { + iters::EdgesMut::new(self.edges.values_mut()) + } + + type EdgesOf<'e> = iters::EdgesByIdx<'e, E, &'e EdgeIdx, IterChoice<&'e EdgeIdx, std::iter::Chain, crate::utils::vecmap::Values<'e, NodeIdx, EdgeIdx>>, crate::utils::vecmap::Values<'e, NodeIdx, EdgeIdx>>> where Self: 'e; + fn edges_of(&self, index: NodeIdx) -> Self::EdgesOf<'_> { + let inner = if DIRECTED { + IterChoice::new_first( + self.adjacencies[index] + .incoming() + .values() + .chain(self.adjacencies[index].outgoing().values()), + ) + } else { + IterChoice::new_second(self.adjacencies[index].incoming().values()) + }; + iters::EdgesByIdx::new(inner, &self.edges) + } + + type EdgesOfMut<'e> = iters::EdgesByIdxMut<'e, E, &'e EdgeIdx, IterChoice<&'e EdgeIdx, std::iter::Chain, crate::utils::vecmap::Values<'e, NodeIdx, EdgeIdx>>, crate::utils::vecmap::Values<'e, NodeIdx, EdgeIdx>>> where Self: 'e; + fn edges_of_mut(&mut self, index: NodeIdx) -> Self::EdgesOfMut<'_> { + let inner = if DIRECTED { + IterChoice::new_first( + self.adjacencies[index] + .incoming() + .values() + .chain(self.adjacencies[index].outgoing().values()), + ) + } else { + IterChoice::new_second(self.adjacencies[index].incoming().values()) + }; + iters::EdgesByIdxMut::new(inner, &mut self.edges) + } + + type Neighbors<'n> = iters::NodesByIdx<'n, N, &'n NodeIdx, IterChoice<&'n NodeIdx, std::iter::Chain, crate::utils::vecmap::Keys<'n, NodeIdx, EdgeIdx>>, crate::utils::vecmap::Keys<'n, NodeIdx, EdgeIdx>>> where Self: 'n; + fn neighbors(&self, index: NodeIdx) -> Self::Neighbors<'_> { + let inner = if DIRECTED { + IterChoice::new_first( + self.adjacencies[index] + .incoming() + .keys() + .chain(self.adjacencies[index].outgoing().keys()), + ) + } else { + IterChoice::new_second(self.adjacencies[index].incoming().keys()) + }; + iters::NodesByIdx::new(inner, &self.nodes) + } + + type NeighborsMut<'n> = iters::NodesByIdxMut<'n, N, &'n NodeIdx, IterChoice<&'n NodeIdx, std::iter::Chain, crate::utils::vecmap::Keys<'n, NodeIdx, EdgeIdx>>, crate::utils::vecmap::Keys<'n, NodeIdx, EdgeIdx>>> where Self: 'n; + fn neighbors_mut(&mut self, index: NodeIdx) -> Self::NeighborsMut<'_> { + let inner = if DIRECTED { + IterChoice::new_first( + self.adjacencies[index] + .incoming() + .keys() + .chain(self.adjacencies[index].outgoing().keys()), + ) + } else { + IterChoice::new_second(self.adjacencies[index].incoming().keys()) + }; + iters::NodesByIdxMut::new(inner, &mut self.nodes) + } + + type Isolated<'n> = iters::Isolated<&'n N, iters::ZipDegree<'n, SimpleListStorage, &'n N, slotmap::hop::Iter<'n, NodeIdx, N>, DIRECTED>> where Self: 'n; + fn isolated(&self) -> Self::Isolated<'_> { + iters::Isolated::new(iters::ZipDegree::new(self.nodes.iter(), &self.adjacencies)) + } + + type IsolatedMut<'n> = iters::Isolated<&'n mut N, iters::ZipDegree<'n, SimpleListStorage, &'n mut N, slotmap::hop::IterMut<'n, NodeIdx, N>, DIRECTED>> where Self: 'n; + fn isolated_mut(&mut self) -> Self::IsolatedMut<'_> { + iters::Isolated::new(iters::ZipDegree::new( + self.nodes.iter_mut(), + &self.adjacencies, + )) + } + + #[inline] + fn in_degree(&self, index: NodeIdx) -> usize { + self.adjacencies[index].incoming().len() + } + + #[inline] + fn out_degree(&self, index: NodeIdx) -> usize { + self.adjacencies[index].outgoing().len() + } + + type IncomingEdgesOf<'e> = iters::EdgesByIdx<'e, E, &'e EdgeIdx, crate::utils::vecmap::Values<'e, NodeIdx, EdgeIdx>> where Self: 'e; + fn incoming_edges_of(&self, index: NodeIdx) -> Self::IncomingEdgesOf<'_> { + iters::EdgesByIdx::new(self.adjacencies[index].incoming().values(), &self.edges) + } + + type IncomingEdgesOfMut<'e> = iters::EdgesByIdxMut<'e, E, &'e EdgeIdx, crate::utils::vecmap::Values<'e, NodeIdx, EdgeIdx>> where Self: 'e; + fn incoming_edges_of_mut(&mut self, index: NodeIdx) -> Self::IncomingEdgesOfMut<'_> { + iters::EdgesByIdxMut::new(self.adjacencies[index].incoming().values(), &mut self.edges) + } + + type OutgoingEdgesOf<'e> = iters::EdgesByIdx<'e, E, &'e EdgeIdx, crate::utils::vecmap::Values<'e, NodeIdx, EdgeIdx>> where Self: 'e; + fn outgoing_edges_of(&self, index: NodeIdx) -> Self::IncomingEdgesOf<'_> { + iters::EdgesByIdx::new(self.adjacencies[index].outgoing().values(), &self.edges) + } + + type OutgoingEdgesOfMut<'e> = iters::EdgesByIdxMut<'e, E, &'e EdgeIdx, crate::utils::vecmap::Values<'e, NodeIdx, EdgeIdx>> where Self: 'e; + fn outgoing_edges_of_mut(&mut self, index: NodeIdx) -> Self::IncomingEdgesOfMut<'_> { + iters::EdgesByIdxMut::new(self.adjacencies[index].outgoing().values(), &mut self.edges) + } + + type InNeighbors<'n> = iters::NodesByIdx<'n, N, &'n NodeIdx, crate::utils::vecmap::Keys<'n, NodeIdx, EdgeIdx>> where Self: 'n; + fn in_neighbors(&self, index: NodeIdx) -> Self::InNeighbors<'_> { + iters::NodesByIdx::new(self.adjacencies[index].incoming().keys(), &self.nodes) + } + + type InNeighborsMut<'n> = iters::NodesByIdxMut<'n, N, &'n NodeIdx, crate::utils::vecmap::Keys<'n, NodeIdx, EdgeIdx>> where Self: 'n; + fn in_neighbors_mut(&mut self, index: NodeIdx) -> Self::InNeighborsMut<'_> { + iters::NodesByIdxMut::new(self.adjacencies[index].incoming().keys(), &mut self.nodes) + } + + type OutNeighbors<'n> = iters::NodesByIdx<'n, N, &'n NodeIdx, crate::utils::vecmap::Keys<'n, NodeIdx, EdgeIdx>> where Self: 'n; + fn out_neighbors(&self, index: NodeIdx) -> Self::OutNeighbors<'_> { + iters::NodesByIdx::new(self.adjacencies[index].outgoing().keys(), &self.nodes) + } + + type OutNeighborsMut<'n> = iters::NodesByIdxMut<'n, N, &'n NodeIdx, crate::utils::vecmap::Keys<'n, NodeIdx, EdgeIdx>> where Self: 'n; + fn out_neighbors_mut(&mut self, index: NodeIdx) -> Self::OutNeighborsMut<'_> { + iters::NodesByIdxMut::new( + self.adjacencies[index].outgoing_mut().keys(), + &mut self.nodes, + ) + } + + type Sources<'n> = iters::Isolated<&'n N, iters::ZipInDegree<'n, SimpleListStorage, &'n N, slotmap::hop::Iter<'n, NodeIdx, N>>> where Self: 'n; + fn sources(&self) -> Self::Sources<'_> { + iters::Isolated::new(iters::ZipInDegree::new( + self.nodes.iter(), + &self.adjacencies, + )) + } + + type SourcesMut<'n> = iters::Isolated<&'n mut N, iters::ZipInDegree<'n, SimpleListStorage, &'n mut N, slotmap::hop::IterMut<'n, NodeIdx, N>>> where Self: 'n; + fn sources_mut(&mut self) -> Self::SourcesMut<'_> { + iters::Isolated::new(iters::ZipInDegree::new( + self.nodes.iter_mut(), + &self.adjacencies, + )) + } + + type Sinks<'n> = iters::Isolated<&'n N, iters::ZipOutDegree<'n, SimpleListStorage, &'n N, slotmap::hop::Iter<'n, NodeIdx, N>>> where Self: 'n; + fn sinks(&self) -> Self::Sinks<'_> { + iters::Isolated::new(iters::ZipOutDegree::new( + self.nodes.iter(), + &self.adjacencies, + )) + } + + type SinksMut<'n> = iters::Isolated<&'n mut N, iters::ZipOutDegree<'n, SimpleListStorage, &'n mut N, slotmap::hop::IterMut<'n, NodeIdx, N>>> where Self: 'n; + fn sinks_mut(&mut self) -> Self::SinksMut<'_> { + iters::Isolated::new(iters::ZipOutDegree::new( + self.nodes.iter_mut(), + &self.adjacencies, + )) + } +} + +impl DirectedGraph for SimpleListGraph { + fn reverse(&mut self) { + self.adjacencies + .values_mut() + .for_each(|list| list.for_each_mut(Vec::clear)); + + for (index, Edge(src, dst, _)) in &mut self.edges { + std::mem::swap(src, dst); // *dst is now *src + self.adjacencies[*src].outgoing_mut().push((*dst, index)); + self.adjacencies[*dst].incoming_mut().push((*src, index)); + } + } + + fn reverse_edge(&mut self, index: EdgeIdx) { + if let Some(Edge(src, dst, _)) = self.edges.get_mut(index) { + self.adjacencies[*src].outgoing_mut().remove_by_key(*dst); + self.adjacencies[*dst].incoming_mut().remove_by_key(*src); + std::mem::swap(src, dst); // *dst is now *src + self.adjacencies[*src].outgoing_mut().push((*dst, index)); + self.adjacencies[*dst].incoming_mut().push((*src, index)); + } + } + + type Ancestors<'n> = iters::NodesByIdx<'n, N, NodeIdx, DepthFirstSearch<'n, N, E, Self, Self::IncomingEdgesOf<'n>>> where Self: 'n; + fn ancestors(&self, index: NodeIdx) -> Self::Ancestors<'_> { + // use DFS here, Vec should be faster than VecDeque + DepthFirstSearch::custom_ref(self, index, |graph, node| graph.incoming_edges_of(node)) + } + + type AncestorsMut<'n> = iters::NodesByIdxMut<'n, N, NodeIdx, DepthFirstSearch<'n, N, E, Self, Self::IncomingEdgesOf<'n>>> where Self: 'n; + fn ancestors_mut(&mut self, index: NodeIdx) -> Self::AncestorsMut<'_> { + // use DFS here, Vec should be faster than VecDeque + DepthFirstSearch::custom_mut(self, index, |graph, node| graph.incoming_edges_of(node)) + } + + type Descendants<'n> = iters::NodesByIdx<'n, N, NodeIdx, DepthFirstSearch<'n, N, E, Self, Self::OutgoingEdgesOf<'n>>> where Self: 'n; + fn descendants(&self, index: NodeIdx) -> Self::Descendants<'_> { + // use DFS here, Vec should be faster than VecDeque + DepthFirstSearch::custom_ref(self, index, |graph, node| graph.outgoing_edges_of(node)) + } + + type DescendantsMut<'n> = iters::NodesByIdxMut<'n, N, NodeIdx, DepthFirstSearch<'n, N, E, Self, Self::OutgoingEdgesOf<'n>>> where Self: 'n; + fn descendants_mut(&mut self, index: NodeIdx) -> Self::DescendantsMut<'_> { + // use DFS here, Vec should be faster than VecDeque + DepthFirstSearch::custom_mut(self, index, |graph, node| graph.outgoing_edges_of(node)) + } +} diff --git a/crates/bevy_graph/src/graphs/map/mod.rs b/crates/bevy_graph/src/graphs/map/mod.rs new file mode 100644 index 0000000000000..3d7e0b9b6cf89 --- /dev/null +++ b/crates/bevy_graph/src/graphs/map/mod.rs @@ -0,0 +1,5 @@ +mod simple; +pub use simple::*; + +mod multi; +pub use multi::*; diff --git a/crates/bevy_graph/src/graphs/map/multi.rs b/crates/bevy_graph/src/graphs/map/multi.rs new file mode 100644 index 0000000000000..dc4fa453a5ddd --- /dev/null +++ b/crates/bevy_graph/src/graphs/map/multi.rs @@ -0,0 +1,586 @@ +use hashbrown::HashMap; +use slotmap::{HopSlotMap, SecondaryMap}; + +use crate::{ + algos::dfs::DepthFirstSearch, + error::GraphError, + graphs::{ + adjacency_storage::AdjacencyStorage, + edge::{Edge, EdgeMut, EdgeRef}, + keys::{EdgeIdx, NodeIdx}, + DirectedGraph, Graph, + }, + iters, + utils::{ + iter_choice::IterChoice, vecset::VecSet, wrapped_indices_iterator::WrappedIndicesIterator, + }, +}; + +type MultiMapStorage = HashMap>; + +/// Implementation of a `MultiGraph` which uses `HashMap>` for adjacencies +/// +/// `MultiGraph`s can hold multiple edges between two nodes and edges between the same node +#[derive(Clone)] +pub struct MultiMapGraph { + nodes: HopSlotMap, + edges: HopSlotMap>, + adjacencies: SecondaryMap>, +} + +impl Graph for MultiMapGraph { + fn new() -> Self { + Self { + nodes: HopSlotMap::with_key(), + edges: HopSlotMap::with_key(), + adjacencies: SecondaryMap::new(), + } + } + + fn with_capacity(node_capacity: usize, edge_capacity: usize) -> Self { + Self { + nodes: HopSlotMap::with_capacity_and_key(node_capacity), + edges: HopSlotMap::with_capacity_and_key(edge_capacity), + adjacencies: SecondaryMap::with_capacity(node_capacity), + } + } + + #[inline] + fn capacity(&self) -> (usize, usize) { + (self.nodes.capacity(), self.edges.capacity()) + } + + #[inline] + fn node_capacity(&self) -> usize { + self.nodes.capacity() + } + + #[inline] + fn edge_capacity(&self) -> usize { + self.edges.capacity() + } + + #[inline] + fn reserve_nodes(&mut self, additional: usize) { + self.nodes.reserve(additional); + } + + #[inline] + fn reserve_edges(&mut self, additional: usize) { + self.edges.reserve(additional); + } + + #[inline] + fn is_directed(&self) -> bool { + DIRECTED + } + + #[inline] + fn is_multigraph(&self) -> bool { + true + } + + #[inline] + fn node_count(&self) -> usize { + self.nodes.len() + } + + #[inline] + fn edge_count(&self) -> usize { + self.edges.len() + } + + fn add_node(&mut self, node: N) -> NodeIdx { + let idx = self.nodes.insert(node); + let storage = if DIRECTED { + AdjacencyStorage::Directed(HashMap::new(), HashMap::new()) + } else { + AdjacencyStorage::Undirected(HashMap::new()) + }; + self.adjacencies.insert(idx, storage); + idx + } + + fn try_add_edge( + &mut self, + src: NodeIdx, + dst: NodeIdx, + value: E, + ) -> Result { + if !self.contains_node(src) { + Err(GraphError::NodeNotFound(src)) + } else if !self.contains_node(dst) { + Err(GraphError::NodeNotFound(dst)) + } else { + unsafe { + let idx = self.edges.insert(Edge(src, dst, value)); + self.adjacencies + .get_unchecked_mut(src) + .outgoing_mut() + .entry(dst) + .or_default() + .push(idx); + self.adjacencies + .get_unchecked_mut(dst) + .incoming_mut() + .entry(src) + .or_default() + .push(idx); + Ok(idx) + } + } + } + + #[inline] + fn contains_node(&self, node: NodeIdx) -> bool { + self.nodes.contains_key(node) + } + + fn contains_edge_between(&self, src: NodeIdx, dst: NodeIdx) -> bool { + if let Some(a1) = self.adjacencies.get(src) { + if let Some(a2) = a1.outgoing().get(&dst) { + !a2.is_empty() + } else { + false + } + } else { + false + } + } + + fn remove_node(&mut self, index: NodeIdx) -> Option { + if self.contains_node(index) { + let edges_to_remove = self + .edges_of(index) + .into_indices() + .collect::>(); + for edge_idx in edges_to_remove { + unsafe { + let Edge(src, dst, _) = self.edges.remove(edge_idx).unwrap_unchecked(); + self.adjacencies + .get_unchecked_mut(src) + .outgoing_mut() + .get_mut(&dst) + .unwrap() + .remove_by_value(&edge_idx); + self.adjacencies + .get_unchecked_mut(dst) + .incoming_mut() + .get_mut(&src) + .unwrap() + .remove_by_value(&edge_idx); + } + } + unsafe { + self.adjacencies.remove(index).unwrap_unchecked(); + } + self.nodes.remove(index) + } else { + None + } + } + + fn remove_edge(&mut self, index: EdgeIdx) -> Option { + if let Some(Edge(src, dst, value)) = self.edges.remove(index) { + unsafe { + self.adjacencies + .get_unchecked_mut(src) + .outgoing_mut() + .get_mut(&dst) + .unwrap() + .remove_by_value(&index); + self.adjacencies + .get_unchecked_mut(dst) + .incoming_mut() + .get_mut(&src) + .unwrap() + .remove_by_value(&index); + } + Some(value) + } else { + None + } + } + + fn clear_edges(&mut self) { + self.adjacencies + .values_mut() + .for_each(|map| map.for_each_mut(HashMap::clear)); + self.edges.clear(); + } + + fn clear(&mut self) { + self.adjacencies.clear(); + self.edges.clear(); + self.nodes.clear(); + } + + #[inline] + fn get_node(&self, index: NodeIdx) -> Option<&N> { + self.nodes.get(index) + } + + #[inline] + fn get_node_mut(&mut self, index: NodeIdx) -> Option<&mut N> { + self.nodes.get_mut(index) + } + + fn find_node(&self, value: &N) -> Option + where + N: PartialEq, + { + self.nodes + .iter() + .find_map(|(key, val)| if val == value { Some(key) } else { None }) + } + + #[inline] + unsafe fn get_edge_raw(&mut self, index: EdgeIdx) -> Option<&mut Edge> { + self.edges.get_mut(index) + } + + #[inline] + fn get_edge(&self, index: EdgeIdx) -> Option> { + self.edges.get(index).map(|edge| edge.as_ref_edge()) + } + + #[inline] + fn get_edge_mut(&mut self, index: EdgeIdx) -> Option> { + self.edges.get_mut(index).map(|edge| edge.as_mut_edge()) + } + + fn find_edge(&self, value: &E) -> Option + where + E: PartialEq, + { + self.edges + .iter() + .find_map(|(key, val)| if &val.2 == value { Some(key) } else { None }) + } + + fn degree(&self, index: NodeIdx) -> usize { + if DIRECTED { + self.in_degree(index) + self.out_degree(index) + } else { + self.adjacencies[index] + .incoming() + .iter() + .map(|(_, vec)| vec.len()) + .sum() + } + } + + type NodeIndices<'n> = slotmap::hop::Keys<'n, NodeIdx, N> where Self: 'n; + fn node_indices(&self) -> Self::NodeIndices<'_> { + self.nodes.keys() + } + + unsafe fn nodes_raw(&self) -> &slotmap::HopSlotMap { + &self.nodes + } + + type Nodes<'n> = slotmap::hop::Values<'n, NodeIdx, N> where Self: 'n; + fn nodes(&self) -> Self::Nodes<'_> { + self.nodes.values() + } + + unsafe fn nodes_mut_raw(&mut self) -> &mut HopSlotMap { + &mut self.nodes + } + + type NodesMut<'n> = slotmap::hop::ValuesMut<'n, NodeIdx, N> where Self: 'n; + fn nodes_mut(&mut self) -> Self::NodesMut<'_> { + self.nodes.values_mut() + } + + type EdgeIndices<'e> = slotmap::hop::Keys<'e, EdgeIdx, Edge> where Self: 'e; + fn edge_indices(&self) -> Self::EdgeIndices<'_> { + self.edges.keys() + } + + unsafe fn edges_raw(&self) -> &HopSlotMap> { + &self.edges + } + + type Edges<'e> = iters::EdgesRef<'e, E, slotmap::hop::Values<'e, EdgeIdx, Edge>> where Self: 'e; + fn edges(&self) -> Self::Edges<'_> { + iters::EdgesRef::new(self.edges.values()) + } + + unsafe fn edges_mut_raw(&mut self) -> &mut HopSlotMap> { + &mut self.edges + } + + type EdgesMut<'e> = iters::EdgesMut<'e, E, slotmap::hop::ValuesMut<'e, EdgeIdx, Edge>> where Self: 'e; + fn edges_mut(&mut self) -> Self::EdgesMut<'_> { + iters::EdgesMut::new(self.edges.values_mut()) + } + + type EdgesOf<'e> = iters::EdgesByIdx<'e, E, &'e EdgeIdx, IterChoice<&'e EdgeIdx, std::iter::Flatten, std::iter::Chain>, iters::SkipIndexIter<'e, &'e Vec, hashbrown::hash_map::Iter<'e, NodeIdx, Vec>>>, false>>, iters::LoopSafetyIter<'e, E, &'e EdgeIdx, std::iter::Flatten, hashbrown::hash_map::Iter<'e, NodeIdx, Vec>, false>>>>> where Self: 'e; + fn edges_of(&self, index: NodeIdx) -> Self::EdgesOf<'_> { + let inner = if DIRECTED { + IterChoice::new_first( + iters::TupleExtract::new_second(self.adjacencies[index].incoming().iter().chain( + iters::SkipIndexIter::new(self.adjacencies[index].outgoing().iter(), index), // the outgoing will skip self to ensure loops will only come once (from incoming) + )) + .flatten(), + ) + } else { + IterChoice::new_second(iters::LoopSafetyIter::new( + iters::TupleExtract::new_second(self.adjacencies[index].incoming().iter()) + .flatten(), + &self.edges, + )) + }; + iters::EdgesByIdx::new(inner, &self.edges) + } + + type EdgesOfMut<'e> = iters::EdgesByIdxMut<'e, E, &'e EdgeIdx, IterChoice<&'e EdgeIdx, std::iter::Flatten, std::iter::Chain>, iters::SkipIndexIter<'e, &'e Vec, hashbrown::hash_map::Iter<'e, NodeIdx, Vec>>>, false>>, iters::LoopSafetyIter<'e, E, &'e EdgeIdx, std::iter::Flatten, hashbrown::hash_map::Iter<'e, NodeIdx, Vec>, false>>>>> where Self: 'e; + fn edges_of_mut(&mut self, index: NodeIdx) -> Self::EdgesOfMut<'_> { + let inner = if DIRECTED { + IterChoice::new_first( + iters::TupleExtract::new_second(self.adjacencies[index].incoming().iter().chain( + iters::SkipIndexIter::new(self.adjacencies[index].outgoing().iter(), index), // the outgoing will skip self to ensure loops will only come once (from incoming) + )) + .flatten(), + ) + } else { + unsafe { + // SAFETY: it should be ok - no data will be removed + let ptr: *mut HopSlotMap> = &mut self.edges; + IterChoice::new_second(iters::LoopSafetyIter::new( + iters::TupleExtract::new_second(self.adjacencies[index].incoming().iter()) + .flatten(), + &*ptr, + )) + } + }; + iters::EdgesByIdxMut::new(inner, &mut self.edges) + } + + type Neighbors<'n> = iters::NodesByIdx<'n, N, &'n NodeIdx, iters::NodeJustOnceIter<&'n NodeIdx, iters::TupleExtract<&'n NodeIdx, &'n Vec, IterChoice<(&'n NodeIdx, &'n Vec), std::iter::Chain>, hashbrown::hash_map::Iter<'n, NodeIdx, Vec>>, hashbrown::hash_map::Iter<'n, NodeIdx, Vec>>, true>>> where Self: 'n; + fn neighbors(&self, index: NodeIdx) -> Self::Neighbors<'_> { + let inner = if DIRECTED { + IterChoice::new_first( + self.adjacencies[index] + .incoming() + .iter() + .chain(self.adjacencies[index].outgoing().iter()), + ) + } else { + IterChoice::new_second(self.adjacencies[index].incoming().iter()) + }; + iters::NodesByIdx::new( + iters::NodeJustOnceIter::new(iters::TupleExtract::new_first(inner)), + &self.nodes, + ) + } + + type NeighborsMut<'n> = iters::NodesByIdxMut<'n, N, &'n NodeIdx, iters::NodeJustOnceIter<&'n NodeIdx, iters::TupleExtract<&'n NodeIdx, &'n Vec, IterChoice<(&'n NodeIdx, &'n Vec), std::iter::Chain>, hashbrown::hash_map::Iter<'n, NodeIdx, Vec>>, hashbrown::hash_map::Iter<'n, NodeIdx, Vec>>, true>>> where Self: 'n; + fn neighbors_mut(&mut self, index: NodeIdx) -> Self::NeighborsMut<'_> { + let inner = if DIRECTED { + IterChoice::new_first( + self.adjacencies[index] + .incoming() + .iter() + .chain(self.adjacencies[index].outgoing().iter()), + ) + } else { + IterChoice::new_second(self.adjacencies[index].incoming().iter()) + }; + iters::NodesByIdxMut::new( + iters::NodeJustOnceIter::new(iters::TupleExtract::new_first(inner)), + &mut self.nodes, + ) + } + + type Isolated<'n> = iters::Isolated<&'n N, iters::ZipDegree<'n, MultiMapStorage, &'n N, slotmap::hop::Iter<'n, NodeIdx, N>, DIRECTED>> where Self: 'n; + fn isolated(&self) -> Self::Isolated<'_> { + iters::Isolated::new(iters::ZipDegree::new(self.nodes.iter(), &self.adjacencies)) + } + + type IsolatedMut<'n> = iters::Isolated<&'n mut N, iters::ZipDegree<'n, MultiMapStorage, &'n mut N, slotmap::hop::IterMut<'n, NodeIdx, N>, DIRECTED>> where Self: 'n; + fn isolated_mut(&mut self) -> Self::IsolatedMut<'_> { + iters::Isolated::new(iters::ZipDegree::new( + self.nodes.iter_mut(), + &self.adjacencies, + )) + } + + #[inline] + fn in_degree(&self, index: NodeIdx) -> usize { + self.adjacencies[index] + .incoming() + .iter() + .map(|(_, vec)| vec.len()) + .sum() + } + + #[inline] + fn out_degree(&self, index: NodeIdx) -> usize { + self.adjacencies[index] + .outgoing() + .iter() + .map(|(_, vec)| vec.len()) + .sum() + } + + type IncomingEdgesOf<'e> = iters::EdgesByIdx<'e, E, &'e EdgeIdx, std::iter::Flatten>>> where Self: 'e; + fn incoming_edges_of(&self, index: NodeIdx) -> Self::IncomingEdgesOf<'_> { + iters::EdgesByIdx::new( + self.adjacencies[index].incoming().values().flatten(), + &self.edges, + ) + } + + type IncomingEdgesOfMut<'e> = iters::EdgesByIdxMut<'e, E, &'e EdgeIdx, std::iter::Flatten>>> where Self: 'e; + fn incoming_edges_of_mut(&mut self, index: NodeIdx) -> Self::IncomingEdgesOfMut<'_> { + iters::EdgesByIdxMut::new( + self.adjacencies[index].incoming().values().flatten(), + &mut self.edges, + ) + } + + type OutgoingEdgesOf<'e> = iters::EdgesByIdx<'e, E, &'e EdgeIdx, std::iter::Flatten>>> where Self: 'e; + fn outgoing_edges_of(&self, index: NodeIdx) -> Self::IncomingEdgesOf<'_> { + iters::EdgesByIdx::new( + self.adjacencies[index].outgoing().values().flatten(), + &self.edges, + ) + } + + type OutgoingEdgesOfMut<'e> = iters::EdgesByIdxMut<'e, E, &'e EdgeIdx, std::iter::Flatten>>> where Self: 'e; + fn outgoing_edges_of_mut(&mut self, index: NodeIdx) -> Self::IncomingEdgesOfMut<'_> { + iters::EdgesByIdxMut::new( + self.adjacencies[index].outgoing().values().flatten(), + &mut self.edges, + ) + } + + type InNeighbors<'n> = iters::NodesByIdx<'n, N, &'n NodeIdx, hashbrown::hash_map::Keys<'n, NodeIdx, Vec>> where Self: 'n; + fn in_neighbors(&self, index: NodeIdx) -> Self::InNeighbors<'_> { + iters::NodesByIdx::new(self.adjacencies[index].incoming().keys(), &self.nodes) + } + + type InNeighborsMut<'n> = iters::NodesByIdxMut<'n, N, &'n NodeIdx, hashbrown::hash_map::Keys<'n, NodeIdx, Vec>> where Self: 'n; + fn in_neighbors_mut(&mut self, index: NodeIdx) -> Self::InNeighborsMut<'_> { + iters::NodesByIdxMut::new(self.adjacencies[index].incoming().keys(), &mut self.nodes) + } + + type OutNeighbors<'n> = iters::NodesByIdx<'n, N, &'n NodeIdx, hashbrown::hash_map::Keys<'n, NodeIdx, Vec>> where Self: 'n; + fn out_neighbors(&self, index: NodeIdx) -> Self::OutNeighbors<'_> { + iters::NodesByIdx::new(self.adjacencies[index].outgoing().keys(), &self.nodes) + } + + type OutNeighborsMut<'n> = iters::NodesByIdxMut<'n, N, &'n NodeIdx, hashbrown::hash_map::Keys<'n, NodeIdx, Vec>> where Self: 'n; + fn out_neighbors_mut(&mut self, index: NodeIdx) -> Self::OutNeighborsMut<'_> { + iters::NodesByIdxMut::new( + self.adjacencies[index].outgoing_mut().keys(), + &mut self.nodes, + ) + } + + type Sources<'n> = iters::Isolated<&'n N, iters::ZipInDegree<'n, MultiMapStorage, &'n N, slotmap::hop::Iter<'n, NodeIdx, N>>> where Self: 'n; + fn sources(&self) -> Self::Sources<'_> { + iters::Isolated::new(iters::ZipInDegree::new( + self.nodes.iter(), + &self.adjacencies, + )) + } + + type SourcesMut<'n> = iters::Isolated<&'n mut N, iters::ZipInDegree<'n, MultiMapStorage, &'n mut N, slotmap::hop::IterMut<'n, NodeIdx, N>>> where Self: 'n; + fn sources_mut(&mut self) -> Self::SourcesMut<'_> { + iters::Isolated::new(iters::ZipInDegree::new( + self.nodes.iter_mut(), + &self.adjacencies, + )) + } + + type Sinks<'n> = iters::Isolated<&'n N, iters::ZipOutDegree<'n, MultiMapStorage, &'n N, slotmap::hop::Iter<'n, NodeIdx, N>>> where Self: 'n; + fn sinks(&self) -> Self::Sinks<'_> { + iters::Isolated::new(iters::ZipOutDegree::new( + self.nodes.iter(), + &self.adjacencies, + )) + } + + type SinksMut<'n> = iters::Isolated<&'n mut N, iters::ZipOutDegree<'n, MultiMapStorage, &'n mut N, slotmap::hop::IterMut<'n, NodeIdx, N>>> where Self: 'n; + fn sinks_mut(&mut self) -> Self::SinksMut<'_> { + iters::Isolated::new(iters::ZipOutDegree::new( + self.nodes.iter_mut(), + &self.adjacencies, + )) + } +} + +impl DirectedGraph for MultiMapGraph { + fn reverse(&mut self) { + self.adjacencies + .values_mut() + .for_each(|map| map.for_each_mut(HashMap::clear)); + + for (index, Edge(src, dst, _)) in &mut self.edges { + std::mem::swap(src, dst); // *dst is now *src + self.adjacencies[*src] + .outgoing_mut() + .entry(*dst) + .or_default() + .push(index); + self.adjacencies[*dst] + .incoming_mut() + .entry(*src) + .or_default() + .push(index); + } + } + + fn reverse_edge(&mut self, index: EdgeIdx) { + if let Some(Edge(src, dst, _)) = self.edges.get_mut(index) { + self.adjacencies[*src] + .outgoing_mut() + .get_mut(dst) + .unwrap() + .remove_by_value(&index); + self.adjacencies[*dst] + .incoming_mut() + .get_mut(src) + .unwrap() + .remove_by_value(&index); + std::mem::swap(src, dst); // *dst is now *src + self.adjacencies[*src] + .outgoing_mut() + .entry(*dst) + .or_default() + .push(index); + self.adjacencies[*dst] + .incoming_mut() + .entry(*src) + .or_default() + .push(index); + } + } + + type Ancestors<'n> = iters::NodesByIdx<'n, N, NodeIdx, DepthFirstSearch<'n, N, E, Self, Self::IncomingEdgesOf<'n>>> where Self: 'n; + fn ancestors(&self, index: NodeIdx) -> Self::Ancestors<'_> { + // use DFS here, Vec should be faster than VecDeque + DepthFirstSearch::custom_ref(self, index, |graph, node| graph.incoming_edges_of(node)) + } + + type AncestorsMut<'n> = iters::NodesByIdxMut<'n, N, NodeIdx, DepthFirstSearch<'n, N, E, Self, Self::IncomingEdgesOf<'n>>> where Self: 'n; + fn ancestors_mut(&mut self, index: NodeIdx) -> Self::AncestorsMut<'_> { + // use DFS here, Vec should be faster than VecDeque + DepthFirstSearch::custom_mut(self, index, |graph, node| graph.incoming_edges_of(node)) + } + + type Descendants<'n> = iters::NodesByIdx<'n, N, NodeIdx, DepthFirstSearch<'n, N, E, Self, Self::OutgoingEdgesOf<'n>>> where Self: 'n; + fn descendants(&self, index: NodeIdx) -> Self::Descendants<'_> { + // use DFS here, Vec should be faster than VecDeque + DepthFirstSearch::custom_ref(self, index, |graph, node| graph.outgoing_edges_of(node)) + } + + type DescendantsMut<'n> = iters::NodesByIdxMut<'n, N, NodeIdx, DepthFirstSearch<'n, N, E, Self, Self::OutgoingEdgesOf<'n>>> where Self: 'n; + fn descendants_mut(&mut self, index: NodeIdx) -> Self::DescendantsMut<'_> { + // use DFS here, Vec should be faster than VecDeque + DepthFirstSearch::custom_mut(self, index, |graph, node| graph.outgoing_edges_of(node)) + } +} diff --git a/crates/bevy_graph/src/graphs/map/simple.rs b/crates/bevy_graph/src/graphs/map/simple.rs new file mode 100644 index 0000000000000..177c9c0736d1f --- /dev/null +++ b/crates/bevy_graph/src/graphs/map/simple.rs @@ -0,0 +1,506 @@ +use hashbrown::HashMap; +use slotmap::{HopSlotMap, SecondaryMap}; + +use crate::{ + algos::dfs::DepthFirstSearch, + error::GraphError, + graphs::{ + adjacency_storage::AdjacencyStorage, + edge::{Edge, EdgeMut, EdgeRef}, + keys::{EdgeIdx, NodeIdx}, + DirectedGraph, Graph, + }, + iters, + utils::{iter_choice::IterChoice, wrapped_indices_iterator::WrappedIndicesIterator}, +}; + +type SimpleMapStorage = HashMap; + +/// Implementation of a `SimpleGraph` which uses `HashMap` for adjacencies +/// +/// `SimpleGraph`s can only hold one edge between two nodes and can't have edges between the same node +#[derive(Clone)] +pub struct SimpleMapGraph { + nodes: HopSlotMap, + edges: HopSlotMap>, + adjacencies: SecondaryMap>, +} + +impl Graph for SimpleMapGraph { + fn new() -> Self { + Self { + nodes: HopSlotMap::with_key(), + edges: HopSlotMap::with_key(), + adjacencies: SecondaryMap::new(), + } + } + + fn with_capacity(node_capacity: usize, edge_capacity: usize) -> Self { + Self { + nodes: HopSlotMap::with_capacity_and_key(node_capacity), + edges: HopSlotMap::with_capacity_and_key(edge_capacity), + adjacencies: SecondaryMap::with_capacity(node_capacity), + } + } + + #[inline] + fn capacity(&self) -> (usize, usize) { + (self.nodes.capacity(), self.edges.capacity()) + } + + #[inline] + fn node_capacity(&self) -> usize { + self.nodes.capacity() + } + + #[inline] + fn edge_capacity(&self) -> usize { + self.edges.capacity() + } + + #[inline] + fn reserve_nodes(&mut self, additional: usize) { + self.nodes.reserve(additional); + } + + #[inline] + fn reserve_edges(&mut self, additional: usize) { + self.edges.reserve(additional); + } + + #[inline] + fn is_directed(&self) -> bool { + DIRECTED + } + + #[inline] + fn is_multigraph(&self) -> bool { + false + } + + #[inline] + fn node_count(&self) -> usize { + self.nodes.len() + } + + #[inline] + fn edge_count(&self) -> usize { + self.edges.len() + } + + fn add_node(&mut self, node: N) -> NodeIdx { + let idx = self.nodes.insert(node); + let storage = if DIRECTED { + AdjacencyStorage::Directed(HashMap::new(), HashMap::new()) + } else { + AdjacencyStorage::Undirected(HashMap::new()) + }; + self.adjacencies.insert(idx, storage); + idx + } + + fn try_add_edge( + &mut self, + src: NodeIdx, + dst: NodeIdx, + value: E, + ) -> Result { + if !self.contains_node(src) { + Err(GraphError::NodeNotFound(src)) + } else if !self.contains_node(dst) { + Err(GraphError::NodeNotFound(dst)) + } else if self.contains_edge_between(src, dst) { + Err(GraphError::ContainsEdgeBetween) + } else if src == dst { + Err(GraphError::Loop) + } else { + unsafe { + let idx = self.edges.insert(Edge(src, dst, value)); + self.adjacencies + .get_unchecked_mut(src) + .outgoing_mut() + .insert(dst, idx); + self.adjacencies + .get_unchecked_mut(dst) + .incoming_mut() + .insert(src, idx); + Ok(idx) + } + } + } + + #[inline] + fn contains_node(&self, node: NodeIdx) -> bool { + self.nodes.contains_key(node) + } + + fn contains_edge_between(&self, src: NodeIdx, dst: NodeIdx) -> bool { + if let Some(a) = self.adjacencies.get(src) { + a.outgoing().contains_key(&dst) + } else { + false + } + } + + fn remove_node(&mut self, index: NodeIdx) -> Option { + if self.contains_node(index) { + let edges_to_remove = self + .edges_of(index) + .into_indices() + .collect::>(); + for edge_idx in edges_to_remove { + unsafe { + let Edge(src, dst, _) = self.edges.remove(edge_idx).unwrap_unchecked(); + self.adjacencies + .get_unchecked_mut(src) + .outgoing_mut() + .remove(&dst); + self.adjacencies + .get_unchecked_mut(dst) + .incoming_mut() + .remove(&src); + } + } + unsafe { + self.adjacencies.remove(index).unwrap_unchecked(); + } + self.nodes.remove(index) + } else { + None + } + } + + fn remove_edge(&mut self, index: EdgeIdx) -> Option { + if let Some(Edge(src, dst, value)) = self.edges.remove(index) { + unsafe { + self.adjacencies + .get_unchecked_mut(src) + .outgoing_mut() + .remove(&dst); + self.adjacencies + .get_unchecked_mut(dst) + .incoming_mut() + .remove(&src); + } + Some(value) + } else { + None + } + } + + fn clear_edges(&mut self) { + self.adjacencies + .values_mut() + .for_each(|map| map.for_each_mut(HashMap::clear)); + self.edges.clear(); + } + + fn clear(&mut self) { + self.adjacencies.clear(); + self.edges.clear(); + self.nodes.clear(); + } + + #[inline] + fn get_node(&self, index: NodeIdx) -> Option<&N> { + self.nodes.get(index) + } + + #[inline] + fn get_node_mut(&mut self, index: NodeIdx) -> Option<&mut N> { + self.nodes.get_mut(index) + } + + fn find_node(&self, value: &N) -> Option + where + N: PartialEq, + { + self.nodes + .iter() + .find_map(|(key, val)| if val == value { Some(key) } else { None }) + } + + #[inline] + unsafe fn get_edge_raw(&mut self, index: EdgeIdx) -> Option<&mut Edge> { + self.edges.get_mut(index) + } + + #[inline] + fn get_edge(&self, index: EdgeIdx) -> Option> { + self.edges.get(index).map(|edge| edge.as_ref_edge()) + } + + #[inline] + fn get_edge_mut(&mut self, index: EdgeIdx) -> Option> { + self.edges.get_mut(index).map(|edge| edge.as_mut_edge()) + } + + fn find_edge(&self, value: &E) -> Option + where + E: PartialEq, + { + self.edges + .iter() + .find_map(|(key, val)| if &val.2 == value { Some(key) } else { None }) + } + + fn degree(&self, index: NodeIdx) -> usize { + if DIRECTED { + self.in_degree(index) + self.out_degree(index) + } else { + self.adjacencies[index].incoming().len() + } + } + + type NodeIndices<'n> = slotmap::hop::Keys<'n, NodeIdx, N> where Self: 'n; + fn node_indices(&self) -> Self::NodeIndices<'_> { + self.nodes.keys() + } + + unsafe fn nodes_raw(&self) -> &slotmap::HopSlotMap { + &self.nodes + } + + type Nodes<'n> = slotmap::hop::Values<'n, NodeIdx, N> where Self: 'n; + fn nodes(&self) -> Self::Nodes<'_> { + self.nodes.values() + } + + type NodesMut<'n> = slotmap::hop::ValuesMut<'n, NodeIdx, N> where Self: 'n; + fn nodes_mut(&mut self) -> Self::NodesMut<'_> { + self.nodes.values_mut() + } + + unsafe fn nodes_mut_raw(&mut self) -> &mut HopSlotMap { + &mut self.nodes + } + + type EdgeIndices<'e> = slotmap::hop::Keys<'e, EdgeIdx, Edge> where Self: 'e; + fn edge_indices(&self) -> Self::EdgeIndices<'_> { + self.edges.keys() + } + + unsafe fn edges_raw(&self) -> &HopSlotMap> { + &self.edges + } + + type Edges<'e> = iters::EdgesRef<'e, E, slotmap::hop::Values<'e, EdgeIdx, Edge>> where Self: 'e; + fn edges(&self) -> Self::Edges<'_> { + iters::EdgesRef::new(self.edges.values()) + } + + unsafe fn edges_mut_raw(&mut self) -> &mut HopSlotMap> { + &mut self.edges + } + + type EdgesMut<'e> = iters::EdgesMut<'e, E, slotmap::hop::ValuesMut<'e, EdgeIdx, Edge>> where Self: 'e; + fn edges_mut(&mut self) -> Self::EdgesMut<'_> { + iters::EdgesMut::new(self.edges.values_mut()) + } + + type EdgesOf<'e> = iters::EdgesByIdx<'e, E, &'e EdgeIdx, IterChoice<&'e EdgeIdx, std::iter::Chain, hashbrown::hash_map::Values<'e, NodeIdx, EdgeIdx>>, hashbrown::hash_map::Values<'e, NodeIdx, EdgeIdx>>> where Self: 'e; + fn edges_of(&self, index: NodeIdx) -> Self::EdgesOf<'_> { + let inner = if DIRECTED { + IterChoice::new_first( + self.adjacencies[index] + .incoming() + .values() + .chain(self.adjacencies[index].outgoing().values()), + ) + } else { + IterChoice::new_second(self.adjacencies[index].incoming().values()) + }; + iters::EdgesByIdx::new(inner, &self.edges) + } + + type EdgesOfMut<'e> = iters::EdgesByIdxMut<'e, E, &'e EdgeIdx, IterChoice<&'e EdgeIdx, std::iter::Chain, hashbrown::hash_map::Values<'e, NodeIdx, EdgeIdx>>, hashbrown::hash_map::Values<'e, NodeIdx, EdgeIdx>>> where Self: 'e; + fn edges_of_mut(&mut self, index: NodeIdx) -> Self::EdgesOfMut<'_> { + let inner = if DIRECTED { + IterChoice::new_first( + self.adjacencies[index] + .incoming() + .values() + .chain(self.adjacencies[index].outgoing().values()), + ) + } else { + IterChoice::new_second(self.adjacencies[index].incoming().values()) + }; + iters::EdgesByIdxMut::new(inner, &mut self.edges) + } + + type Neighbors<'n> = iters::NodesByIdx<'n, N, &'n NodeIdx, IterChoice<&'n NodeIdx, std::iter::Chain, hashbrown::hash_map::Keys<'n, NodeIdx, EdgeIdx>>, hashbrown::hash_map::Keys<'n, NodeIdx, EdgeIdx>>> where Self: 'n; + fn neighbors(&self, index: NodeIdx) -> Self::Neighbors<'_> { + let inner = if DIRECTED { + IterChoice::new_first( + self.adjacencies[index] + .incoming() + .keys() + .chain(self.adjacencies[index].outgoing().keys()), + ) + } else { + IterChoice::new_second(self.adjacencies[index].incoming().keys()) + }; + iters::NodesByIdx::new(inner, &self.nodes) + } + + type NeighborsMut<'n> = iters::NodesByIdxMut<'n, N, &'n NodeIdx, IterChoice<&'n NodeIdx, std::iter::Chain, hashbrown::hash_map::Keys<'n, NodeIdx, EdgeIdx>>, hashbrown::hash_map::Keys<'n, NodeIdx, EdgeIdx>>> where Self: 'n; + fn neighbors_mut(&mut self, index: NodeIdx) -> Self::NeighborsMut<'_> { + let inner = if DIRECTED { + IterChoice::new_first( + self.adjacencies[index] + .incoming() + .keys() + .chain(self.adjacencies[index].outgoing().keys()), + ) + } else { + IterChoice::new_second(self.adjacencies[index].incoming().keys()) + }; + iters::NodesByIdxMut::new(inner, &mut self.nodes) + } + + type Isolated<'n> = iters::Isolated<&'n N, iters::ZipDegree<'n, SimpleMapStorage, &'n N, slotmap::hop::Iter<'n, NodeIdx, N>, DIRECTED>> where Self: 'n; + fn isolated(&self) -> Self::Isolated<'_> { + iters::Isolated::new(iters::ZipDegree::new(self.nodes.iter(), &self.adjacencies)) + } + + type IsolatedMut<'n> = iters::Isolated<&'n mut N, iters::ZipDegree<'n, SimpleMapStorage, &'n mut N, slotmap::hop::IterMut<'n, NodeIdx, N>, DIRECTED>> where Self: 'n; + fn isolated_mut(&mut self) -> Self::IsolatedMut<'_> { + iters::Isolated::new(iters::ZipDegree::new( + self.nodes.iter_mut(), + &self.adjacencies, + )) + } + + #[inline] + fn in_degree(&self, index: NodeIdx) -> usize { + self.adjacencies[index].incoming().len() + } + + #[inline] + fn out_degree(&self, index: NodeIdx) -> usize { + self.adjacencies[index].outgoing().len() + } + + type IncomingEdgesOf<'e> = iters::EdgesByIdx<'e, E, &'e EdgeIdx, hashbrown::hash_map::Values<'e, NodeIdx, EdgeIdx>> where Self: 'e; + fn incoming_edges_of(&self, index: NodeIdx) -> Self::IncomingEdgesOf<'_> { + iters::EdgesByIdx::new(self.adjacencies[index].incoming().values(), &self.edges) + } + + type IncomingEdgesOfMut<'e> = iters::EdgesByIdxMut<'e, E, &'e EdgeIdx, hashbrown::hash_map::Values<'e, NodeIdx, EdgeIdx>> where Self: 'e; + fn incoming_edges_of_mut(&mut self, index: NodeIdx) -> Self::IncomingEdgesOfMut<'_> { + iters::EdgesByIdxMut::new(self.adjacencies[index].incoming().values(), &mut self.edges) + } + + type OutgoingEdgesOf<'e> = iters::EdgesByIdx<'e, E, &'e EdgeIdx, hashbrown::hash_map::Values<'e, NodeIdx, EdgeIdx>> where Self: 'e; + fn outgoing_edges_of(&self, index: NodeIdx) -> Self::IncomingEdgesOf<'_> { + iters::EdgesByIdx::new(self.adjacencies[index].outgoing().values(), &self.edges) + } + + type OutgoingEdgesOfMut<'e> = iters::EdgesByIdxMut<'e, E, &'e EdgeIdx, hashbrown::hash_map::Values<'e, NodeIdx, EdgeIdx>> where Self: 'e; + fn outgoing_edges_of_mut(&mut self, index: NodeIdx) -> Self::IncomingEdgesOfMut<'_> { + iters::EdgesByIdxMut::new(self.adjacencies[index].outgoing().values(), &mut self.edges) + } + + type InNeighbors<'n> = iters::NodesByIdx<'n, N, &'n NodeIdx, hashbrown::hash_map::Keys<'n, NodeIdx, EdgeIdx>> where Self: 'n; + fn in_neighbors(&self, index: NodeIdx) -> Self::InNeighbors<'_> { + iters::NodesByIdx::new(self.adjacencies[index].incoming().keys(), &self.nodes) + } + + type InNeighborsMut<'n> = iters::NodesByIdxMut<'n, N, &'n NodeIdx, hashbrown::hash_map::Keys<'n, NodeIdx, EdgeIdx>> where Self: 'n; + fn in_neighbors_mut(&mut self, index: NodeIdx) -> Self::InNeighborsMut<'_> { + iters::NodesByIdxMut::new(self.adjacencies[index].incoming().keys(), &mut self.nodes) + } + + type OutNeighbors<'n> = iters::NodesByIdx<'n, N, &'n NodeIdx, hashbrown::hash_map::Keys<'n, NodeIdx, EdgeIdx>> where Self: 'n; + fn out_neighbors(&self, index: NodeIdx) -> Self::OutNeighbors<'_> { + iters::NodesByIdx::new(self.adjacencies[index].outgoing().keys(), &self.nodes) + } + + type OutNeighborsMut<'n> = iters::NodesByIdxMut<'n, N, &'n NodeIdx, hashbrown::hash_map::Keys<'n, NodeIdx, EdgeIdx>> where Self: 'n; + fn out_neighbors_mut(&mut self, index: NodeIdx) -> Self::OutNeighborsMut<'_> { + iters::NodesByIdxMut::new( + self.adjacencies[index].outgoing_mut().keys(), + &mut self.nodes, + ) + } + + type Sources<'n> = iters::Isolated<&'n N, iters::ZipInDegree<'n, SimpleMapStorage, &'n N, slotmap::hop::Iter<'n, NodeIdx, N>>> where Self: 'n; + fn sources(&self) -> Self::Sources<'_> { + iters::Isolated::new(iters::ZipInDegree::new( + self.nodes.iter(), + &self.adjacencies, + )) + } + + type SourcesMut<'n> = iters::Isolated<&'n mut N, iters::ZipInDegree<'n, SimpleMapStorage, &'n mut N, slotmap::hop::IterMut<'n, NodeIdx, N>>> where Self: 'n; + fn sources_mut(&mut self) -> Self::SourcesMut<'_> { + iters::Isolated::new(iters::ZipInDegree::new( + self.nodes.iter_mut(), + &self.adjacencies, + )) + } + + type Sinks<'n> = iters::Isolated<&'n N, iters::ZipOutDegree<'n, SimpleMapStorage, &'n N, slotmap::hop::Iter<'n, NodeIdx, N>>> where Self: 'n; + fn sinks(&self) -> Self::Sinks<'_> { + iters::Isolated::new(iters::ZipOutDegree::new( + self.nodes.iter(), + &self.adjacencies, + )) + } + + type SinksMut<'n> = iters::Isolated<&'n mut N, iters::ZipOutDegree<'n, SimpleMapStorage, &'n mut N, slotmap::hop::IterMut<'n, NodeIdx, N>>> where Self: 'n; + fn sinks_mut(&mut self) -> Self::SinksMut<'_> { + iters::Isolated::new(iters::ZipOutDegree::new( + self.nodes.iter_mut(), + &self.adjacencies, + )) + } +} + +impl DirectedGraph for SimpleMapGraph { + fn reverse(&mut self) { + self.adjacencies + .values_mut() + .for_each(|map| map.for_each_mut(HashMap::clear)); + + for (index, Edge(src, dst, _)) in &mut self.edges { + std::mem::swap(src, dst); // *dst is now *src + self.adjacencies[*src].outgoing_mut().insert(*dst, index); + self.adjacencies[*dst].incoming_mut().insert(*src, index); + } + } + + fn reverse_edge(&mut self, index: EdgeIdx) { + if let Some(Edge(src, dst, _)) = self.edges.get_mut(index) { + self.adjacencies[*src].outgoing_mut().remove(dst); + self.adjacencies[*dst].incoming_mut().remove(src); + std::mem::swap(src, dst); // *dst is now *src + self.adjacencies[*src].outgoing_mut().insert(*dst, index); + self.adjacencies[*dst].incoming_mut().insert(*src, index); + } + } + + type Ancestors<'n> = iters::NodesByIdx<'n, N, NodeIdx, DepthFirstSearch<'n, N, E, Self, Self::IncomingEdgesOf<'n>>> where Self: 'n; + fn ancestors(&self, index: NodeIdx) -> Self::Ancestors<'_> { + // use DFS here, Vec should be faster than VecDeque + DepthFirstSearch::custom_ref(self, index, |graph, node| graph.incoming_edges_of(node)) + } + + type AncestorsMut<'n> = iters::NodesByIdxMut<'n, N, NodeIdx, DepthFirstSearch<'n, N, E, Self, Self::IncomingEdgesOf<'n>>> where Self: 'n; + fn ancestors_mut(&mut self, index: NodeIdx) -> Self::AncestorsMut<'_> { + // use DFS here, Vec should be faster than VecDeque + DepthFirstSearch::custom_mut(self, index, |graph, node| graph.incoming_edges_of(node)) + } + + type Descendants<'n> = iters::NodesByIdx<'n, N, NodeIdx, DepthFirstSearch<'n, N, E, Self, Self::OutgoingEdgesOf<'n>>> where Self: 'n; + fn descendants(&self, index: NodeIdx) -> Self::Descendants<'_> { + // use DFS here, Vec should be faster than VecDeque + DepthFirstSearch::custom_ref(self, index, |graph, node| graph.outgoing_edges_of(node)) + } + + type DescendantsMut<'n> = iters::NodesByIdxMut<'n, N, NodeIdx, DepthFirstSearch<'n, N, E, Self, Self::OutgoingEdgesOf<'n>>> where Self: 'n; + fn descendants_mut(&mut self, index: NodeIdx) -> Self::DescendantsMut<'_> { + // use DFS here, Vec should be faster than VecDeque + DepthFirstSearch::custom_mut(self, index, |graph, node| graph.outgoing_edges_of(node)) + } +} diff --git a/crates/bevy_graph/src/graphs/mod.rs b/crates/bevy_graph/src/graphs/mod.rs new file mode 100644 index 0000000000000..003aa22e796ba --- /dev/null +++ b/crates/bevy_graph/src/graphs/mod.rs @@ -0,0 +1,445 @@ +/// `Vec` implementation of a graph +pub mod list; +/// `HashMap` implementation of a graph +pub mod map; + +/// Adjacency storage enum helper: `Directed` or `Undirected` +pub mod adjacency_storage; +/// An edge between nodes that store data of type `E`. +pub mod edge; +/// The `NodeIdx` and `EdgeIdx` structs +pub mod keys; + +use slotmap::HopSlotMap; + +use crate::{error::GraphError, utils::wrapped_indices_iterator::WrappedIndicesIterator}; + +use self::{ + edge::{Edge, EdgeMut, EdgeRef}, + keys::{EdgeIdx, NodeIdx}, +}; + +// NOTE: There should always be a common function and if needed a more precise function which the common function wraps. +// Example: `edges_between` is `trait Graph` general and has support for Simple- and Multigraphs. +// `edge_between` is only available for `SimpleGraph` but is also called from `edges_between`. + +/// A trait with all the common functions for a graph +pub trait Graph { + /// Iterator fix because TAIT not available + type NodeIndices<'n>: Iterator + where + Self: 'n, + E: 'n; + /// Iterator fix because TAIT not available + type Nodes<'n>: Iterator + where + Self: 'n, + N: 'n; + /// Iterator fix because TAIT not available + type NodesMut<'n>: Iterator + where + Self: 'n, + N: 'n; + /// Iterator fix because TAIT not available + type EdgeIndices<'e>: Iterator + where + Self: 'e, + E: 'e; + /// Iterator fix because TAIT not available + type Edges<'e>: Iterator> + where + Self: 'e, + E: 'e; + /// Iterator fix because TAIT not available + type EdgesMut<'e>: Iterator> + where + Self: 'e, + E: 'e; + /// Iterator fix because TAIT not available + type EdgesOf<'e>: Iterator> + WrappedIndicesIterator + where + Self: 'e, + E: 'e; + /// Iterator fix because TAIT not available + type EdgesOfMut<'e>: Iterator> + WrappedIndicesIterator + where + Self: 'e, + E: 'e; + /// Iterator fix because TAIT not available + type Neighbors<'n>: Iterator + WrappedIndicesIterator + where + Self: 'n, + N: 'n; + /// Iterator fix because TAIT not available + type NeighborsMut<'n>: Iterator + WrappedIndicesIterator + where + Self: 'n, + N: 'n; + /// Iterator fix because TAIT not available + type Isolated<'n>: Iterator + WrappedIndicesIterator + where + Self: 'n, + N: 'n; + /// Iterator fix because TAIT not available + type IsolatedMut<'n>: Iterator + WrappedIndicesIterator + where + Self: 'n, + N: 'n; + /// Iterator fix because TAIT not available + type IncomingEdgesOf<'e>: Iterator> + WrappedIndicesIterator + where + Self: 'e, + E: 'e; + /// Iterator fix because TAIT not available + type IncomingEdgesOfMut<'e>: Iterator> + WrappedIndicesIterator + where + Self: 'e, + E: 'e; + /// Iterator fix because TAIT not available + type OutgoingEdgesOf<'e>: Iterator> + WrappedIndicesIterator + where + Self: 'e, + E: 'e; + /// Iterator fix because TAIT not available + type OutgoingEdgesOfMut<'e>: Iterator> + WrappedIndicesIterator + where + Self: 'e, + E: 'e; + /// Iterator fix because TAIT not available + type InNeighbors<'n>: Iterator + WrappedIndicesIterator + where + Self: 'n, + N: 'n; + /// Iterator fix because TAIT not available + type InNeighborsMut<'n>: Iterator + WrappedIndicesIterator + where + Self: 'n, + N: 'n; + /// Iterator fix because TAIT not available + type OutNeighbors<'n>: Iterator + WrappedIndicesIterator + where + Self: 'n, + N: 'n; + /// Iterator fix because TAIT not available + type OutNeighborsMut<'n>: Iterator + WrappedIndicesIterator + where + Self: 'n, + N: 'n; + /// Iterator fix because TAIT not available + type Sources<'n>: Iterator + WrappedIndicesIterator + where + Self: 'n, + N: 'n; + /// Iterator fix because TAIT not available + type SourcesMut<'n>: Iterator + WrappedIndicesIterator + where + Self: 'n, + N: 'n; + /// Iterator fix because TAIT not available + type Sinks<'n>: Iterator + WrappedIndicesIterator + where + Self: 'n, + N: 'n; + /// Iterator fix because TAIT not available + type SinksMut<'n>: Iterator + WrappedIndicesIterator + where + Self: 'n, + N: 'n; + + /// Creates a new graph + fn new() -> Self + where + Self: Sized; + + /// Constructs a new, empty graph with the specified node and edge capacity. + /// The graph will be able to hold exactly `node_capacity` nodes and `edge_capacity` edges + /// elements without reallocating. + /// + /// If the capacites are zero, the graph will not allocate. + fn with_capacity(node_capacity: usize, edge_capacity: usize) -> Self; + + /// Returns the number of nodes and edges the graph can hold without reallocating. + fn capacity(&self) -> (usize, usize); + + /// Returns the number of nodes the graph can hold without reallocating. + fn node_capacity(&self) -> usize; + + /// Returns the number of edges the graph can hold without reallocating. + fn edge_capacity(&self) -> usize; + + /// Reserves capacity for at least `additional` more nodes to be inserted in the given `Self`. + /// The collection may reserve more space to avoid frequent reallocations. + /// After calling `reserve_nodes`, the node capacity will be greater than or equal to + /// `self.node_count() + additional`. Does nothing if capacity is already sufficient. + /// + /// # Panics + /// + /// Panics if the new capacity exceeds `isize::MAX` bytes. + fn reserve_nodes(&mut self, additional: usize); + + /// Reserves capacity for at least `additional` more edges to be inserted in the given `Self`. + /// The collection may reserve more space to avoid frequent reallocations. + /// After calling `reserve_edges`, the edge capacity will be greater than or equal to + /// `self.edge_count() + additional`. Does nothing if capacity is already sufficient. + /// + /// # Panics + /// + /// Panics if the new capacity exceeds `isize::MAX` bytes. + fn reserve_edges(&mut self, additional: usize); + + /// Returns `true` if the edges in the graph are directed. + fn is_directed(&self) -> bool; + + /// Returns `true` if the graph allows for more than one edge between a pair of nodes. + fn is_multigraph(&self) -> bool; + + /// Returns the number of nodes in the graph. + fn node_count(&self) -> usize; + + /// Returns the number of edges in the graph. + fn edge_count(&self) -> usize; + + /// Returns `true` if the graph has no nodes. + fn is_empty(&self) -> bool { + self.node_count() == 0 + } + + /// Adds a node with the associated `value` and returns its [`NodeIdx`]. + fn add_node(&mut self, value: N) -> NodeIdx; + + /// Adds an edge between the specified nodes with the associated `value`. + /// + /// # Returns + /// * `Ok`: `EdgeIdx` of the new edge + /// * `Err`: + /// * `GraphError::NodeNotFound(NodeIdx)`: the given `src` or `dst` isn't preset in the graph + /// * `GraphError::ContainsEdgeBetween`: there is already an edge between those nodes (not allowed in `SimpleGraph`) + /// * `GraphError::Loop`: the `src` and `dst` nodes are equal, the edge would be a loop (not allowed in `SimpleGraph`) + fn try_add_edge(&mut self, src: NodeIdx, dst: NodeIdx, value: E) + -> Result; + + /// Adds an edge between the specified nodes with the associated `value`. + /// + /// # Panics + /// + /// look at the `Returns/Err` in the docs from [`Graph::try_add_edge`] + #[inline] + fn add_edge(&mut self, src: NodeIdx, dst: NodeIdx, value: E) -> EdgeIdx { + self.try_add_edge(src, dst, value).unwrap() + } + + /// Returns `true` if the graph contains the `node`. + fn contains_node(&self, node: NodeIdx) -> bool; + + /// Returns `true` if an edge between the specified nodes exists. + /// + /// NOTE: When `src` or `dst` node isn't present, `false` will be returned. + fn contains_edge_between(&self, src: NodeIdx, dst: NodeIdx) -> bool; + + /// Removes the specified node from the graph, returning its value if it existed. + fn remove_node(&mut self, index: NodeIdx) -> Option; + + /// Removes the specified edge from the graph, returning its value if it existed. + fn remove_edge(&mut self, index: EdgeIdx) -> Option; + + /// Removes all edges from the graph. + fn clear_edges(&mut self); + + /// Removes all nodes and edges from the graph. + fn clear(&mut self); + + /// Returns a reference to the specified node. + fn get_node(&self, index: NodeIdx) -> Option<&N>; + + /// Returns a mutable reference to the specified node. + fn get_node_mut(&mut self, index: NodeIdx) -> Option<&mut N>; + + /// Returns the `NodeIdx` by a node value when it could be found in the graph. + fn find_node(&self, value: &N) -> Option + where + N: PartialEq; + + /// Returns a raw edge handle to the specified edge. + /// + /// # Safety + /// + /// This function should only be called when you really know what you are doing. + unsafe fn get_edge_raw(&mut self, index: EdgeIdx) -> Option<&mut Edge>; + + /// Returns a reference to the specified edge. + fn get_edge(&self, index: EdgeIdx) -> Option>; + + /// Returns a mutable reference to the specified edge. + fn get_edge_mut(&mut self, index: EdgeIdx) -> Option>; + + /// Returns the *first found* `EdgeIdx` by a edge value when it could be found in the graph. + fn find_edge(&self, value: &E) -> Option + where + E: PartialEq; + + /// Returns the number of edges connected to the specified node. + /// + /// In multi-graphs, edges that form self-loops add 2 to the degree. + fn degree(&self, index: NodeIdx) -> usize; + + /// Returns an iterator over all `NodeIdx`s. + fn node_indices(&self) -> Self::NodeIndices<'_>; + + /// Returns a immutable raw handle to the nodes slotmap. + /// + /// # Safety + /// + /// This function should only be called when you really know what you are doing. + unsafe fn nodes_raw(&self) -> &HopSlotMap; + + /// Returns an iterator over all nodes. + fn nodes(&self) -> Self::Nodes<'_>; + + /// Returns a mutable raw handle to the nodes slotmap. + /// + /// # Safety + /// + /// This function should only be called when you really know what you are doing. + unsafe fn nodes_mut_raw(&mut self) -> &mut HopSlotMap; + + /// Returns a mutable iterator over all nodes. + fn nodes_mut(&mut self) -> Self::NodesMut<'_>; + + /// Returns an iterator over all `EdgeIdx`s. + fn edge_indices(&self) -> Self::EdgeIndices<'_>; + + /// Returns a immutable raw handle to the edges slotmap. + /// + /// # Safety + /// + /// This function should only be called when you really know what you are doing. + unsafe fn edges_raw(&self) -> &HopSlotMap>; + + /// Returns an iterator over all edges. + fn edges(&self) -> Self::Edges<'_>; + + /// Returns a mutable raw handle to the edges slotmap. + /// + /// # Safety + /// + /// This function should only be called when you really know what you are doing. + unsafe fn edges_mut_raw(&mut self) -> &mut HopSlotMap>; + + /// Returns a mutable iterator over all edges. + fn edges_mut(&mut self) -> Self::EdgesMut<'_>; + + /// Returns an iterator over the edges of the specified node. + fn edges_of(&self, index: NodeIdx) -> Self::EdgesOf<'_>; + + /// Returns a mutable iterator over the edges of the specified node. + fn edges_of_mut(&mut self, index: NodeIdx) -> Self::EdgesOfMut<'_>; + + /// Returns an iterator over the nodes the share an edge with the specified node. + fn neighbors(&self, index: NodeIdx) -> Self::Neighbors<'_>; + + /// Returns a mutable iterator over the nodes the share an edge with the specified node. + fn neighbors_mut(&mut self, index: NodeIdx) -> Self::NeighborsMut<'_>; + + /// Returns an iterator over all nodes with zero degree. + fn isolated(&self) -> Self::Isolated<'_>; + + /// Returns a mutable iterator over all nodes with zero degree. + fn isolated_mut(&mut self) -> Self::IsolatedMut<'_>; + + /// Returns the number of edges going into the specified node. + fn in_degree(&self, index: NodeIdx) -> usize; + + /// Returns the number of edges coming out of the specified node. + fn out_degree(&self, index: NodeIdx) -> usize; + + /// Returns an iterator over the edge indices going into the specified node. + fn incoming_edges_of(&self, index: NodeIdx) -> Self::IncomingEdgesOf<'_>; + + /// Returns a mutable iterator over the edges going into the specified node. + fn incoming_edges_of_mut(&mut self, index: NodeIdx) -> Self::IncomingEdgesOfMut<'_>; + + /// Returns an iterator over the edge indices coming out of the specified node. + fn outgoing_edges_of(&self, index: NodeIdx) -> Self::OutgoingEdgesOf<'_>; + + /// Returns a mutable iterator over the edges coming out of the specified node. + fn outgoing_edges_of_mut(&mut self, index: NodeIdx) -> Self::OutgoingEdgesOfMut<'_>; + + /// Returns an iterator over the the specified node's direct predecessors. + fn in_neighbors(&self, index: NodeIdx) -> Self::InNeighbors<'_>; + + /// Returns a mutable iterator over the the specified node's direct predecessors. + fn in_neighbors_mut(&mut self, index: NodeIdx) -> Self::InNeighborsMut<'_>; + + /// Returns an iterator over the the specified node's direct successors. + fn out_neighbors(&self, index: NodeIdx) -> Self::OutNeighbors<'_>; + + /// Returns a mutable iterator over the the specified node's direct successors. + fn out_neighbors_mut(&mut self, index: NodeIdx) -> Self::OutNeighborsMut<'_>; + + /// Returns an iterator over all nodes with zero in-degree. + fn sources(&self) -> Self::Sources<'_>; + + /// Returns a mutable iterator over all nodes with zero in-degree. + fn sources_mut(&mut self) -> Self::SourcesMut<'_>; + + /// Returns an iterator over all nodes with zero out-degree. + fn sinks(&self) -> Self::Sinks<'_>; + + /// Returns a mutable iterator over all nodes with zero out-degree. + fn sinks_mut(&mut self) -> Self::SinksMut<'_>; +} + +/// A more precise trait with functions special for simple graphs +pub trait SimpleGraph: Graph { + /// Returns an edge between two nodes as `EdgeIdx` + fn edge_between(&self, from: NodeIdx, to: NodeIdx) -> Result, GraphError>; + + /// Returns an edge between two nodes as `EdgeIdx` + /// + /// # Safety + /// + /// This function should only be called when the nodes and the edge between exists. + unsafe fn edge_between_unchecked(&self, from: NodeIdx, to: NodeIdx) -> EdgeIdx; +} + +/// A more precise trait with functions special for directed graphs +pub trait DirectedGraph: Graph { + /// Iterator fix because TAIT not available + type Ancestors<'n>: Iterator + where + Self: 'n, + N: 'n; + /// Iterator fix because TAIT not available + type AncestorsMut<'n>: Iterator + where + Self: 'n, + N: 'n; + /// Iterator fix because TAIT not available + type Descendants<'n>: Iterator + where + Self: 'n, + N: 'n; + /// Iterator fix because TAIT not available + type DescendantsMut<'n>: Iterator + where + Self: 'n, + N: 'n; + + /// Reverse the direction of all edges in the graph. + fn reverse(&mut self); + + /// Reverse the direction of the specified edge. + fn reverse_edge(&mut self, index: EdgeIdx); + + /// Returns an iterator that visits all nodes that can reach the specified node. + fn ancestors(&self, index: NodeIdx) -> Self::Ancestors<'_>; + + /// Returns a mutable iterator that visits all nodes that can reach the specifed node. + fn ancestors_mut(&mut self, index: NodeIdx) -> Self::AncestorsMut<'_>; + + /// Returns iterator that visits all nodes that are reachable from the specified node. + fn descendants(&self, index: NodeIdx) -> Self::Descendants<'_>; + + /// Returns a mutable iterator that visits all nodes that are reachable from the specified node. + fn descendants_mut(&mut self, index: NodeIdx) -> Self::DescendantsMut<'_>; +} diff --git a/crates/bevy_graph/src/iters/edges_by_idx.rs b/crates/bevy_graph/src/iters/edges_by_idx.rs new file mode 100644 index 0000000000000..c3b44e772932c --- /dev/null +++ b/crates/bevy_graph/src/iters/edges_by_idx.rs @@ -0,0 +1,69 @@ +use std::borrow::Borrow; + +use slotmap::HopSlotMap; + +use crate::{ + graphs::{ + edge::{Edge, EdgeRef}, + keys::EdgeIdx, + Graph, + }, + utils::wrapped_indices_iterator::WrappedIndicesIterator, +}; + +/// An iterator which converts `(&)EdgeIdx` to a `EdgeRef` of the graph +pub struct EdgesByIdx<'g, E: 'g, B: Borrow, I: Iterator> { + edges: &'g HopSlotMap>, + inner: I, +} + +impl<'g, E: 'g, B: Borrow, I: Iterator> EdgesByIdx<'g, E, B, I> { + /// Creates a new `EdgesByIdx` iterator over a graph with the provided `inner` iterator + pub fn from_graph(inner: I, graph: &'g mut impl Graph) -> Self { + Self { + edges: unsafe { graph.edges_raw() }, + inner, + } + } + + /// Creates a new `EdgesByIdx` iterator over a graph with the provided `inner` iterator + pub fn new(inner: I, edges: &'g HopSlotMap>) -> Self { + Self { edges, inner } + } +} + +impl<'g, E: 'g, I: Iterator> WrappedIndicesIterator + for EdgesByIdx<'g, E, &'g EdgeIdx, I> +{ + type IndicesIter = std::iter::Cloned; + + #[inline] + fn into_indices(self) -> Self::IndicesIter { + self.inner.cloned() + } +} + +impl<'g, E: 'g, I: Iterator> WrappedIndicesIterator + for EdgesByIdx<'g, E, EdgeIdx, I> +{ + type IndicesIter = I; + + #[inline] + fn into_indices(self) -> Self::IndicesIter { + self.inner + } +} + +impl<'g, E: 'g, B: Borrow, I: Iterator> Iterator for EdgesByIdx<'g, E, B, I> { + type Item = EdgeRef<'g, E>; + + fn next(&mut self) -> Option { + if let Some(index) = self.inner.next() { + self.edges + .get(*index.borrow()) + .map(|edge| edge.as_ref_edge()) + } else { + None + } + } +} diff --git a/crates/bevy_graph/src/iters/edges_by_idx_mut.rs b/crates/bevy_graph/src/iters/edges_by_idx_mut.rs new file mode 100644 index 0000000000000..d6d806b54027a --- /dev/null +++ b/crates/bevy_graph/src/iters/edges_by_idx_mut.rs @@ -0,0 +1,74 @@ +use std::borrow::Borrow; + +use slotmap::HopSlotMap; + +use crate::{ + graphs::{ + edge::{Edge, EdgeMut}, + keys::EdgeIdx, + Graph, + }, + utils::wrapped_indices_iterator::WrappedIndicesIterator, +}; + +/// An iterator which converts `(&)EdgeIdx` to a `EdgeMut` of the graph +pub struct EdgesByIdxMut<'g, E: 'g, B: Borrow, I: Iterator> { + edges: &'g mut HopSlotMap>, + inner: I, +} + +impl<'g, E: 'g, B: Borrow, I: Iterator> EdgesByIdxMut<'g, E, B, I> { + /// Creates a new `EdgesByIdxMut` iterator over a graph with the provided `inner` iterator + pub fn from_graph(inner: I, graph: &'g mut impl Graph) -> Self { + Self { + edges: unsafe { graph.edges_mut_raw() }, + inner, + } + } + + /// Creates a new `EdgesByIdxMut` iterator over a graph with the provided `inner` iterator + pub fn new(inner: I, edges: &'g mut HopSlotMap>) -> Self { + Self { edges, inner } + } +} + +impl<'g, E: 'g, I: Iterator> WrappedIndicesIterator + for EdgesByIdxMut<'g, E, &'g EdgeIdx, I> +{ + type IndicesIter = std::iter::Cloned; + + #[inline] + fn into_indices(self) -> Self::IndicesIter { + self.inner.cloned() + } +} + +impl<'g, E: 'g, I: Iterator> WrappedIndicesIterator + for EdgesByIdxMut<'g, E, EdgeIdx, I> +{ + type IndicesIter = I; + + #[inline] + fn into_indices(self) -> Self::IndicesIter { + self.inner + } +} + +impl<'g, E: 'g, B: Borrow, I: Iterator> Iterator for EdgesByIdxMut<'g, E, B, I> { + type Item = EdgeMut<'g, E>; + + fn next(&mut self) -> Option { + if let Some(index) = self.inner.next() { + // Unsafe necessary because Rust can't deduce that we won't + // return multiple references to the same value. + unsafe { + self.edges.get_mut(*index.borrow()).map(|edge| { + let ptr: *mut E = &mut edge.2; + EdgeMut(edge.0, edge.1, &mut *ptr) + }) + } + } else { + None + } + } +} diff --git a/crates/bevy_graph/src/iters/edges_mut.rs b/crates/bevy_graph/src/iters/edges_mut.rs new file mode 100644 index 0000000000000..2980a456d3c6f --- /dev/null +++ b/crates/bevy_graph/src/iters/edges_mut.rs @@ -0,0 +1,27 @@ +use std::marker::PhantomData; + +use crate::graphs::edge::{Edge, EdgeMut}; + +/// An iterator which converts `&'g mut Edge` to a `EdgeMut<'g, E>` +pub struct EdgesMut<'g, E: 'g, I: Iterator>> { + inner: I, + phantom: PhantomData, +} + +impl<'g, E: 'g, I: Iterator>> EdgesMut<'g, E, I> { + /// Creates a new `EdgesMut` iterator with the provided `inner` iterator + pub fn new(inner: I) -> Self { + Self { + inner, + phantom: PhantomData, + } + } +} + +impl<'g, E: 'g, I: Iterator>> Iterator for EdgesMut<'g, E, I> { + type Item = EdgeMut<'g, E>; + + fn next(&mut self) -> Option { + self.inner.next().map(|edge| edge.as_mut_edge()) + } +} diff --git a/crates/bevy_graph/src/iters/edges_ref.rs b/crates/bevy_graph/src/iters/edges_ref.rs new file mode 100644 index 0000000000000..49df915fa68d4 --- /dev/null +++ b/crates/bevy_graph/src/iters/edges_ref.rs @@ -0,0 +1,27 @@ +use std::marker::PhantomData; + +use crate::graphs::edge::{Edge, EdgeRef}; + +/// An iterator which converts `&'g Edge` to a `EdgeRef<'g, E>` +pub struct EdgesRef<'g, E: 'g, I: Iterator>> { + inner: I, + phantom: PhantomData, +} + +impl<'g, E: 'g, I: Iterator>> EdgesRef<'g, E, I> { + /// Creates a new `EdgesRef` iterator with the provided `inner` iterator + pub fn new(inner: I) -> Self { + Self { + inner, + phantom: PhantomData, + } + } +} + +impl<'g, E: 'g, I: Iterator>> Iterator for EdgesRef<'g, E, I> { + type Item = EdgeRef<'g, E>; + + fn next(&mut self) -> Option { + self.inner.next().map(|edge| edge.as_ref_edge()) + } +} diff --git a/crates/bevy_graph/src/iters/isolated.rs b/crates/bevy_graph/src/iters/isolated.rs new file mode 100644 index 0000000000000..042d76dcd78e1 --- /dev/null +++ b/crates/bevy_graph/src/iters/isolated.rs @@ -0,0 +1,55 @@ +use std::marker::PhantomData; + +use crate::{graphs::keys::NodeIdx, utils::wrapped_indices_iterator::WrappedIndicesIterator}; + +/// An iterator which filters out every non-isolated node of a sub-iterator +pub struct Isolated> { + inner: InnerIsolated, + phantom: PhantomData, +} + +impl> Isolated { + /// Creates a new `Isolated` iterator + pub fn new(inner: I) -> Self { + Self { + inner: InnerIsolated { + inner, + phantom: PhantomData, + }, + phantom: PhantomData, + } + } +} + +impl> WrappedIndicesIterator + for Isolated +{ + type IndicesIter = std::iter::Map, fn(((NodeIdx, T), usize)) -> NodeIdx>; + + #[inline] + fn into_indices(self) -> Self::IndicesIter { + self.inner.map(|((index, _), _)| index) + } +} + +impl> Iterator for Isolated { + type Item = T; + + fn next(&mut self) -> Option { + self.inner.next().map(|((_, node), _)| node) + } +} + +/// An iterator which filters out every non-isolated node of a sub-iterator +pub struct InnerIsolated> { + inner: I, + phantom: PhantomData, +} + +impl> Iterator for InnerIsolated { + type Item = ((NodeIdx, T), usize); + + fn next(&mut self) -> Option { + self.inner.find(|(_, in_out_degree)| *in_out_degree == 0) + } +} diff --git a/crates/bevy_graph/src/iters/loop_safety_iter.rs b/crates/bevy_graph/src/iters/loop_safety_iter.rs new file mode 100644 index 0000000000000..4a0693e190e1d --- /dev/null +++ b/crates/bevy_graph/src/iters/loop_safety_iter.rs @@ -0,0 +1,55 @@ +use std::borrow::Borrow; + +use hashbrown::HashSet; +use slotmap::HopSlotMap; + +use crate::graphs::{edge::Edge, keys::EdgeIdx, Graph}; + +/// Iterator over `(&)EdgeIdx` which guarantees that loops will only come once +pub struct LoopSafetyIter<'g, E: 'g, B: Borrow, I: Iterator> { + edges: &'g HopSlotMap>, + loops: HashSet, + inner: I, +} + +impl<'g, E: 'g, B: Borrow, I: Iterator> LoopSafetyIter<'g, E, B, I> { + /// Creates a new `LoopSafetyIter` iterator over a graph with the provided `inner` iterator + pub fn from_graph(inner: I, graph: &'g mut impl Graph) -> Self { + Self { + edges: unsafe { graph.edges_raw() }, + loops: HashSet::new(), + inner, + } + } + + /// Creates a new `LoopSafetyIter` iterator over a graph with the provided `inner` iterator + pub fn new(inner: I, edges: &'g HopSlotMap>) -> Self { + Self { + edges, + loops: HashSet::new(), + inner, + } + } +} + +impl<'g, E: 'g, B: Borrow, I: Iterator> Iterator + for LoopSafetyIter<'g, E, B, I> +{ + type Item = B; + + fn next(&mut self) -> Option { + if let Some(index) = self.inner.next() { + let edge_idx = *index.borrow(); + let Edge(src, dst, _) = unsafe { self.edges.get_unchecked(edge_idx) }; + if src == dst { + if self.loops.contains(&edge_idx) { + return self.next(); + } + self.loops.insert(edge_idx); + } + Some(index) + } else { + None + } + } +} diff --git a/crates/bevy_graph/src/iters/mod.rs b/crates/bevy_graph/src/iters/mod.rs new file mode 100644 index 0000000000000..4bb0fc42c1965 --- /dev/null +++ b/crates/bevy_graph/src/iters/mod.rs @@ -0,0 +1,41 @@ +mod edges_by_idx; +pub use edges_by_idx::*; + +mod edges_by_idx_mut; +pub use edges_by_idx_mut::*; + +mod nodes_by_idx; +pub use nodes_by_idx::*; + +mod nodes_by_idx_mut; +pub use nodes_by_idx_mut::*; + +mod edges_ref; +pub use edges_ref::*; + +mod edges_mut; +pub use edges_mut::*; + +mod zip_degree; +pub use zip_degree::*; + +mod zip_in_degree; +pub use zip_in_degree::*; + +mod zip_out_degree; +pub use zip_out_degree::*; + +mod isolated; +pub use isolated::*; + +mod skip_index_iter; +pub use skip_index_iter::*; + +mod loop_safety_iter; +pub use loop_safety_iter::*; + +mod tuple_extract; +pub use tuple_extract::*; + +mod node_just_once_iter; +pub use node_just_once_iter::*; diff --git a/crates/bevy_graph/src/iters/node_just_once_iter.rs b/crates/bevy_graph/src/iters/node_just_once_iter.rs new file mode 100644 index 0000000000000..6407cb8e46fe4 --- /dev/null +++ b/crates/bevy_graph/src/iters/node_just_once_iter.rs @@ -0,0 +1,38 @@ +use std::borrow::Borrow; + +use hashbrown::HashSet; + +use crate::graphs::keys::NodeIdx; + +/// Iterator over `(&)NodeIdx` which guarantees that a value will only come once +pub struct NodeJustOnceIter, I: Iterator> { + duplicates: HashSet, + inner: I, +} + +impl, I: Iterator> NodeJustOnceIter { + /// Creates a new `NodeJustOnceIter` iterator over a graph with the provided `inner` iterator + pub fn new(inner: I) -> Self { + Self { + duplicates: HashSet::new(), + inner, + } + } +} + +impl, I: Iterator> Iterator for NodeJustOnceIter { + type Item = B; + + fn next(&mut self) -> Option { + if let Some(index) = self.inner.next() { + let node_idx = *index.borrow(); + if self.duplicates.contains(&node_idx) { + return self.next(); + } + self.duplicates.insert(node_idx); + Some(index) + } else { + None + } + } +} diff --git a/crates/bevy_graph/src/iters/nodes_by_idx.rs b/crates/bevy_graph/src/iters/nodes_by_idx.rs new file mode 100644 index 0000000000000..c1913d9bffbad --- /dev/null +++ b/crates/bevy_graph/src/iters/nodes_by_idx.rs @@ -0,0 +1,63 @@ +use std::borrow::Borrow; + +use slotmap::HopSlotMap; + +use crate::{ + graphs::{keys::NodeIdx, Graph}, + utils::wrapped_indices_iterator::WrappedIndicesIterator, +}; + +/// An iterator which converts `(&)NodeIdx` to a `&'g N` of the graph +pub struct NodesByIdx<'g, N: 'g, B: Borrow, I: Iterator> { + nodes: &'g HopSlotMap, + inner: I, +} + +impl<'g, N: 'g, B: Borrow, I: Iterator> NodesByIdx<'g, N, B, I> { + /// Creates a new `NodesByIdx` iterator over a graph with the provided `inner` iterator + pub fn from_graph(inner: I, graph: &'g impl Graph) -> Self { + Self { + nodes: unsafe { graph.nodes_raw() }, + inner, + } + } + + /// Creates a new `NodesByIdx` iterator over a graph with the provided `inner` iterator + pub fn new(inner: I, nodes: &'g HopSlotMap) -> Self { + Self { nodes, inner } + } +} + +impl<'g, N: 'g, I: Iterator> WrappedIndicesIterator + for NodesByIdx<'g, N, &'g NodeIdx, I> +{ + type IndicesIter = std::iter::Cloned; + + #[inline] + fn into_indices(self) -> Self::IndicesIter { + self.inner.cloned() + } +} + +impl<'g, N: 'g, I: Iterator> WrappedIndicesIterator + for NodesByIdx<'g, N, NodeIdx, I> +{ + type IndicesIter = I; + + #[inline] + fn into_indices(self) -> Self::IndicesIter { + self.inner + } +} + +impl<'g, N: 'g, B: Borrow, I: Iterator> Iterator for NodesByIdx<'g, N, B, I> { + type Item = &'g N; + + fn next(&mut self) -> Option { + if let Some(index) = self.inner.next() { + self.nodes.get(*index.borrow()) + } else { + None + } + } +} diff --git a/crates/bevy_graph/src/iters/nodes_by_idx_mut.rs b/crates/bevy_graph/src/iters/nodes_by_idx_mut.rs new file mode 100644 index 0000000000000..1e58664a8b7f3 --- /dev/null +++ b/crates/bevy_graph/src/iters/nodes_by_idx_mut.rs @@ -0,0 +1,70 @@ +use std::borrow::Borrow; + +use slotmap::HopSlotMap; + +use crate::{ + graphs::{keys::NodeIdx, Graph}, + utils::wrapped_indices_iterator::WrappedIndicesIterator, +}; + +/// An iterator which converts `(&)NodeIdx` to a `&'g mut N` of the graph +pub struct NodesByIdxMut<'g, N: 'g, B: Borrow, I: Iterator> { + nodes: &'g mut HopSlotMap, + inner: I, +} + +impl<'g, N: 'g, B: Borrow, I: Iterator> NodesByIdxMut<'g, N, B, I> { + /// Creates a new `NodesByIdxMut` iterator over a graph with the provided `inner` iterator + pub fn from_graph(inner: I, graph: &'g mut impl Graph) -> Self { + Self { + nodes: unsafe { graph.nodes_mut_raw() }, + inner, + } + } + + /// Creates a new `NodesByIdxMut` iterator over a graph with the provided `inner` iterator + pub fn new(inner: I, nodes: &'g mut HopSlotMap) -> Self { + Self { nodes, inner } + } +} + +impl<'g, N: 'g, I: Iterator> WrappedIndicesIterator + for NodesByIdxMut<'g, N, &'g NodeIdx, I> +{ + type IndicesIter = std::iter::Cloned; + + #[inline] + fn into_indices(self) -> Self::IndicesIter { + self.inner.cloned() + } +} + +impl<'g, N: 'g, I: Iterator> WrappedIndicesIterator + for NodesByIdxMut<'g, N, NodeIdx, I> +{ + type IndicesIter = I; + + #[inline] + fn into_indices(self) -> Self::IndicesIter { + self.inner + } +} + +impl<'g, N: 'g, B: Borrow, I: Iterator> Iterator for NodesByIdxMut<'g, N, B, I> { + type Item = &'g mut N; + + fn next(&mut self) -> Option { + if let Some(index) = self.inner.next() { + // Unsafe necessary because Rust can't deduce that we won't + // return multiple references to the same value. + unsafe { + self.nodes.get_mut(*index.borrow()).map(|node| { + let ptr: *mut N = &mut *node; + &mut *ptr + }) + } + } else { + None + } + } +} diff --git a/crates/bevy_graph/src/iters/skip_index_iter.rs b/crates/bevy_graph/src/iters/skip_index_iter.rs new file mode 100644 index 0000000000000..15ba68d3bf384 --- /dev/null +++ b/crates/bevy_graph/src/iters/skip_index_iter.rs @@ -0,0 +1,33 @@ +use crate::graphs::keys::NodeIdx; + +/// Iterator which guarantees that loops will only come once by skipping itself in adjacencies +pub struct SkipIndexIter<'g, V: 'g, I: Iterator> { + index_to_skip: NodeIdx, + inner: I, +} + +impl<'g, V: 'g, I: Iterator> SkipIndexIter<'g, V, I> { + /// Creates a new `LoopSafetyIter` iterator with the provided `inner` iterator and the `NodeIdx` it should skip + pub fn new(inner: I, index_to_skip: NodeIdx) -> Self { + Self { + inner, + index_to_skip, + } + } +} + +impl<'g, V: 'g, I: Iterator> Iterator for SkipIndexIter<'g, V, I> { + type Item = (&'g NodeIdx, V); + + fn next(&mut self) -> Option { + if let Some(i) = self.inner.next() { + if i.0 == &self.index_to_skip { + self.next() + } else { + Some(i) + } + } else { + None + } + } +} diff --git a/crates/bevy_graph/src/iters/tuple_extract.rs b/crates/bevy_graph/src/iters/tuple_extract.rs new file mode 100644 index 0000000000000..1f451e1e756ac --- /dev/null +++ b/crates/bevy_graph/src/iters/tuple_extract.rs @@ -0,0 +1,36 @@ +/// Iterator which iterates the `FIRST` or second entry in a tuple +pub struct TupleExtract, const FIRST: bool> { + inner: I, +} + +impl> TupleExtract { + /// Creates a `TupleExtract` iterator which extracts the *first* entry + pub fn new_first(inner: I) -> Self { + Self { inner } + } +} + +impl> TupleExtract { + /// Creates a `TupleExtract` iterator which extracts the *second* entry + pub fn new_second(inner: I) -> Self { + Self { inner } + } +} + +impl> Iterator for TupleExtract { + type Item = F; + + #[inline] + fn next(&mut self) -> Option { + self.inner.next().map(|(f, _s)| f) + } +} + +impl> Iterator for TupleExtract { + type Item = S; + + #[inline] + fn next(&mut self) -> Option { + self.inner.next().map(|(_f, s)| s) + } +} diff --git a/crates/bevy_graph/src/iters/zip_degree.rs b/crates/bevy_graph/src/iters/zip_degree.rs new file mode 100644 index 0000000000000..44c6434ea1822 --- /dev/null +++ b/crates/bevy_graph/src/iters/zip_degree.rs @@ -0,0 +1,53 @@ +use hashbrown::HashMap; +use slotmap::SecondaryMap; + +use crate::graphs::{adjacency_storage::AdjacencyStorage, keys::NodeIdx}; + +/// An iterator which zips the `degree` of a `NodeIdx` with it +pub struct ZipDegree<'g, S, N, I: Iterator, const DIRECTED: bool> { + adjacencies: &'g SecondaryMap>, + inner: I, +} + +impl<'g, S, N, I: Iterator, const DIRECTED: bool> + ZipDegree<'g, S, N, I, DIRECTED> +{ + /// Creates a new `ZipDegree` iterator with the provided `inner` iterator + pub fn new(inner: I, adjacencies: &'g SecondaryMap>) -> Self { + Self { adjacencies, inner } + } +} + +impl<'g, T, N, I: Iterator, const DIRECTED: bool> Iterator + for ZipDegree<'g, Vec, N, I, DIRECTED> +{ + type Item = ((NodeIdx, N), usize); + + fn next(&mut self) -> Option { + self.inner.next().map(|(index, node)| { + let degree = if DIRECTED { + self.adjacencies[index].incoming().len() + self.adjacencies[index].outgoing().len() + } else { + self.adjacencies[index].incoming().len() + }; + ((index, node), degree) + }) + } +} + +impl<'g, K, V, N, I: Iterator, const DIRECTED: bool> Iterator + for ZipDegree<'g, HashMap, N, I, DIRECTED> +{ + type Item = ((NodeIdx, N), usize); + + fn next(&mut self) -> Option { + self.inner.next().map(|(index, node)| { + let degree = if DIRECTED { + self.adjacencies[index].incoming().len() + self.adjacencies[index].outgoing().len() + } else { + self.adjacencies[index].incoming().len() + }; + ((index, node), degree) + }) + } +} diff --git a/crates/bevy_graph/src/iters/zip_in_degree.rs b/crates/bevy_graph/src/iters/zip_in_degree.rs new file mode 100644 index 0000000000000..9ff544c17ac26 --- /dev/null +++ b/crates/bevy_graph/src/iters/zip_in_degree.rs @@ -0,0 +1,41 @@ +use hashbrown::HashMap; +use slotmap::SecondaryMap; + +use crate::graphs::{adjacency_storage::AdjacencyStorage, keys::NodeIdx}; + +/// An iterator which zips the `in_degree` of a `NodeIdx` with it +pub struct ZipInDegree<'g, S, N, I: Iterator> { + adjacencies: &'g SecondaryMap>, + inner: I, +} + +impl<'g, S, N, I: Iterator> ZipInDegree<'g, S, N, I> { + /// Creates a new `ZipInDegree` iterator with the provided `inner` iterator + pub fn new(inner: I, adjacencies: &'g SecondaryMap>) -> Self { + Self { adjacencies, inner } + } +} + +impl<'g, T, N, I: Iterator> Iterator for ZipInDegree<'g, Vec, N, I> { + type Item = ((NodeIdx, N), usize); + + fn next(&mut self) -> Option { + self.inner.next().map(|(index, node)| { + let in_degree = self.adjacencies[index].incoming().len(); + ((index, node), in_degree) + }) + } +} + +impl<'g, K, V, N, I: Iterator> Iterator + for ZipInDegree<'g, HashMap, N, I> +{ + type Item = ((NodeIdx, N), usize); + + fn next(&mut self) -> Option { + self.inner.next().map(|(index, node)| { + let in_degree = self.adjacencies[index].incoming().len(); + ((index, node), in_degree) + }) + } +} diff --git a/crates/bevy_graph/src/iters/zip_out_degree.rs b/crates/bevy_graph/src/iters/zip_out_degree.rs new file mode 100644 index 0000000000000..6f64d7d91f5d6 --- /dev/null +++ b/crates/bevy_graph/src/iters/zip_out_degree.rs @@ -0,0 +1,41 @@ +use hashbrown::HashMap; +use slotmap::SecondaryMap; + +use crate::graphs::{adjacency_storage::AdjacencyStorage, keys::NodeIdx}; + +/// An iterator which zips the `out_degree` of a `NodeIdx` with it +pub struct ZipOutDegree<'g, S, N, I: Iterator> { + adjacencies: &'g SecondaryMap>, + inner: I, +} + +impl<'g, S, N, I: Iterator> ZipOutDegree<'g, S, N, I> { + /// Creates a new `ZipOutDegree` iterator with the provided `inner` iterator + pub fn new(inner: I, adjacencies: &'g SecondaryMap>) -> Self { + Self { adjacencies, inner } + } +} + +impl<'g, T, N, I: Iterator> Iterator for ZipOutDegree<'g, Vec, N, I> { + type Item = ((NodeIdx, N), usize); + + fn next(&mut self) -> Option { + self.inner.next().map(|(index, node)| { + let in_degree = self.adjacencies[index].outgoing().len(); + ((index, node), in_degree) + }) + } +} + +impl<'g, K, V, N, I: Iterator> Iterator + for ZipOutDegree<'g, HashMap, N, I> +{ + type Item = ((NodeIdx, N), usize); + + fn next(&mut self) -> Option { + self.inner.next().map(|(index, node)| { + let in_degree = self.adjacencies[index].outgoing().len(); + ((index, node), in_degree) + }) + } +} diff --git a/crates/bevy_graph/src/lib.rs b/crates/bevy_graph/src/lib.rs new file mode 100644 index 0000000000000..e7bba897d0ed7 --- /dev/null +++ b/crates/bevy_graph/src/lib.rs @@ -0,0 +1,13 @@ +#![warn(missing_docs)] +//! Graph data structures, as used by the Bevy game engine + +/// All implemented algorithms for graphs +pub mod algos; +/// All errors that can occur when executing a graph operation +pub mod error; +/// The `Graph` trait and all graph implementations +pub mod graphs; +/// Helping `Iterator`s for graphs +pub mod iters; +/// Utils used by graphs +pub mod utils; diff --git a/crates/bevy_graph/src/utils/iter_choice.rs b/crates/bevy_graph/src/utils/iter_choice.rs new file mode 100644 index 0000000000000..7a8c94ef0aad4 --- /dev/null +++ b/crates/bevy_graph/src/utils/iter_choice.rs @@ -0,0 +1,33 @@ +/// Iterator type holding chosen iterator +pub enum IterChoice, J: Iterator> { + /// The first iter possibility + First(I), + /// The second iter possibility + Second(J), +} + +impl, J: Iterator> IterChoice { + /// Returns an [`IterChoice`] with the first possibility + #[inline] + pub fn new_first(iter: I) -> Self { + Self::First(iter) + } + + /// Returns an [`IterChoice`] with the second possibility + #[inline] + pub fn new_second(iter: J) -> Self { + Self::Second(iter) + } +} + +impl, J: Iterator> Iterator for IterChoice { + type Item = T; + + #[inline] + fn next(&mut self) -> Option { + match self { + IterChoice::First(iter) => iter.next(), + IterChoice::Second(iter) => iter.next(), + } + } +} diff --git a/crates/bevy_graph/src/utils/mod.rs b/crates/bevy_graph/src/utils/mod.rs new file mode 100644 index 0000000000000..31adcdf161c06 --- /dev/null +++ b/crates/bevy_graph/src/utils/mod.rs @@ -0,0 +1,8 @@ +/// Iterator type holding chosen iterator +pub mod iter_choice; +/// Map-like methods for `Vec<(K, V)>` +pub mod vecmap; +/// Set-like methods for `Vec` +pub mod vecset; +/// Trait with helping function for yielding the index instead of the value. +pub mod wrapped_indices_iterator; diff --git a/crates/bevy_graph/src/utils/vecmap.rs b/crates/bevy_graph/src/utils/vecmap.rs new file mode 100644 index 0000000000000..f877d5f141c47 --- /dev/null +++ b/crates/bevy_graph/src/utils/vecmap.rs @@ -0,0 +1,150 @@ +use core::slice; + +/// Map-like methods for `Vec<(K, V)>` +pub trait VecMap { + /// Gets an immutable reference to value by key + fn get_value(&self, key: K) -> Option<&V>; + + /// Gets a mutable reference to value by key + fn get_value_mut(&mut self, key: K) -> Option<&mut V>; + + /// Gets an immutable reference to value by key and inserts by closure when it's not preset + fn get_value_or(&mut self, key: K, or: fn() -> V) -> &V; + + /// Gets an immutable reference to value by key and inserts by closure when it's not preset + fn get_value_or_mut(&mut self, key: K, or: fn() -> V) -> &mut V; + + /// Gets an immutable reference to value by key and inserts the default when it's not preset + fn get_value_or_default(&mut self, key: K) -> &V + where + V: Default, + { + self.get_value_or(key, Default::default) + } + + /// Gets a mutable reference to value by key and inserts the default when it's not preset + fn get_value_or_default_mut(&mut self, key: K) -> &mut V + where + V: Default, + { + self.get_value_or_mut(key, Default::default) + } + + /// Returns `true` if the given key is preset + fn contains_key(&self, key: K) -> bool; + + /// Gets the index by the key + fn index_by_key(&self, key: &K) -> Option; + + /// Removes the entry by the key + fn remove_by_key(&mut self, key: K) -> Option; + + /// Returns an iterator over the entries + fn tuple_iter(&self) -> TupleIter; + + /// Returns an iterator over the keys + fn keys(&self) -> Keys; + + /// Returns an iterator over the values + fn values(&self) -> Values; +} + +impl VecMap for Vec<(K, V)> { + fn get_value(&self, key: K) -> Option<&V> { + if let Some(index) = self.index_by_key(&key) { + unsafe { Some(&self.get_unchecked(index).1) } + } else { + None + } + } + + fn get_value_mut(&mut self, key: K) -> Option<&mut V> { + if let Some(index) = self.index_by_key(&key) { + unsafe { Some(&mut self.get_unchecked_mut(index).1) } + } else { + None + } + } + + fn get_value_or(&mut self, key: K, or: fn() -> V) -> &V { + if let Some(index) = self.index_by_key(&key) { + unsafe { &self.get_unchecked(index).1 } + } else { + self.push((key, or())); + unsafe { &self.last().unwrap_unchecked().1 } + } + } + + fn get_value_or_mut(&mut self, key: K, or: fn() -> V) -> &mut V { + if let Some(index) = self.index_by_key(&key) { + unsafe { &mut self.get_unchecked_mut(index).1 } + } else { + self.push((key, or())); + unsafe { &mut self.last_mut().unwrap_unchecked().1 } + } + } + + fn contains_key(&self, key: K) -> bool { + self.iter().any(|l| l.0 == key) + } + + #[inline] + fn index_by_key(&self, key: &K) -> Option { + self.iter().position(|l| l.0.eq(key)) + } + + fn remove_by_key(&mut self, key: K) -> Option { + self.index_by_key(&key).map(|index| self.remove(index).1) + } + + fn tuple_iter(&self) -> TupleIter { + TupleIter { inner: self.iter() } + } + + fn keys(&self) -> Keys { + Keys { inner: self.iter() } + } + + fn values(&self) -> Values { + Values { inner: self.iter() } + } +} + +/// Iterator over all keys in a `VecMap` +pub struct Keys<'s, K: PartialEq, V> { + inner: slice::Iter<'s, (K, V)>, +} + +impl<'s, K: PartialEq, V> Iterator for Keys<'s, K, V> { + type Item = &'s K; + + fn next(&mut self) -> Option { + self.inner.next().map(|l| &l.0) + } +} + +/// Iterator over all values in a `VecMap` +pub struct Values<'s, K: PartialEq, V> { + inner: slice::Iter<'s, (K, V)>, +} + +impl<'s, K: PartialEq, V> Iterator for Values<'s, K, V> { + type Item = &'s V; + + fn next(&mut self) -> Option { + self.inner.next().map(|l| &l.1) + } +} + +/// Iterator over all entries in a `VecMap` in an entry-like tuple +pub struct TupleIter<'s, K: PartialEq, V> { + inner: slice::Iter<'s, (K, V)>, +} + +impl<'s, K: PartialEq, V> Iterator for TupleIter<'s, K, V> { + type Item = (&'s K, &'s V); + + fn next(&mut self) -> Option { + self.inner.next().map(|l| (&l.0, &l.1)) + } +} diff --git a/crates/bevy_graph/src/utils/vecset.rs b/crates/bevy_graph/src/utils/vecset.rs new file mode 100644 index 0000000000000..f079202e07b27 --- /dev/null +++ b/crates/bevy_graph/src/utils/vecset.rs @@ -0,0 +1,17 @@ +/// Set-like methods for `Vec` +pub trait VecSet { + /// Gets a index by value + fn index_by_value(&self, value: &T) -> Option; + /// Removes an entry by value + fn remove_by_value(&mut self, value: &T) -> Option; +} + +impl VecSet for Vec { + fn index_by_value(&self, value: &T) -> Option { + self.iter().position(|l| l == value) + } + + fn remove_by_value(&mut self, value: &T) -> Option { + self.index_by_value(value).map(|index| self.remove(index)) + } +} diff --git a/crates/bevy_graph/src/utils/wrapped_indices_iterator.rs b/crates/bevy_graph/src/utils/wrapped_indices_iterator.rs new file mode 100644 index 0000000000000..0eb46f9cba751 --- /dev/null +++ b/crates/bevy_graph/src/utils/wrapped_indices_iterator.rs @@ -0,0 +1,8 @@ +/// Trait with helping function for yielding the index instead of the value. +pub trait WrappedIndicesIterator: Iterator { + /// The indices iterator type. + type IndicesIter: Iterator; + + /// Returns an iterator which yields the index instead of the value. + fn into_indices(self) -> Self::IndicesIter; +} diff --git a/crates/bevy_graph/tests/multi_list.rs b/crates/bevy_graph/tests/multi_list.rs new file mode 100644 index 0000000000000..88296e04f4124 --- /dev/null +++ b/crates/bevy_graph/tests/multi_list.rs @@ -0,0 +1,302 @@ +use bevy_graph::{ + graphs::{ + keys::{EdgeIdx, NodeIdx}, + list::MultiListGraph, + DirectedGraph, Graph, + }, + utils::wrapped_indices_iterator::WrappedIndicesIterator, +}; +use hashbrown::HashSet; + +#[test] +fn undirected() { + let mut graph = MultiListGraph::<&str, i32, false>::new(); + + assert!(!graph.is_directed()); + assert!(graph.is_multigraph()); + + assert_eq!(graph.node_count(), 0); + let jakob = graph.add_node("Jakob"); + let edgar = graph.add_node("Edgar"); + let bernhard = graph.add_node("Bernhard"); + let no_friends_manny = graph.add_node("No Friends Manny"); + let ego = graph.add_node("Ego"); + assert_eq!(graph.node_count(), 5); + + assert!(graph.contains_node(jakob)); + assert!(graph.contains_node(edgar)); + assert!(graph.contains_node(bernhard)); + assert!(graph.contains_node(no_friends_manny)); + assert!(graph.contains_node(ego)); + + assert_eq!(graph.find_node(&"Edgar"), Some(edgar)); + assert_eq!(graph.find_node(&"NoIReallyDon'tExist"), None); + + assert_eq!( + graph.node_indices().collect::>(), + [jakob, edgar, bernhard, no_friends_manny, ego].into() + ); + + assert_eq!(graph.edge_count(), 0); + let je = graph.add_edge(jakob, edgar, 12); + let je2 = graph.add_edge(jakob, edgar, 2); + let eb = graph.add_edge(edgar, bernhard, 7); + let ego_self = graph.add_edge(ego, ego, 3); + assert_eq!(graph.edge_count(), 4); + + assert!(graph.contains_edge_between(jakob, edgar)); + assert!(graph.contains_edge_between(edgar, jakob)); + assert!(!graph.contains_edge_between(jakob, bernhard)); + assert!(graph.contains_edge_between(ego, ego)); + + assert_eq!(graph.find_edge(&12), Some(je)); + assert_eq!(graph.find_edge(&2), Some(je2)); + assert_eq!(graph.find_edge(&0), None); + + assert_eq!( + graph.edge_indices().collect::>(), + [je, je2, eb, ego_self].into() + ); + + assert_eq!(graph.degree(jakob), 2); + assert_eq!(graph.degree(edgar), 3); + + assert_eq!( + graph + .edges_of(jakob) + .into_indices() + .collect::>(), + [je, je2].into() + ); + assert_eq!( + graph + .edges_of(edgar) + .into_indices() + .collect::>(), + [je, je2, eb].into() + ); + + assert_eq!( + graph + .neighbors(jakob) + .into_indices() + .collect::>(), + [edgar].into() + ); + assert_eq!( + graph + .neighbors(edgar) + .into_indices() + .collect::>(), + [jakob, bernhard].into() + ); + + assert_eq!( + graph + .isolated() + .into_indices() + .collect::>(), + [no_friends_manny].into() + ); + + assert_eq!( + graph + .edges_of(ego) + .into_indices() + .collect::>(), + [ego_self].into() + ); + + assert!(graph.contains_edge_between(edgar, bernhard)); + graph.remove_edge(eb); + assert_eq!(graph.edge_count(), 3); + + assert!(!graph.contains_edge_between(edgar, bernhard)); + assert!(graph.contains_edge_between(jakob, edgar)); + assert!(graph.contains_edge_between(edgar, jakob)); + assert!(!graph.contains_edge_between(jakob, bernhard)); + assert!(graph.contains_edge_between(ego, ego)); + + graph.remove_node(edgar); + assert_eq!(graph.node_count(), 4); + + assert!(!graph.contains_node(edgar)); + assert!(graph.contains_node(jakob)); + assert!(graph.contains_node(bernhard)); + assert!(graph.contains_node(no_friends_manny)); + assert!(graph.contains_node(ego)); + + assert_eq!(graph.edge_count(), 1); +} + +#[test] +fn directed() { + let mut graph = MultiListGraph::<&str, i32, true>::new(); + + assert!(graph.is_directed()); + assert!(graph.is_multigraph()); + + assert_eq!(graph.node_count(), 0); + let jakob = graph.add_node("Jakob"); + let edgar = graph.add_node("Edgar"); + let bernhard = graph.add_node("Bernhard"); + let no_friends_manny = graph.add_node("No Friends Manny"); + let ego = graph.add_node("Ego"); + assert_eq!(graph.node_count(), 5); + + assert!(graph.contains_node(jakob)); + assert!(graph.contains_node(edgar)); + assert!(graph.contains_node(bernhard)); + assert!(graph.contains_node(no_friends_manny)); + + assert_eq!(graph.find_node(&"Edgar"), Some(edgar)); + assert_eq!(graph.find_node(&"NoIReallyDon'tExist"), None); + + assert_eq!( + graph.node_indices().collect::>(), + [jakob, edgar, bernhard, no_friends_manny, ego].into() + ); + + assert_eq!(graph.edge_count(), 0); + let je = graph.add_edge(jakob, edgar, 12); + let je2 = graph.add_edge(jakob, edgar, 2); + let eb = graph.add_edge(edgar, bernhard, 7); + let ego_self = graph.add_edge(ego, ego, 3); + assert_eq!(graph.edge_count(), 4); + + assert!(graph.contains_edge_between(jakob, edgar)); + assert!(!graph.contains_edge_between(edgar, jakob)); + assert!(!graph.contains_edge_between(jakob, bernhard)); + assert!(graph.contains_edge_between(ego, ego)); + + assert_eq!(graph.find_edge(&12), Some(je)); + assert_eq!(graph.find_edge(&2), Some(je2)); + assert_eq!(graph.find_edge(&0), None); + + assert_eq!( + graph.edge_indices().collect::>(), + [je, je2, eb, ego_self].into() + ); + + assert_eq!(graph.degree(jakob), 2); + assert_eq!(graph.degree(edgar), 3); + assert_eq!(graph.out_degree(edgar), 1); + assert_eq!(graph.in_degree(edgar), 2); + + assert_eq!( + graph + .edges_of(jakob) + .into_indices() + .collect::>(), + [je, je2].into() + ); + assert_eq!( + graph + .edges_of(edgar) + .into_indices() + .collect::>(), + [je, je2, eb].into() + ); + assert_eq!( + graph + .incoming_edges_of(edgar) + .into_indices() + .collect::>(), + [je, je2].into() + ); + assert_eq!( + graph + .outgoing_edges_of(edgar) + .into_indices() + .collect::>(), + [eb].into() + ); + + assert_eq!( + graph + .neighbors(jakob) + .into_indices() + .collect::>(), + [edgar].into() + ); + assert_eq!( + graph + .neighbors(edgar) + .into_indices() + .collect::>(), + [jakob, bernhard].into() + ); + assert_eq!( + graph + .in_neighbors(edgar) + .into_indices() + .collect::>(), + [jakob].into() + ); + assert_eq!( + graph + .out_neighbors(edgar) + .into_indices() + .collect::>(), + [bernhard].into() + ); + + assert_eq!( + graph + .isolated() + .into_indices() + .collect::>(), + [no_friends_manny].into() + ); + + assert_eq!( + graph.sources().into_indices().collect::>(), + [jakob, no_friends_manny].into() + ); + assert_eq!( + graph.sinks().into_indices().collect::>(), + [bernhard, no_friends_manny].into() + ); + + graph.reverse(); + + assert_eq!( + graph.sinks().into_indices().collect::>(), + [jakob, no_friends_manny].into() + ); + assert_eq!( + graph.sources().into_indices().collect::>(), + [bernhard, no_friends_manny].into() + ); + + assert!(!graph.contains_edge_between(jakob, edgar)); + assert!(graph.contains_edge_between(edgar, jakob)); + + graph.reverse_edge(je); + graph.reverse_edge(je2); + + assert!(graph.contains_edge_between(jakob, edgar)); + assert!(!graph.contains_edge_between(edgar, jakob)); + + graph.reverse_edge(eb); // just for more readable tests - rereverse it + + assert!(graph.contains_edge_between(edgar, bernhard)); + graph.remove_edge(eb); + assert_eq!(graph.edge_count(), 3); + + assert!(!graph.contains_edge_between(edgar, bernhard)); + assert!(graph.contains_edge_between(jakob, edgar)); + assert!(!graph.contains_edge_between(jakob, bernhard)); + assert!(graph.contains_edge_between(ego, ego)); + + graph.remove_node(edgar); + assert_eq!(graph.node_count(), 4); + + assert!(!graph.contains_node(edgar)); + assert!(graph.contains_node(jakob)); + assert!(graph.contains_node(bernhard)); + assert!(graph.contains_node(no_friends_manny)); + assert!(graph.contains_node(ego)); + + assert_eq!(graph.edge_count(), 1); +} diff --git a/crates/bevy_graph/tests/multi_map.rs b/crates/bevy_graph/tests/multi_map.rs new file mode 100644 index 0000000000000..77894b214ecf2 --- /dev/null +++ b/crates/bevy_graph/tests/multi_map.rs @@ -0,0 +1,302 @@ +use bevy_graph::{ + graphs::{ + keys::{EdgeIdx, NodeIdx}, + map::MultiMapGraph, + DirectedGraph, Graph, + }, + utils::wrapped_indices_iterator::WrappedIndicesIterator, +}; +use hashbrown::HashSet; + +#[test] +fn undirected() { + let mut graph = MultiMapGraph::<&str, i32, false>::new(); + + assert!(!graph.is_directed()); + assert!(graph.is_multigraph()); + + assert_eq!(graph.node_count(), 0); + let jakob = graph.add_node("Jakob"); + let edgar = graph.add_node("Edgar"); + let bernhard = graph.add_node("Bernhard"); + let no_friends_manny = graph.add_node("No Friends Manny"); + let ego = graph.add_node("Ego"); + assert_eq!(graph.node_count(), 5); + + assert!(graph.contains_node(jakob)); + assert!(graph.contains_node(edgar)); + assert!(graph.contains_node(bernhard)); + assert!(graph.contains_node(no_friends_manny)); + assert!(graph.contains_node(ego)); + + assert_eq!(graph.find_node(&"Edgar"), Some(edgar)); + assert_eq!(graph.find_node(&"NoIReallyDon'tExist"), None); + + assert_eq!( + graph.node_indices().collect::>(), + [jakob, edgar, bernhard, no_friends_manny, ego].into() + ); + + assert_eq!(graph.edge_count(), 0); + let je = graph.add_edge(jakob, edgar, 12); + let je2 = graph.add_edge(jakob, edgar, 2); + let eb = graph.add_edge(edgar, bernhard, 7); + let ego_self = graph.add_edge(ego, ego, 3); + assert_eq!(graph.edge_count(), 4); + + assert!(graph.contains_edge_between(jakob, edgar)); + assert!(graph.contains_edge_between(edgar, jakob)); + assert!(!graph.contains_edge_between(jakob, bernhard)); + assert!(graph.contains_edge_between(ego, ego)); + + assert_eq!(graph.find_edge(&12), Some(je)); + assert_eq!(graph.find_edge(&2), Some(je2)); + assert_eq!(graph.find_edge(&0), None); + + assert_eq!( + graph.edge_indices().collect::>(), + [je, je2, eb, ego_self].into() + ); + + assert_eq!(graph.degree(jakob), 2); + assert_eq!(graph.degree(edgar), 3); + + assert_eq!( + graph + .edges_of(jakob) + .into_indices() + .collect::>(), + [je, je2].into() + ); + assert_eq!( + graph + .edges_of(edgar) + .into_indices() + .collect::>(), + [je, je2, eb].into() + ); + + assert_eq!( + graph + .neighbors(jakob) + .into_indices() + .collect::>(), + [edgar].into() + ); + assert_eq!( + graph + .neighbors(edgar) + .into_indices() + .collect::>(), + [jakob, bernhard].into() + ); + + assert_eq!( + graph + .isolated() + .into_indices() + .collect::>(), + [no_friends_manny].into() + ); + + assert_eq!( + graph + .edges_of(ego) + .into_indices() + .collect::>(), + [ego_self].into() + ); + + assert!(graph.contains_edge_between(edgar, bernhard)); + graph.remove_edge(eb); + assert_eq!(graph.edge_count(), 3); + + assert!(!graph.contains_edge_between(edgar, bernhard)); + assert!(graph.contains_edge_between(jakob, edgar)); + assert!(graph.contains_edge_between(edgar, jakob)); + assert!(!graph.contains_edge_between(jakob, bernhard)); + assert!(graph.contains_edge_between(ego, ego)); + + graph.remove_node(edgar); + assert_eq!(graph.node_count(), 4); + + assert!(!graph.contains_node(edgar)); + assert!(graph.contains_node(jakob)); + assert!(graph.contains_node(bernhard)); + assert!(graph.contains_node(no_friends_manny)); + assert!(graph.contains_node(ego)); + + assert_eq!(graph.edge_count(), 1); +} + +#[test] +fn directed() { + let mut graph = MultiMapGraph::<&str, i32, true>::new(); + + assert!(graph.is_directed()); + assert!(graph.is_multigraph()); + + assert_eq!(graph.node_count(), 0); + let jakob = graph.add_node("Jakob"); + let edgar = graph.add_node("Edgar"); + let bernhard = graph.add_node("Bernhard"); + let no_friends_manny = graph.add_node("No Friends Manny"); + let ego = graph.add_node("Ego"); + assert_eq!(graph.node_count(), 5); + + assert!(graph.contains_node(jakob)); + assert!(graph.contains_node(edgar)); + assert!(graph.contains_node(bernhard)); + assert!(graph.contains_node(no_friends_manny)); + + assert_eq!(graph.find_node(&"Edgar"), Some(edgar)); + assert_eq!(graph.find_node(&"NoIReallyDon'tExist"), None); + + assert_eq!( + graph.node_indices().collect::>(), + [jakob, edgar, bernhard, no_friends_manny, ego].into() + ); + + assert_eq!(graph.edge_count(), 0); + let je = graph.add_edge(jakob, edgar, 12); + let je2 = graph.add_edge(jakob, edgar, 2); + let eb = graph.add_edge(edgar, bernhard, 7); + let ego_self = graph.add_edge(ego, ego, 3); + assert_eq!(graph.edge_count(), 4); + + assert!(graph.contains_edge_between(jakob, edgar)); + assert!(!graph.contains_edge_between(edgar, jakob)); + assert!(!graph.contains_edge_between(jakob, bernhard)); + assert!(graph.contains_edge_between(ego, ego)); + + assert_eq!(graph.find_edge(&12), Some(je)); + assert_eq!(graph.find_edge(&2), Some(je2)); + assert_eq!(graph.find_edge(&0), None); + + assert_eq!( + graph.edge_indices().collect::>(), + [je, je2, eb, ego_self].into() + ); + + assert_eq!(graph.degree(jakob), 2); + assert_eq!(graph.degree(edgar), 3); + assert_eq!(graph.out_degree(edgar), 1); + assert_eq!(graph.in_degree(edgar), 2); + + assert_eq!( + graph + .edges_of(jakob) + .into_indices() + .collect::>(), + [je, je2].into() + ); + assert_eq!( + graph + .edges_of(edgar) + .into_indices() + .collect::>(), + [je, je2, eb].into() + ); + assert_eq!( + graph + .incoming_edges_of(edgar) + .into_indices() + .collect::>(), + [je, je2].into() + ); + assert_eq!( + graph + .outgoing_edges_of(edgar) + .into_indices() + .collect::>(), + [eb].into() + ); + + assert_eq!( + graph + .neighbors(jakob) + .into_indices() + .collect::>(), + [edgar].into() + ); + assert_eq!( + graph + .neighbors(edgar) + .into_indices() + .collect::>(), + [jakob, bernhard].into() + ); + assert_eq!( + graph + .in_neighbors(edgar) + .into_indices() + .collect::>(), + [jakob].into() + ); + assert_eq!( + graph + .out_neighbors(edgar) + .into_indices() + .collect::>(), + [bernhard].into() + ); + + assert_eq!( + graph + .isolated() + .into_indices() + .collect::>(), + [no_friends_manny].into() + ); + + assert_eq!( + graph.sources().into_indices().collect::>(), + [jakob, no_friends_manny].into() + ); + assert_eq!( + graph.sinks().into_indices().collect::>(), + [bernhard, no_friends_manny].into() + ); + + graph.reverse(); + + assert_eq!( + graph.sinks().into_indices().collect::>(), + [jakob, no_friends_manny].into() + ); + assert_eq!( + graph.sources().into_indices().collect::>(), + [bernhard, no_friends_manny].into() + ); + + assert!(!graph.contains_edge_between(jakob, edgar)); + assert!(graph.contains_edge_between(edgar, jakob)); + + graph.reverse_edge(je); + graph.reverse_edge(je2); + + assert!(graph.contains_edge_between(jakob, edgar)); + assert!(!graph.contains_edge_between(edgar, jakob)); + + graph.reverse_edge(eb); // just for more readable tests - rereverse it + + assert!(graph.contains_edge_between(edgar, bernhard)); + graph.remove_edge(eb); + assert_eq!(graph.edge_count(), 3); + + assert!(!graph.contains_edge_between(edgar, bernhard)); + assert!(graph.contains_edge_between(jakob, edgar)); + assert!(!graph.contains_edge_between(jakob, bernhard)); + assert!(graph.contains_edge_between(ego, ego)); + + graph.remove_node(edgar); + assert_eq!(graph.node_count(), 4); + + assert!(!graph.contains_node(edgar)); + assert!(graph.contains_node(jakob)); + assert!(graph.contains_node(bernhard)); + assert!(graph.contains_node(no_friends_manny)); + assert!(graph.contains_node(ego)); + + assert_eq!(graph.edge_count(), 1); +} diff --git a/crates/bevy_graph/tests/simple_list.rs b/crates/bevy_graph/tests/simple_list.rs new file mode 100644 index 0000000000000..1ed5df31ee427 --- /dev/null +++ b/crates/bevy_graph/tests/simple_list.rs @@ -0,0 +1,278 @@ +use bevy_graph::{ + graphs::{ + keys::{EdgeIdx, NodeIdx}, + list::SimpleListGraph, + DirectedGraph, Graph, + }, + utils::wrapped_indices_iterator::WrappedIndicesIterator, +}; +use hashbrown::HashSet; + +#[test] +fn undirected() { + let mut graph = SimpleListGraph::<&str, i32, false>::new(); + + assert!(!graph.is_directed()); + assert!(!graph.is_multigraph()); + + assert_eq!(graph.node_count(), 0); + let jakob = graph.add_node("Jakob"); + let edgar = graph.add_node("Edgar"); + let bernhard = graph.add_node("Bernhard"); + let no_friends_manny = graph.add_node("No Friends Manny"); + assert_eq!(graph.node_count(), 4); + + assert!(graph.contains_node(jakob)); + assert!(graph.contains_node(edgar)); + assert!(graph.contains_node(bernhard)); + assert!(graph.contains_node(no_friends_manny)); + + assert_eq!(graph.find_node(&"Edgar"), Some(edgar)); + assert_eq!(graph.find_node(&"NoIReallyDon'tExist"), None); + + assert_eq!( + graph.node_indices().collect::>(), + [jakob, edgar, bernhard, no_friends_manny].into() + ); + + assert_eq!(graph.edge_count(), 0); + let je = graph.add_edge(jakob, edgar, 12); + let eb = graph.add_edge(edgar, bernhard, 7); + assert_eq!(graph.edge_count(), 2); + + assert!(graph.contains_edge_between(jakob, edgar)); + assert!(graph.contains_edge_between(edgar, jakob)); + assert!(!graph.contains_edge_between(jakob, bernhard)); + + assert_eq!(graph.find_edge(&12), Some(je)); + assert_eq!(graph.find_edge(&0), None); + + assert_eq!( + graph.edge_indices().collect::>(), + [je, eb].into() + ); + + assert_eq!(graph.degree(jakob), 1); + assert_eq!(graph.degree(edgar), 2); + + assert_eq!( + graph + .edges_of(jakob) + .into_indices() + .collect::>(), + [je].into() + ); + assert_eq!( + graph + .edges_of(edgar) + .into_indices() + .collect::>(), + [je, eb].into() + ); + + assert_eq!( + graph + .neighbors(jakob) + .into_indices() + .collect::>(), + [edgar].into() + ); + assert_eq!( + graph + .neighbors(edgar) + .into_indices() + .collect::>(), + [jakob, bernhard].into() + ); + + assert_eq!( + graph + .isolated() + .into_indices() + .collect::>(), + [no_friends_manny].into() + ); + + assert!(graph.contains_edge_between(edgar, bernhard)); + graph.remove_edge(eb); + assert_eq!(graph.edge_count(), 1); + + assert!(!graph.contains_edge_between(edgar, bernhard)); + assert!(graph.contains_edge_between(jakob, edgar)); + assert!(graph.contains_edge_between(edgar, jakob)); + assert!(!graph.contains_edge_between(jakob, bernhard)); + + graph.remove_node(edgar); + assert_eq!(graph.node_count(), 3); + + assert!(!graph.contains_node(edgar)); + assert!(graph.contains_node(jakob)); + assert!(graph.contains_node(bernhard)); + assert!(graph.contains_node(no_friends_manny)); + + assert_eq!(graph.edge_count(), 0); +} + +#[test] +fn directed() { + let mut graph = SimpleListGraph::<&str, i32, true>::new(); + + assert!(graph.is_directed()); + assert!(!graph.is_multigraph()); + + assert_eq!(graph.node_count(), 0); + let jakob = graph.add_node("Jakob"); + let edgar = graph.add_node("Edgar"); + let bernhard = graph.add_node("Bernhard"); + let no_friends_manny = graph.add_node("No Friends Manny"); + assert_eq!(graph.node_count(), 4); + + assert!(graph.contains_node(jakob)); + assert!(graph.contains_node(edgar)); + assert!(graph.contains_node(bernhard)); + assert!(graph.contains_node(no_friends_manny)); + + assert_eq!(graph.find_node(&"Edgar"), Some(edgar)); + assert_eq!(graph.find_node(&"NoIReallyDon'tExist"), None); + + assert_eq!( + graph.node_indices().collect::>(), + [jakob, edgar, bernhard, no_friends_manny].into() + ); + + assert_eq!(graph.edge_count(), 0); + let je = graph.add_edge(jakob, edgar, 12); + let eb = graph.add_edge(edgar, bernhard, 7); + assert_eq!(graph.edge_count(), 2); + + assert!(graph.contains_edge_between(jakob, edgar)); + assert!(!graph.contains_edge_between(edgar, jakob)); + assert!(!graph.contains_edge_between(jakob, bernhard)); + + assert_eq!(graph.find_edge(&12), Some(je)); + assert_eq!(graph.find_edge(&0), None); + + assert_eq!( + graph.edge_indices().collect::>(), + [je, eb].into() + ); + + assert_eq!(graph.degree(jakob), 1); + assert_eq!(graph.degree(edgar), 2); + assert_eq!(graph.out_degree(edgar), 1); + assert_eq!(graph.in_degree(edgar), 1); + + assert_eq!( + graph + .edges_of(jakob) + .into_indices() + .collect::>(), + [je].into() + ); + assert_eq!( + graph + .edges_of(edgar) + .into_indices() + .collect::>(), + [je, eb].into() + ); + assert_eq!( + graph + .incoming_edges_of(edgar) + .into_indices() + .collect::>(), + [je].into() + ); + assert_eq!( + graph + .outgoing_edges_of(edgar) + .into_indices() + .collect::>(), + [eb].into() + ); + + assert_eq!( + graph + .neighbors(jakob) + .into_indices() + .collect::>(), + [edgar].into() + ); + assert_eq!( + graph + .neighbors(edgar) + .into_indices() + .collect::>(), + [jakob, bernhard].into() + ); + assert_eq!( + graph + .in_neighbors(edgar) + .into_indices() + .collect::>(), + [jakob].into() + ); + assert_eq!( + graph + .out_neighbors(edgar) + .into_indices() + .collect::>(), + [bernhard].into() + ); + + assert_eq!( + graph + .isolated() + .into_indices() + .collect::>(), + [no_friends_manny].into() + ); + + assert_eq!( + graph.sources().into_indices().collect::>(), + [jakob, no_friends_manny].into() + ); + assert_eq!( + graph.sinks().into_indices().collect::>(), + [bernhard, no_friends_manny].into() + ); + + graph.reverse(); + + assert_eq!( + graph.sinks().into_indices().collect::>(), + [jakob, no_friends_manny].into() + ); + assert_eq!( + graph.sources().into_indices().collect::>(), + [bernhard, no_friends_manny].into() + ); + + assert!(!graph.contains_edge_between(jakob, edgar)); + assert!(graph.contains_edge_between(edgar, jakob)); + + graph.reverse_edge(je); + + assert!(graph.contains_edge_between(jakob, edgar)); + assert!(!graph.contains_edge_between(edgar, jakob)); + + graph.reverse_edge(eb); // just for more readable tests - rereverse it + + assert!(graph.contains_edge_between(edgar, bernhard)); + graph.remove_edge(eb); + assert_eq!(graph.edge_count(), 1); + + assert!(!graph.contains_edge_between(edgar, bernhard)); + assert!(graph.contains_edge_between(jakob, edgar)); + assert!(!graph.contains_edge_between(jakob, bernhard)); + + graph.remove_node(edgar); + assert_eq!(graph.node_count(), 3); + + assert!(!graph.contains_node(edgar)); + assert!(graph.contains_node(jakob)); + assert!(graph.contains_node(bernhard)); + assert!(graph.contains_node(no_friends_manny)); + + assert_eq!(graph.edge_count(), 0); +} diff --git a/crates/bevy_graph/tests/simple_map.rs b/crates/bevy_graph/tests/simple_map.rs new file mode 100644 index 0000000000000..858ad0c6f2bd4 --- /dev/null +++ b/crates/bevy_graph/tests/simple_map.rs @@ -0,0 +1,278 @@ +use bevy_graph::{ + graphs::{ + keys::{EdgeIdx, NodeIdx}, + map::SimpleMapGraph, + DirectedGraph, Graph, + }, + utils::wrapped_indices_iterator::WrappedIndicesIterator, +}; +use hashbrown::HashSet; + +#[test] +fn undirected() { + let mut graph = SimpleMapGraph::<&str, i32, false>::new(); + + assert!(!graph.is_directed()); + assert!(!graph.is_multigraph()); + + assert_eq!(graph.node_count(), 0); + let jakob = graph.add_node("Jakob"); + let edgar = graph.add_node("Edgar"); + let bernhard = graph.add_node("Bernhard"); + let no_friends_manny = graph.add_node("No Friends Manny"); + assert_eq!(graph.node_count(), 4); + + assert!(graph.contains_node(jakob)); + assert!(graph.contains_node(edgar)); + assert!(graph.contains_node(bernhard)); + assert!(graph.contains_node(no_friends_manny)); + + assert_eq!(graph.find_node(&"Edgar"), Some(edgar)); + assert_eq!(graph.find_node(&"NoIReallyDon'tExist"), None); + + assert_eq!( + graph.node_indices().collect::>(), + [jakob, edgar, bernhard, no_friends_manny].into() + ); + + assert_eq!(graph.edge_count(), 0); + let je = graph.add_edge(jakob, edgar, 12); + let eb = graph.add_edge(edgar, bernhard, 7); + assert_eq!(graph.edge_count(), 2); + + assert!(graph.contains_edge_between(jakob, edgar)); + assert!(graph.contains_edge_between(edgar, jakob)); + assert!(!graph.contains_edge_between(jakob, bernhard)); + + assert_eq!(graph.find_edge(&12), Some(je)); + assert_eq!(graph.find_edge(&0), None); + + assert_eq!( + graph.edge_indices().collect::>(), + [je, eb].into() + ); + + assert_eq!(graph.degree(jakob), 1); + assert_eq!(graph.degree(edgar), 2); + + assert_eq!( + graph + .edges_of(jakob) + .into_indices() + .collect::>(), + [je].into() + ); + assert_eq!( + graph + .edges_of(edgar) + .into_indices() + .collect::>(), + [je, eb].into() + ); + + assert_eq!( + graph + .neighbors(jakob) + .into_indices() + .collect::>(), + [edgar].into() + ); + assert_eq!( + graph + .neighbors(edgar) + .into_indices() + .collect::>(), + [jakob, bernhard].into() + ); + + assert_eq!( + graph + .isolated() + .into_indices() + .collect::>(), + [no_friends_manny].into() + ); + + assert!(graph.contains_edge_between(edgar, bernhard)); + graph.remove_edge(eb); + assert_eq!(graph.edge_count(), 1); + + assert!(!graph.contains_edge_between(edgar, bernhard)); + assert!(graph.contains_edge_between(jakob, edgar)); + assert!(graph.contains_edge_between(edgar, jakob)); + assert!(!graph.contains_edge_between(jakob, bernhard)); + + graph.remove_node(edgar); + assert_eq!(graph.node_count(), 3); + + assert!(!graph.contains_node(edgar)); + assert!(graph.contains_node(jakob)); + assert!(graph.contains_node(bernhard)); + assert!(graph.contains_node(no_friends_manny)); + + assert_eq!(graph.edge_count(), 0); +} + +#[test] +fn directed() { + let mut graph = SimpleMapGraph::<&str, i32, true>::new(); + + assert!(graph.is_directed()); + assert!(!graph.is_multigraph()); + + assert_eq!(graph.node_count(), 0); + let jakob = graph.add_node("Jakob"); + let edgar = graph.add_node("Edgar"); + let bernhard = graph.add_node("Bernhard"); + let no_friends_manny = graph.add_node("No Friends Manny"); + assert_eq!(graph.node_count(), 4); + + assert!(graph.contains_node(jakob)); + assert!(graph.contains_node(edgar)); + assert!(graph.contains_node(bernhard)); + assert!(graph.contains_node(no_friends_manny)); + + assert_eq!(graph.find_node(&"Edgar"), Some(edgar)); + assert_eq!(graph.find_node(&"NoIReallyDon'tExist"), None); + + assert_eq!( + graph.node_indices().collect::>(), + [jakob, edgar, bernhard, no_friends_manny].into() + ); + + assert_eq!(graph.edge_count(), 0); + let je = graph.add_edge(jakob, edgar, 12); + let eb = graph.add_edge(edgar, bernhard, 7); + assert_eq!(graph.edge_count(), 2); + + assert!(graph.contains_edge_between(jakob, edgar)); + assert!(!graph.contains_edge_between(edgar, jakob)); + assert!(!graph.contains_edge_between(jakob, bernhard)); + + assert_eq!(graph.find_edge(&12), Some(je)); + assert_eq!(graph.find_edge(&0), None); + + assert_eq!( + graph.edge_indices().collect::>(), + [je, eb].into() + ); + + assert_eq!(graph.degree(jakob), 1); + assert_eq!(graph.degree(edgar), 2); + assert_eq!(graph.out_degree(edgar), 1); + assert_eq!(graph.in_degree(edgar), 1); + + assert_eq!( + graph + .edges_of(jakob) + .into_indices() + .collect::>(), + [je].into() + ); + assert_eq!( + graph + .edges_of(edgar) + .into_indices() + .collect::>(), + [je, eb].into() + ); + assert_eq!( + graph + .incoming_edges_of(edgar) + .into_indices() + .collect::>(), + [je].into() + ); + assert_eq!( + graph + .outgoing_edges_of(edgar) + .into_indices() + .collect::>(), + [eb].into() + ); + + assert_eq!( + graph + .neighbors(jakob) + .into_indices() + .collect::>(), + [edgar].into() + ); + assert_eq!( + graph + .neighbors(edgar) + .into_indices() + .collect::>(), + [jakob, bernhard].into() + ); + assert_eq!( + graph + .in_neighbors(edgar) + .into_indices() + .collect::>(), + [jakob].into() + ); + assert_eq!( + graph + .out_neighbors(edgar) + .into_indices() + .collect::>(), + [bernhard].into() + ); + + assert_eq!( + graph + .isolated() + .into_indices() + .collect::>(), + [no_friends_manny].into() + ); + + assert_eq!( + graph.sources().into_indices().collect::>(), + [jakob, no_friends_manny].into() + ); + assert_eq!( + graph.sinks().into_indices().collect::>(), + [bernhard, no_friends_manny].into() + ); + + graph.reverse(); + + assert_eq!( + graph.sinks().into_indices().collect::>(), + [jakob, no_friends_manny].into() + ); + assert_eq!( + graph.sources().into_indices().collect::>(), + [bernhard, no_friends_manny].into() + ); + + assert!(!graph.contains_edge_between(jakob, edgar)); + assert!(graph.contains_edge_between(edgar, jakob)); + + graph.reverse_edge(je); + + assert!(graph.contains_edge_between(jakob, edgar)); + assert!(!graph.contains_edge_between(edgar, jakob)); + + graph.reverse_edge(eb); // just for more readable tests - rereverse it + + assert!(graph.contains_edge_between(edgar, bernhard)); + graph.remove_edge(eb); + assert_eq!(graph.edge_count(), 1); + + assert!(!graph.contains_edge_between(edgar, bernhard)); + assert!(graph.contains_edge_between(jakob, edgar)); + assert!(!graph.contains_edge_between(jakob, bernhard)); + + graph.remove_node(edgar); + assert_eq!(graph.node_count(), 3); + + assert!(!graph.contains_node(edgar)); + assert!(graph.contains_node(jakob)); + assert!(graph.contains_node(bernhard)); + assert!(graph.contains_node(no_friends_manny)); + + assert_eq!(graph.edge_count(), 0); +}