Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

First approach for bevy_graph #7130

Closed
wants to merge 186 commits into from
Closed
Show file tree
Hide file tree
Changes from 185 commits
Commits
Show all changes
186 commits
Select commit Hold shift + click to select a range
1a3cbba
Add AdjacencyMapGraph for `bevy_graph`
DasLixou Jan 8, 2023
b091e36
Make the CI happy :D and add Default trait
DasLixou Jan 8, 2023
bb0f39c
Directed and Undirected Edges
DasLixou Jan 8, 2023
3b035c0
Moving files and modules
DasLixou Jan 8, 2023
9c5f2ea
Whoops was faster than cargo check
DasLixou Jan 8, 2023
6a8d902
Make NodeIdx and EdgeIdx new keys
DasLixou Jan 9, 2023
c5157b8
Add basic SimpleListGraph
DasLixou Jan 9, 2023
27c9fe0
Set directed with const generic bool
DasLixou Jan 9, 2023
3eabc13
Let Map also use SecondaryMap instead of HashMap
DasLixou Jan 9, 2023
d6e0f9c
Replace SlotMap with HopSlotMap
DasLixou Jan 9, 2023
817556c
Extract basic Graph functions into `Graph` trait
DasLixou Jan 9, 2023
49582c7
`DirectedGraph` and `UndirectedGraph` traits
DasLixou Jan 9, 2023
8ffee13
More Edge util functions
DasLixou Jan 9, 2023
e239c99
Removing edges with `edge_between`
DasLixou Jan 9, 2023
9031134
Make CI happy :D
DasLixou Jan 9, 2023
ac4cdce
remove returns the Edge Data
DasLixou Jan 9, 2023
40c27c5
Naming
DasLixou Jan 9, 2023
3a4d94d
API Design changes
DasLixou Jan 9, 2023
602bd2b
Add basic benchmark for `SimpleMapGraph`
DasLixou Jan 9, 2023
a75bf1a
Another day of making the CI happy
DasLixou Jan 9, 2023
defb878
Making Graphs clonable
DasLixou Jan 10, 2023
b0cee31
naive implementation of `edges_of`
DasLixou Jan 10, 2023
f427cd8
Make `edges_of` also return destination of edge
DasLixou Jan 10, 2023
c79ba21
make clippy happy
DasLixou Jan 10, 2023
3edc070
Better Error Handling with `GraphError`
DasLixou Jan 10, 2023
69b9f59
Simple BFS
DasLixou Jan 10, 2023
803efc0
Better BFS Api
DasLixou Jan 10, 2023
499db70
Mutable Iteration for BFS
DasLixou Jan 10, 2023
8b0031b
allow(clippy::len_without_is_empty)
DasLixou Jan 10, 2023
6e8725c
ok clippy ill just write it
DasLixou Jan 10, 2023
daa4646
Auto trait function for algos
DasLixou Jan 10, 2023
b815eb3
Store connected Nodes for Edge
DasLixou Jan 10, 2023
bbbef82
Restructuring crate
DasLixou Jan 10, 2023
e19e320
Use the new data from edge to simplify edge-remove
DasLixou Jan 10, 2023
73b59b0
Fix benchmarks
DasLixou Jan 10, 2023
e3e791d
CI...
DasLixou Jan 10, 2023
4fad13e
Restructed traits
DasLixou Jan 11, 2023
3db9d7e
Another try of api
DasLixou Jan 11, 2023
70f1700
impl_graph macro is now a Sigma 🗿
DasLixou Jan 12, 2023
5c51e84
Update Bench
DasLixou Jan 12, 2023
be83c21
Now i understand what $crate does :D
DasLixou Jan 12, 2023
46fcd86
Add support to remove nodes
DasLixou Jan 12, 2023
a7442e7
Move Tests for Graph in one file
DasLixou Jan 13, 2023
5aa9c14
graph_tests macro :o
DasLixou Jan 13, 2023
0710a1d
Cleanup
DasLixou Jan 13, 2023
cf661e1
Refactor `new_edge` with safety ⚠️
DasLixou Jan 13, 2023
7e1571d
outscore in unsafe functions 🥶
DasLixou Jan 13, 2023
033c200
Make clippy happy 😆
DasLixou Jan 13, 2023
18a4232
benchmarks fast fix
DasLixou Jan 13, 2023
e3be206
make indices const :D
DasLixou Jan 13, 2023
450ff00
Preparation for Multi Graphs :O
DasLixou Jan 13, 2023
c2c0ddc
`edge_between` now returns GraphResult
DasLixou Jan 13, 2023
7753257
Fix tests
DasLixou Jan 13, 2023
d25ec0a
Clippy.
DasLixou Jan 13, 2023
06d5709
Fix benchmarks
DasLixou Jan 14, 2023
83467a2
Add iterative DepthFirstSearch algo
DasLixou Jan 14, 2023
86cbf7e
Implement unsafe `get_node` for graphs
DasLixou Jan 14, 2023
78f510c
Add `next_unchecked` functions for BFS and DFS
DasLixou Jan 14, 2023
d59e6ba
Clippy 📎
DasLixou Jan 14, 2023
9788bae
Simple Graph `new_edge` should not have same node
DasLixou Jan 14, 2023
5c0fe70
`edge(s)_between` chaos for multi graph
DasLixou Jan 14, 2023
ca9d139
[DIRTY] refactor `impl_graph!` macro and traits
DasLixou Jan 15, 2023
2410df4
Use new `impl_graph` for cleaner `edge_between`
DasLixou Jan 15, 2023
7908c1f
benchi benchi
DasLixou Jan 15, 2023
d011cd9
Add benchmarks for `bfs` and `dfs` algo
DasLixou Jan 15, 2023
f94dd22
Move `new` into Graph trait
DasLixou Jan 15, 2023
5518c6b
Impl `new_edge` for `MultiMapGraph`
DasLixou Jan 15, 2023
088cfe1
add `remove_edge` for `MultiMapGraph`
DasLixou Jan 15, 2023
159e70f
Add unsafe `remove_edge_unchecked`
DasLixou Jan 15, 2023
bb1333c
Cleanup duplicates
DasLixou Jan 15, 2023
dcc8df7
Add `remove_node` for `MultiMapGraph`
DasLixou Jan 15, 2023
a007ee5
Add `edges_of` for `MultiMapGraph`
DasLixou Jan 15, 2023
2dac00b
Make `SimpleGraph`'s `edge_between` an Option
DasLixou Jan 15, 2023
7ab4f66
Implement last methods for `MultiMapGraph` :D
DasLixou Jan 15, 2023
b8dea3a
Add tests for MultiGraphs
DasLixou Jan 15, 2023
c957fc5
Fix benches
DasLixou Jan 15, 2023
dc00827
Add skeleton for `MultiListGraph`
DasLixou Jan 15, 2023
09eee1e
Finish `MultiListGraph`
DasLixou Jan 15, 2023
dcfcfeb
Ci is happy = I am happy
DasLixou Jan 15, 2023
8d750db
Documenting Graph and SimpleGraph traits
DasLixou Jan 16, 2023
f4e703a
Merge branches 'graph' and 'graph' of github.com:DasLixou/bevy into g…
DasLixou Jan 16, 2023
1523a38
More documentation
DasLixou Jan 16, 2023
3aec991
backticks...
DasLixou Jan 16, 2023
c63597a
Apparently don't know my own API - well thats cool
DasLixou Jan 16, 2023
3867311
Update description in Cargo.toml
DasLixou Jan 16, 2023
51faf5f
Link algorythms
DasLixou Jan 16, 2023
c4150ca
algos with `with_capacity`
DasLixou Jan 16, 2023
a13b35b
docs
DasLixou Jan 16, 2023
e0c24ae
docs
DasLixou Jan 16, 2023
2552932
docs
DasLixou Jan 16, 2023
6389a6a
docs
DasLixou Jan 16, 2023
c387337
the wording i saw infront of me
DasLixou Jan 16, 2023
8cf48b3
docs
DasLixou Jan 16, 2023
0ced1be
docs
DasLixou Jan 16, 2023
5304cc2
docs
DasLixou Jan 16, 2023
b082151
`GraphError` docs
DasLixou Jan 16, 2023
849e9e9
`Edge<E>` docs
DasLixou Jan 16, 2023
5df0f04
missing_docs warn
DasLixou Jan 16, 2023
6c9e168
thiserror and document Idx types
DasLixou Jan 16, 2023
37bcd3f
Merge branch 'graph' of github.com:DasLixou/bevy into graph
DasLixou Jan 16, 2023
91e4c79
big doc and result change
DasLixou Jan 16, 2023
d23424c
renaming `algo_*` methods for graph
DasLixou Jan 16, 2023
624d871
working on algos
DasLixou Jan 16, 2023
8fb962c
docs for Graph traits
DasLixou Jan 16, 2023
f864a31
benchies
DasLixou Jan 16, 2023
03b95db
remove algo functions from `Graph` trait
DasLixou Jan 16, 2023
1249298
remove `impl_graph` macro
DasLixou Jan 16, 2023
dd78ecc
clippy
DasLixou Jan 16, 2023
3d6fac5
more cleanup
DasLixou Jan 16, 2023
2c75cdf
add some more counting functions
DasLixou Jan 16, 2023
f57620b
changes
DasLixou Jan 17, 2023
8531841
`add_node`
DasLixou Jan 17, 2023
498c0f6
Remove all old code and implement `new_edge`
DasLixou Jan 17, 2023
d170306
`remove_edge`
DasLixou Jan 17, 2023
3a6f22a
changes to `add_edge` signature
DasLixou Jan 17, 2023
4a548f7
`clear_edges` and `clear`
DasLixou Jan 17, 2023
840f53a
getting nodes and edges
DasLixou Jan 17, 2023
0781f40
Add `AdjacencyStorage`
DasLixou Jan 17, 2023
ea34164
`nodes` iterator 🤧
DasLixou Jan 17, 2023
aa47441
`EdgeRef` and `EdgeMut`
DasLixou Jan 17, 2023
1245f90
`edges` and `edges_mut`
DasLixou Jan 17, 2023
34a48b1
Resolve all errors
DasLixou Jan 18, 2023
c58dec5
Make VecMap faster with binary search
DasLixou Jan 18, 2023
2f3ea70
Some more graph creation funcs
DasLixou Jan 18, 2023
658da34
Add `reserve`ing for graphs
DasLixou Jan 18, 2023
974d0ab
Change file structure
DasLixou Jan 19, 2023
c9b2d6c
Make `Edges` iter 🥳🫠
DasLixou Jan 19, 2023
584f89c
Move iters to own folder
DasLixou Jan 19, 2023
68d6b86
make ci happy
DasLixou Jan 20, 2023
71c8881
ci
DasLixou Jan 20, 2023
6b20640
doc backtick
DasLixou Jan 20, 2023
d790808
Renaimg iter
DasLixou Jan 20, 2023
eabfb85
Add `EdgesRef` iter
DasLixou Jan 20, 2023
ab9cdcd
Use the new `EdgesRef` iter
DasLixou Jan 20, 2023
8ffa7a8
Add `EdgesMut` iter
DasLixou Jan 20, 2023
2ff2a2f
Use new `EdgesMut` iter
DasLixou Jan 20, 2023
52db276
[DIRTY] brokey iter code :c
DasLixou Jan 20, 2023
f66274b
Add `in_degree` and `out_degree`
DasLixou Jan 20, 2023
99dfc53
Changes
DasLixou Jan 20, 2023
ed5e7ce
Fix `EdgesByIdxMut`
DasLixou Jan 21, 2023
1500cc8
Cleanup
DasLixou Jan 21, 2023
c7f410b
backticks
DasLixou Jan 21, 2023
f3230a3
Add `reverse_edge`
DasLixou Jan 21, 2023
1c6511e
Add `reverse` function
DasLixou Jan 21, 2023
7776f94
Fix `with_capacity` adjacencies
DasLixou Jan 21, 2023
fb4ac2c
[WIP] Source and Sink iters
DasLixou Jan 21, 2023
96c2aa5
Rework on `Source` iter
DasLixou Jan 21, 2023
dbb082d
Finally `SourcesMut`
DasLixou Jan 21, 2023
069f6b3
Add `Sinks` 😀
DasLixou Jan 21, 2023
507d991
Add `in_neighbors(_mut)` and `out_neighbors(_mut)`
DasLixou Jan 22, 2023
5947d27
implement `degree` for directed graphs
DasLixou Jan 22, 2023
3c3a37d
Store `EdgeIdx` in `Edge<E>`
DasLixou Jan 22, 2023
1192e3d
Add umimplemented `edges_of`
DasLixou Jan 22, 2023
38424ac
implement `remove_node`
DasLixou Jan 22, 2023
caed68e
Revert saving `EdgeIdx` in `Edge<E>`
DasLixou Jan 22, 2023
0dfbe39
yeah forgot about algos lol
DasLixou Jan 22, 2023
d393fde
docs
DasLixou Jan 22, 2023
7349546
Add `GraphIterator` trait for BFS and DFS
DasLixou Jan 23, 2023
bad5971
Make `BFS/DFS` return `NodeIdx` and wrap them
DasLixou Jan 23, 2023
639e8ec
Add `new_mut` to algos
DasLixou Jan 23, 2023
222b73b
Add `from_graph` also for other `ByIdx(Mut)` iters
DasLixou Jan 23, 2023
ee88d17
Work on customizing algos
DasLixou Jan 23, 2023
14a98e8
Add `custom_ref` and `custom_mut` for algos
DasLixou Jan 23, 2023
4bc80f2
Add `ancestors(_mut)` and `descendants(_mut)`
DasLixou Jan 23, 2023
05f639a
Add `edges_of(_mut)` for simple graphs
DasLixou Jan 23, 2023
cd2c693
Hopefully working `edges_of(_mut)` for multi graph
DasLixou Jan 23, 2023
d204e1e
Finish `degree`
DasLixou Jan 24, 2023
c9a0581
CI
DasLixou Jan 24, 2023
f37b72a
Make WrappedIterator simpler
DasLixou Jan 25, 2023
aff9c04
Lemme just implement that 💀
DasLixou Jan 25, 2023
9befe86
Start of simple integration tests
DasLixou Jan 25, 2023
0551822
Rename `has_node` to `contains_node`
DasLixou Jan 27, 2023
0a3a257
Add `find_node` and `find_edge`
DasLixou Jan 27, 2023
4a22a17
Test the new funcs
DasLixou Jan 27, 2023
638d503
tests: unordered eq
DasLixou Jan 27, 2023
e79151b
Now finally ever iter is nonborrowed index
DasLixou Jan 27, 2023
2a55cbd
Undirected Simple List works aswell 🤗
DasLixou Jan 27, 2023
4bbd4c0
Add tests for `directed` simple graphs and ...
DasLixou Jan 28, 2023
d3f356e
`remove_[node/edge]` tests and list fix
DasLixou Jan 28, 2023
1ca6cb3
Big fixes
DasLixou Jan 29, 2023
2f23d10
Fix everything
DasLixou Jan 29, 2023
dc294ed
Add tests for `multi_list`
DasLixou Jan 29, 2023
573ed2e
the final clippy touches
DasLixou Jan 29, 2023
baa23a0
Update BFS/DFS Safety comments
DasLixou Jul 8, 2023
4e9e959
fix: `contains_edge_between` panics no more
DasLixou Jul 8, 2023
21d5fb0
Merge branch 'main' into graph
DasLixou Sep 18, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions benches/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
bevy_graph = { path = "../crates/bevy_graph" }
bevy_reflect = { path = "../crates/bevy_reflect" }
bevy_tasks = { path = "../crates/bevy_tasks" }
bevy_utils = { path = "../crates/bevy_utils" }
Expand Down
14 changes: 14 additions & 0 deletions crates/bevy_graph/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"
151 changes: 151 additions & 0 deletions crates/bevy_graph/src/algos/bfs.rs
Original file line number Diff line number Diff line change
@@ -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<N, E>, I: Iterator<Item = EdgeRef<'g, E>>> {
graph: &'g G,
queue: VecDeque<NodeIdx>,
visited: HashSet<NodeIdx>,
visitor: fn(&'g G, NodeIdx) -> I,
phantom: PhantomData<(N, E)>,
}

