From 34a540e5f18f2c7cc3511b6a802aca7c4199a527 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Fri, 21 Feb 2025 16:45:51 +1300 Subject: [PATCH 1/9] Update `CHANGELOG.md`. --- CHANGELOG.md | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 23cda0f..2c83f76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,14 +4,11 @@ * Add `Dag::transitive_reduce` ([#36][#36], [#41][#41]). * Add `Dag::{reserve_*, shrink_*}` functions ([#38][#38]). - - `reserve_nodes` - - `reserve_exact_nodes` - - `reserve_edges` - - `reserve_exact_edges` - - `shrink_to_fit` - - `shrink_to_fit_nodes` - - `shrink_to_fit_edges` +* Add `Dag::transitive_reduce` ([#36][#36], [#41][#41]). +* Add `Dag::{reserve_*, shrink_*}` functions ([#38][#38]). +* Update `petgraph` to `0.7` ([#40][#40]) [#36]: https://github.com/mitchmindtree/daggy/pull/36 [#38]: https://github.com/mitchmindtree/daggy/pull/38 +[#40]: https://github.com/mitchmindtree/daggy/pull/40 [#41]: https://github.com/mitchmindtree/daggy/pull/41 From ee987b1e2f38ed394f37e3341fa9b5792636b086 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Fri, 21 Feb 2025 16:51:58 +1300 Subject: [PATCH 2/9] Address clippy lints. --- src/lib.rs | 29 +++++++++++------------------ src/serde.rs | 4 ++-- src/stable_dag/mod.rs | 27 ++++++++++----------------- src/stable_dag/serde.rs | 4 ++-- src/walker.rs | 9 ++++----- 5 files changed, 29 insertions(+), 44 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 2341d98..c0a4585 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -211,10 +211,7 @@ where { let graph = self.graph.map(node_map, edge_map); let cycle_state = self.cycle_state.clone(); - Dag { - graph: graph, - cycle_state: cycle_state, - } + Dag { graph, cycle_state } } /// Create a new `Dag` by mapping node and edge weights. A node or edge may be mapped to `None` @@ -237,10 +234,7 @@ where { let graph = self.graph.filter_map(node_map, edge_map); let cycle_state = DfsSpace::new(&graph); - Dag { - graph: graph, - cycle_state: cycle_state, - } + Dag { graph, cycle_state } } /// Removes all nodes and edges from the **Dag**. @@ -434,10 +428,8 @@ where for (a, b, weight) in edges { // Check whether or not we'll need to check for cycles. - if !should_check_for_cycle { - if must_check_for_cycle(self, a, b) { - should_check_for_cycle = true; - } + if !should_check_for_cycle && must_check_for_cycle(self, a, b) { + should_check_for_cycle = true; } self.graph.add_edge(a, b, weight); @@ -614,6 +606,7 @@ where /// Both indices can be either `NodeIndex`s, `EdgeIndex`s or a combination of the two. /// /// **Panics** if the indices are equal or if they are out of bounds. + #[allow(clippy::type_complexity)] pub fn index_twice_mut( &mut self, a: A, @@ -657,7 +650,7 @@ where pub fn parents(&self, child: NodeIndex) -> Parents { let walk_edges = self.graph.neighbors_directed(child, pg::Incoming).detach(); Parents { - walk_edges: walk_edges, + walk_edges, _node: PhantomData, _edge: PhantomData, } @@ -675,7 +668,7 @@ where pub fn children(&self, parent: NodeIndex) -> Children { let walk_edges = self.graph.neighbors_directed(parent, pg::Outgoing).detach(); Children { - walk_edges: walk_edges, + walk_edges, _node: PhantomData, _edge: PhantomData, } @@ -749,12 +742,12 @@ where // Dag implementations. -impl Into> for Dag +impl From> for DiGraph where Ix: IndexType, { - fn into(self) -> DiGraph { - self.into_graph() + fn from(val: Dag) -> Self { + val.into_graph() } } @@ -890,7 +883,7 @@ where } } -impl<'a, N, E, Ix> IntoNodeIdentifiers for &'a Dag +impl IntoNodeIdentifiers for &Dag where Ix: IndexType, { diff --git a/src/serde.rs b/src/serde.rs index cba8c2b..60d1792 100644 --- a/src/serde.rs +++ b/src/serde.rs @@ -31,8 +31,8 @@ where let graph = Deserialize::deserialize(deserializer)?; let cycle_state = DfsSpace::new(&graph); let dag = Dag { - graph: graph, - cycle_state: cycle_state, + graph, + cycle_state, }; Ok(dag) } diff --git a/src/stable_dag/mod.rs b/src/stable_dag/mod.rs index a6b377e..697b3ad 100644 --- a/src/stable_dag/mod.rs +++ b/src/stable_dag/mod.rs @@ -182,10 +182,7 @@ where { let graph = self.graph.map(node_map, edge_map); let cycle_state = self.cycle_state.clone(); - StableDag { - graph: graph, - cycle_state: cycle_state, - } + StableDag { graph, cycle_state } } /// Create a new `StableDag` by mapping node and edge weights. A node or edge may be mapped to @@ -203,10 +200,7 @@ where { let graph = self.graph.filter_map(node_map, edge_map); let cycle_state = DfsSpace::new(&graph); - StableDag { - graph: graph, - cycle_state: cycle_state, - } + StableDag { graph, cycle_state } } /// Removes all nodes and edges from the **StableDag**. @@ -349,10 +343,8 @@ where for (a, b, weight) in edges { // Check whether or not we'll need to check for cycles. - if !should_check_for_cycle { - if must_check_for_cycle(self, a, b) { - should_check_for_cycle = true; - } + if !should_check_for_cycle && must_check_for_cycle(self, a, b) { + should_check_for_cycle = true; } self.graph.add_edge(a, b, weight); @@ -506,6 +498,7 @@ where /// Both indices can be either `NodeIndex`s, `EdgeIndex`s or a combination of the two. /// /// **Panics** if the indices are equal or if they are out of bounds. + #[allow(clippy::type_complexity)] pub fn index_twice_mut( &mut self, a: A, @@ -552,7 +545,7 @@ where pub fn parents(&self, child: NodeIndex) -> Parents { let walk_edges = self.graph.neighbors_directed(child, pg::Incoming).detach(); Parents { - walk_edges: walk_edges, + walk_edges, _node: PhantomData, _edge: PhantomData, } @@ -570,7 +563,7 @@ where pub fn children(&self, parent: NodeIndex) -> Children { let walk_edges = self.graph.neighbors_directed(parent, pg::Outgoing).detach(); Children { - walk_edges: walk_edges, + walk_edges, _node: PhantomData, _edge: PhantomData, } @@ -614,12 +607,12 @@ where // Dag implementations. -impl Into> for StableDag +impl From> for StableDiGraph where Ix: IndexType, { - fn into(self) -> StableDiGraph { - self.into_graph() + fn from(val: StableDag) -> Self { + val.into_graph() } } diff --git a/src/stable_dag/serde.rs b/src/stable_dag/serde.rs index e3450e4..b034a58 100644 --- a/src/stable_dag/serde.rs +++ b/src/stable_dag/serde.rs @@ -31,8 +31,8 @@ where let graph = Deserialize::deserialize(deserializer)?; let cycle_state = DfsSpace::new(&graph); let dag = StableDag { - graph: graph, - cycle_state: cycle_state, + graph, + cycle_state, }; Ok(dag) } diff --git a/src/walker.rs b/src/walker.rs index 094967b..18d9eeb 100644 --- a/src/walker.rs +++ b/src/walker.rs @@ -23,7 +23,7 @@ where pub fn new(start: G::NodeId, recursive_fn: F) -> Self { Recursive { next: start, - recursive_fn: recursive_fn, + recursive_fn, _graph: PhantomData, } } @@ -42,7 +42,7 @@ where } } -impl<'a, G, F> Walker<&'a G> for Recursive +impl Walker<&G> for Recursive where G: GraphBase, F: FnMut(&G, G::NodeId) -> Option<(G::EdgeId, G::NodeId)>, @@ -429,9 +429,8 @@ where type Item = W::Item; #[inline] fn walk_next(&mut self, graph: G) -> Option { - self.walker.walk_next(graph).map(|item| { - (self.f)(graph, &item); - item + self.walker.walk_next(graph).inspect(|item| { + (self.f)(graph, item); }) } } From f5322ea4693b88e12da8a236ba266706dbdf394d Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Fri, 21 Feb 2025 16:57:37 +1300 Subject: [PATCH 3/9] Use `nightly` toolchain for rustfmt in github actions. The nightly toolchain has access to more rustfmt options. --- .github/workflows/daggy.yml | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/.github/workflows/daggy.yml b/.github/workflows/daggy.yml index 11989b9..0fe8cde 100644 --- a/.github/workflows/daggy.yml +++ b/.github/workflows/daggy.yml @@ -15,7 +15,7 @@ jobs: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: - toolchain: stable + toolchain: nightly components: rustfmt - name: Run rustfmt uses: actions-rs/cargo@v1 @@ -23,6 +23,20 @@ jobs: command: fmt args: --all -- --check + clippy: + name: Clippy + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: nightly + components: clippy + + - name: 'Run clippy' + run: cargo clippy --all-features -- -D warnings + cargo-test: runs-on: ubuntu-latest steps: From 487acb4ec600aa50459cb8164920792dcf11b17b Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Fri, 21 Feb 2025 16:59:49 +1300 Subject: [PATCH 4/9] Use `run:` instead of deprecated `actions-rs/cargo`. --- .github/workflows/daggy.yml | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/.github/workflows/daggy.yml b/.github/workflows/daggy.yml index 0fe8cde..bad9bbf 100644 --- a/.github/workflows/daggy.yml +++ b/.github/workflows/daggy.yml @@ -18,10 +18,7 @@ jobs: toolchain: nightly components: rustfmt - name: Run rustfmt - uses: actions-rs/cargo@v1 - with: - command: fmt - args: --all -- --check + run: cargo fmt --all -- --check clippy: name: Clippy @@ -43,10 +40,7 @@ jobs: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - name: cargo test - uses: actions-rs/cargo@v1 - with: - command: test - args: --verbose + run: cargo test --verbose cargo-test-all-features: runs-on: ubuntu-latest From a82f9c0cbd636191e1c02d5e1aed235221e3ced6 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Fri, 21 Feb 2025 17:04:16 +1300 Subject: [PATCH 5/9] Update crate version to `0.8.1`. --- Cargo.toml | 2 +- README.md | 6 +++--- src/lib.rs | 11 ++++++++++- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2fe3906..5b442cf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "daggy" -version = "0.8.0" +version = "0.8.1" authors = ["mitchmindtree "] description = "A directed acyclic graph data structure library. It is Implemented on top of petgraph's Graph data structure and attempts to follow similar conventions where suitable." readme = "README.md" diff --git a/README.md b/README.md index 3d93899..490dcc5 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ A [directed acyclic graph](https://en.wikipedia.org/wiki/Directed_acyclic_graph) data structure for Rust. -It is Implemented on top of [petgraph](https://github.com/petgraph/petgraph)'s [Graph](https://docs.rs/petgraph/latest/petgraph/graph/struct.Graph.html) data structure and attempts to follow similar conventions where suitable. +It is implemented on top of [petgraph](https://github.com/petgraph/petgraph)'s [Graph](https://docs.rs/petgraph/latest/petgraph/graph/struct.Graph.html) data structure and attempts to follow similar conventions where suitable. Usage @@ -11,11 +11,11 @@ Usage Please see the [tests directory](https://github.com/mitchmindtree/daggy/tree/master/tests) for some basic usage examples. -Use daggy in your project by adding it to your Cargo.toml dependencies like so: +Use daggy in your project by adding it to your `Cargo.toml` dependencies: ```toml [dependencies] -daggy = "*" +daggy = "0.8.1" ``` diff --git a/src/lib.rs b/src/lib.rs index c0a4585..54066cb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,10 +8,19 @@ //! methods behave similarly to iterator types, however **Walker**s do not require borrowing the //! graph. This means that we can still safely mutably borrow from the graph whilst we traverse it. //! -//! //! [1]: Dag //! [2]: petgraph //! [3]: petgraph::graph::Graph +//! +//! +//! ## Usage +//! +//! Use daggy in your project by adding it to your `Cargo.toml` dependencies: +//! +//! ```toml +//! [dependencies] +//! daggy = "0.8.1" +//! ``` #![forbid(unsafe_code)] #![warn(missing_docs)] From 8a332716dcdd9c077ebf68406a1cc40e35fb3a8f Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Fri, 21 Feb 2025 17:12:46 +1300 Subject: [PATCH 6/9] Include features in `README.md`. --- README.md | 13 ++++++++----- src/lib.rs | 6 ++++++ 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 490dcc5..5a4e353 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,7 @@ A [directed acyclic graph](https://en.wikipedia.org/wiki/Directed_acyclic_graph) It is implemented on top of [petgraph](https://github.com/petgraph/petgraph)'s [Graph](https://docs.rs/petgraph/latest/petgraph/graph/struct.Graph.html) data structure and attempts to follow similar conventions where suitable. -Usage ------ +## Usage Please see the [tests directory](https://github.com/mitchmindtree/daggy/tree/master/tests) for some basic usage examples. @@ -16,13 +15,17 @@ Use daggy in your project by adding it to your `Cargo.toml` dependencies: ```toml [dependencies] daggy = "0.8.1" + +# Enables the `StableDag` type. +daggy = { version = "0.8.1", features = ["stable_dag"] } + +# Allows the `Dag` to be serialized and deserialized. +daggy = { version = "0.8.1", features = ["serde-1"] } ``` -License -------- +## License Dual-licensed to be compatible with the petgraph and Rust projects. Licensed under the Apache License, Version 2.0 http://www.apache.org/licenses/LICENSE-2.0 or the MIT license http://opensource.org/licenses/MIT, at your option. This file may not be copied, modified, or distributed except according to those terms. - diff --git a/src/lib.rs b/src/lib.rs index 54066cb..6274581 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,6 +20,12 @@ //! ```toml //! [dependencies] //! daggy = "0.8.1" +//! +//! # Enables the `StableDag` type. +//! daggy = { version = "0.8.1", features = ["stable_dag"] } +//! +//! # Allows the `Dag` to be serialized and deserialized. +//! daggy = { version = "0.8.1", features = ["serde-1"] } //! ``` #![forbid(unsafe_code)] From 514d3ef1562289e37e16f8f54b68758c9c9b7722 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Fri, 21 Feb 2025 17:32:56 +1300 Subject: [PATCH 7/9] Add comment showing which edges are reduced in `transitive_reduce` test. --- tests/transitive_reduce.rs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/transitive_reduce.rs b/tests/transitive_reduce.rs index 4ca20d6..606c049 100644 --- a/tests/transitive_reduce.rs +++ b/tests/transitive_reduce.rs @@ -7,6 +7,32 @@ fn transitive_reduce() { let mut dag = Dag::<&str, &str>::new(); // construct example DAG from wikipedia + // + // Before reduce: + // + // ```text + // a -> b ----. + // | | + // |-> c ----|----. + // | \ | | + // | \ v | + // |------>> d | + // | \ v + // '----------->> e + // ``` + // + // After reduce: + // + // ```text + // a -> b ----. + // | | + // '-> c | + // \ | + // \ v + // '> d + // \ + // '> e + // ``` let a = dag.add_node("a"); @@ -26,7 +52,10 @@ fn transitive_reduce() { dag.transitive_reduce(vec![a]); + let mut edges = dag.graph().edge_weights().copied().collect::>(); + edges.sort(); assert_eq!(dag.edge_count(), 5); + assert_eq!(&edges, &["a->b", "a->c", "b->d", "c->d", "d->e"]); // test case where the alternate route from the parent is more than one node long @@ -36,5 +65,8 @@ fn transitive_reduce() { dag.transitive_reduce(vec![a]); + let mut edges = dag.graph().edge_weights().copied().collect::>(); + edges.sort(); assert_eq!(dag.edge_count(), 5); + assert_eq!(&edges, &["a->b", "a->c", "b->d", "c->d", "d->e"]); } From ee241c9c9e79e843be6e01cfbddeaee976dc7530 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Fri, 21 Feb 2025 17:39:24 +1300 Subject: [PATCH 8/9] Add example to `lib.rs` and `README.md`. --- README.md | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++-- src/lib.rs | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5a4e353..f106c04 100644 --- a/README.md +++ b/README.md @@ -8,8 +8,6 @@ It is implemented on top of [petgraph](https://github.com/petgraph/petgraph)'s [ ## Usage -Please see the [tests directory](https://github.com/mitchmindtree/daggy/tree/master/tests) for some basic usage examples. - Use daggy in your project by adding it to your `Cargo.toml` dependencies: ```toml @@ -23,6 +21,56 @@ daggy = { version = "0.8.1", features = ["stable_dag"] } daggy = { version = "0.8.1", features = ["serde-1"] } ``` +## Examples + +> Please see the [tests directory](https://github.com/mitchmindtree/daggy/tree/master/tests) for some basic usage examples. + +Transitive reduction: + +```rust +use daggy::Dag; + +let mut dag = Dag::<&str, &str>::new(); + +// Reduce edges: +// +// ```text +// # Before: | # After: +// | +// a -> b ----. | a -> b ----. +// | | | | | +// |-> c ----|----. | '-> c | +// | \ | | | \ | +// | \ v | | \ v +// |------>> d | | '> d +// | \ v | \ +// '----------->> e | '> e +// ``` + +let a = dag.add_node("a"); + +let (_, b) = dag.add_child(a, "a->b", "b"); +let (_, c) = dag.add_child(a, "a->c", "c"); +let (_, d) = dag.add_child(a, "a->d", "d"); +let (_, e) = dag.add_child(a, "a->e", "e"); + +dag.add_edge(b, d, "b->d").unwrap(); + +dag.add_edge(c, d, "c->d").unwrap(); +dag.add_edge(c, e, "c->e").unwrap(); + +dag.add_edge(d, e, "d->e").unwrap(); + +assert_eq!(dag.edge_count(), 8); + +dag.transitive_reduce(vec![a]); + +let mut edges = dag.graph().edge_weights().copied().collect::>(); +edges.sort(); +assert_eq!(dag.edge_count(), 5); +assert_eq!(&edges, &["a->b", "a->c", "b->d", "c->d", "d->e"]); +``` + ## License diff --git a/src/lib.rs b/src/lib.rs index 6274581..7d171c2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,6 +27,58 @@ //! # Allows the `Dag` to be serialized and deserialized. //! daggy = { version = "0.8.1", features = ["serde-1"] } //! ``` +//! +//! # Examples +//! +//! > Please see the [tests directory][4] for some basic usage examples. +//! +//! Transitive reduction: +//! +//! ```rust +//! use daggy::Dag; +//! +//! let mut dag = Dag::<&str, &str>::new(); +//! +//! // Reduce edges: +//! // +//! // ```text +//! // # Before: | # After: +//! // | +//! // a -> b ----. | a -> b ----. +//! // | | | | | +//! // |-> c ----|----. | '-> c | +//! // | \ | | | \ | +//! // | \ v | | \ v +//! // |------>> d | | '> d +//! // | \ v | \ +//! // '----------->> e | '> e +//! // ``` +//! +//! let a = dag.add_node("a"); +//! +//! let (_, b) = dag.add_child(a, "a->b", "b"); +//! let (_, c) = dag.add_child(a, "a->c", "c"); +//! let (_, d) = dag.add_child(a, "a->d", "d"); +//! let (_, e) = dag.add_child(a, "a->e", "e"); +//! +//! dag.add_edge(b, d, "b->d").unwrap(); +//! +//! dag.add_edge(c, d, "c->d").unwrap(); +//! dag.add_edge(c, e, "c->e").unwrap(); +//! +//! dag.add_edge(d, e, "d->e").unwrap(); +//! +//! assert_eq!(dag.edge_count(), 8); +//! +//! dag.transitive_reduce(vec![a]); +//! +//! let mut edges = dag.graph().edge_weights().copied().collect::>(); +//! edges.sort(); +//! assert_eq!(dag.edge_count(), 5); +//! assert_eq!(&edges, &["a->b", "a->c", "b->d", "c->d", "d->e"]); +//! ``` +//! +//! [4]: https://github.com/mitchmindtree/daggy/tree/master/tests #![forbid(unsafe_code)] #![warn(missing_docs)] From 0574d37284e28bc2a6c576fab73db7581b33e28f Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Fri, 21 Feb 2025 17:42:13 +1300 Subject: [PATCH 9/9] Run `rustfmt`. --- src/serde.rs | 5 +---- src/stable_dag/serde.rs | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/serde.rs b/src/serde.rs index 60d1792..860fd8c 100644 --- a/src/serde.rs +++ b/src/serde.rs @@ -30,10 +30,7 @@ where { let graph = Deserialize::deserialize(deserializer)?; let cycle_state = DfsSpace::new(&graph); - let dag = Dag { - graph, - cycle_state, - }; + let dag = Dag { graph, cycle_state }; Ok(dag) } } diff --git a/src/stable_dag/serde.rs b/src/stable_dag/serde.rs index b034a58..ae46269 100644 --- a/src/stable_dag/serde.rs +++ b/src/stable_dag/serde.rs @@ -30,10 +30,7 @@ where { let graph = Deserialize::deserialize(deserializer)?; let cycle_state = DfsSpace::new(&graph); - let dag = StableDag { - graph, - cycle_state, - }; + let dag = StableDag { graph, cycle_state }; Ok(dag) } }