impl<'g, N, E: 'g, G: Graph<N, E>, I: Iterator<Item = EdgeRef<'g, E>>>
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<N, E>> 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<N, E>, I: Iterator<Item = EdgeRef<'g, E>>> Iterator
for BreadthFirstSearch<'g, N, E, G, I>
{
type Item = NodeIdx;

fn next(&mut self) -> Option<Self::Item> {
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::<i32, (), true>::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);
}
}
148 changes: 148 additions & 0 deletions crates/bevy_graph/src/algos/dfs.rs
Original file line number Diff line number Diff line change
@@ -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<N, E>, I: Iterator<Item = EdgeRef<'g, E>>> {
graph: &'g G,
stack: Vec<NodeIdx>,
visited: HashSet<NodeIdx>,
visitor: fn(&'g G, NodeIdx) -> I,
phantom: PhantomData<(N, E)>,
}

impl<'g, N, E: 'g, G: Graph<N, E>, I: Iterator<Item = EdgeRef<'g, E>>>
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<N, E>> 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<N, E>, I: Iterator<Item = EdgeRef<'g, E>>> Iterator
for DepthFirstSearch<'g, N, E, G, I>
{
type Item = NodeIdx;

fn next(&mut self) -> Option<Self::Item> {
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::<i32, (), true>::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);
}
}
4 changes: 4 additions & 0 deletions crates/bevy_graph/src/algos/mod.rs
Original file line number Diff line number Diff line change
@@ -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;
17 changes: 17 additions & 0 deletions crates/bevy_graph/src/error.rs
Original file line number Diff line number Diff line change
@@ -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 {
DasLixou marked this conversation as resolved.
Show resolved Hide resolved
/// 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,
}
Loading
Loading