From 9adeec82ac8b7e1a7de686fb2791b41b24f3c22c Mon Sep 17 00:00:00 2001 From: Edwin Navarro Date: Sun, 8 Jan 2023 14:39:11 -0800 Subject: [PATCH 1/5] Initial lollipop --- .../src/generators/lollipop_graph.rs | 287 ++++++++++++++++++ rustworkx-core/src/generators/utils.rs | 8 + 2 files changed, 295 insertions(+) create mode 100644 rustworkx-core/src/generators/lollipop_graph.rs diff --git a/rustworkx-core/src/generators/lollipop_graph.rs b/rustworkx-core/src/generators/lollipop_graph.rs new file mode 100644 index 000000000..c11f42ac6 --- /dev/null +++ b/rustworkx-core/src/generators/lollipop_graph.rs @@ -0,0 +1,287 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +use std::hash::Hash; + +use petgraph::data::{Build, Create}; +use petgraph::visit::{Data, NodeIndexable}; + +use super::utils::pairwise; +use super::InvalidInputError; + +/// Generate a llolipop graph +/// +/// Arguments: +/// +/// Generate an undirected lollipop graph where a mesh graph is connected to a +/// path. +/// +/// If neither `num_path_nodes` nor `path_weights` (both described +/// below) are specified then this is equivalent to +/// :func:`~rustworkx.generators.mesh_graph` +/// +/// * `num_mesh_nodes` - The number of nodes to generate the mesh graph +/// with. Node weights will be None if this is specified. If both +/// `num_mesh_nodes` and ``mesh_weights`` are set this will be ignored and +/// `mesh_weights` will be used. +/// * `num_path_nodes` - The number of nodes to generate the path +/// with. Node weights will be None if this is specified. If both +/// `num_path_nodes` and `path_weights` are set this will be ignored and +/// `path_weights` will be used. +/// * `mesh_weights` - A list of node weights for the mesh graph. If both +/// `num_mesh_nodes` and `mesh_weights` are set `num_mesh_nodes` will +/// be ignored and `mesh_weights` will be used. +/// * `path_weights` - A list of node weights for the path. If both +/// `num_path_nodes` and `path_weights` are set `num_path_nodes` will +/// be ignored and `path_weights` will be used. +/// * `default_node_weight` - A callable that will return the weight to use +/// for newly created nodes. This is ignored if `weights` is specified, +/// as the weights from that argument will be used instead. +/// * `default_edge_weight` - A callable that will return the weight object +/// to use for newly created edges. +/// +/// # Example +/// ```rust +/// use rustworkx_core::petgraph; +/// use rustworkx_core::generators::llolipop_graph; +/// use rustworkx_core::petgraph::visit::EdgeRef; +/// +/// let g: petgraph::graph::UnGraph<(), ()> = llolipop_graph( +/// Some(3), +/// Some(3), +/// None, +/// || {()}, +/// || {()}, +/// false +/// ).unwrap(); +/// assert_eq!( +/// vec![(0, 3), (0, 1), (1, 4), (1, 2), (2, 5), +/// (3, 6), (3, 4), (4, 7), (4, 5), (5, 8), (6, 7), (7, 8)], +/// g.edge_references() +/// .map(|edge| (edge.source().index(), edge.target().index())) +/// .collect::>(), +/// ) +/// ``` +pub fn llolipop_graph( + num_mesh_nodes: Option, + num_path_nodes: Option, + mesh_weights: Option>, + path_weights: Option>, + mut default_node_weight: F, + mut default_edge_weight: H, + bidirectional: bool, +) -> Result +where + G: Build + Create + Data + NodeIndexable, + F: FnMut() -> T, + H: FnMut() -> M, + G::NodeId: Eq + Hash; +{ + let mut graph = complete_graph(num_mesh_nodes, mesh_weights); + if num_path_nodes.is_none() && path_weights.is_none() { + return Ok(graph); + } + let meshlen = graph.num_nodes(); + + let path_nodes: Vec = match path_weights { + Some(path_weights) => path_weights + .into_iter() + .map(|weight| graph.add_node(weight)) + .collect(), + None => (0..num_path_nodes.unwrap()) + .map(|_| graph.graph.add_node(default_node_weight())) + .collect(), + }; + + let pathlen = path_nodes.len(); + if pathlen > 0 { + graph.add_edge( + graph.from_index(meshlen - 1), + graph.from_index(meshlen), + default_edge_weight(), + ); + for (node_a, node_b) in pairwise(path_nodes) { + match node_a { + Some(node_a) => graph.add_edge(node_a, node_b, default_edge_weight()), + None => continue, + }; + } + } + Ok(graph) +} + +#[cfg(test)] +mod tests { + use crate::generators::llolipop_graph; + use crate::generators::InvalidInputError; + use crate::petgraph::visit::EdgeRef; + + #[test] + fn test_directed_llolipop_simple_row_col() { + let g: petgraph::graph::DiGraph<(), ()> = + llolipop_graph(Some(3), Some(3), None, || (), || (), false).unwrap(); + assert_eq!( + vec![ + (0, 3), + (0, 1), + (1, 4), + (1, 2), + (2, 5), + (3, 6), + (3, 4), + (4, 7), + (4, 5), + (5, 8), + (6, 7), + (7, 8) + ], + g.edge_references() + .map(|edge| (edge.source().index(), edge.target().index())) + .collect::>(), + ); + assert_eq!(g.edge_count(), 12); + } + + #[test] + fn test_llolipop_simple_row_col() { + let g: petgraph::graph::UnGraph<(), ()> = + llolipop_graph(Some(3), Some(3), None, || (), || (), false).unwrap(); + assert_eq!( + vec![ + (0, 3), + (0, 1), + (1, 4), + (1, 2), + (2, 5), + (3, 6), + (3, 4), + (4, 7), + (4, 5), + (5, 8), + (6, 7), + (7, 8) + ], + g.edge_references() + .map(|edge| (edge.source().index(), edge.target().index())) + .collect::>(), + ); + assert_eq!(g.edge_count(), 12); + } + + #[test] + fn test_directed_llolipop_weights() { + let g: petgraph::graph::DiGraph = llolipop_graph( + Some(2), + Some(3), + Some(vec![0, 1, 2, 3, 4, 5]), + || 4, + || (), + false, + ) + .unwrap(); + assert_eq!( + vec![(0, 3), (0, 1), (1, 4), (1, 2), (2, 5), (3, 4), (4, 5),], + g.edge_references() + .map(|edge| (edge.source().index(), edge.target().index())) + .collect::>(), + ); + assert_eq!(g.edge_count(), 7); + assert_eq!( + vec![0, 1, 2, 3, 4, 5], + g.node_weights().copied().collect::>(), + ); + } + + #[test] + fn test_directed_llolipop_more_weights() { + let g: petgraph::graph::DiGraph = llolipop_graph( + Some(2), + Some(3), + Some(vec![0, 1, 2, 3, 4, 5, 6, 7]), + || 4, + || (), + false, + ) + .unwrap(); + assert_eq!( + vec![(0, 3), (0, 1), (1, 4), (1, 2), (2, 5), (3, 4), (4, 5),], + g.edge_references() + .map(|edge| (edge.source().index(), edge.target().index())) + .collect::>(), + ); + assert_eq!(g.edge_count(), 7); + assert_eq!( + vec![0, 1, 2, 3, 4, 5], + g.node_weights().copied().collect::>(), + ); + } + + #[test] + fn test_directed_llolipop_less_weights() { + let g: petgraph::graph::DiGraph = + llolipop_graph(Some(2), Some(3), Some(vec![0, 1, 2, 3]), || 6, || (), false).unwrap(); + assert_eq!( + vec![(0, 3), (0, 1), (1, 4), (1, 2), (2, 5), (3, 4), (4, 5),], + g.edge_references() + .map(|edge| (edge.source().index(), edge.target().index())) + .collect::>(), + ); + assert_eq!(g.edge_count(), 7); + assert_eq!( + vec![0, 1, 2, 3, 6, 6], + g.node_weights().copied().collect::>(), + ); + } + + #[test] + fn test_directed_llolipop_bidirectional() { + let g: petgraph::graph::DiGraph<(), ()> = + llolipop_graph(Some(2), Some(3), None, || (), || (), true).unwrap(); + assert_eq!( + vec![ + (0, 3), + (3, 0), + (0, 1), + (1, 0), + (1, 4), + (4, 1), + (1, 2), + (2, 1), + (2, 5), + (5, 2), + (3, 4), + (4, 3), + (4, 5), + (5, 4), + ], + g.edge_references() + .map(|edge| (edge.source().index(), edge.target().index())) + .collect::>(), + ); + assert_eq!(g.edge_count(), 14); + } + + #[test] + fn test_llolipop_error() { + match llolipop_graph::, (), _, _, ()>( + None, + None, + None, + || (), + || (), + false, + ) { + Ok(_) => panic!("Returned a non-error"), + Err(e) => assert_eq!(e, InvalidInputError), + }; + } +} diff --git a/rustworkx-core/src/generators/utils.rs b/rustworkx-core/src/generators/utils.rs index b63ab7043..9df82e6e4 100644 --- a/rustworkx-core/src/generators/utils.rs +++ b/rustworkx-core/src/generators/utils.rs @@ -18,3 +18,11 @@ pub fn get_num_nodes(num_nodes: &Option, weights: &Option>) -> num_nodes.unwrap() } } + +pub fn pairwise(right: I) -> impl Iterator, I::Item)> +where + I: IntoIterator + Clone, +{ + let left = iter::once(None).chain(right.clone().into_iter().map(Some)); + left.zip(right) +} From 7b686624d8abee54265985e34f04e619e9a5e843 Mon Sep 17 00:00:00 2001 From: Edwin Navarro Date: Sun, 8 Jan 2023 14:55:50 -0800 Subject: [PATCH 2/5] First tests --- .../src/generators/lollipop_graph.rs | 48 +++++++++---------- rustworkx-core/src/generators/mod.rs | 2 + rustworkx-core/src/generators/utils.rs | 2 + 3 files changed, 28 insertions(+), 24 deletions(-) diff --git a/rustworkx-core/src/generators/lollipop_graph.rs b/rustworkx-core/src/generators/lollipop_graph.rs index c11f42ac6..4bdc8b03f 100644 --- a/rustworkx-core/src/generators/lollipop_graph.rs +++ b/rustworkx-core/src/generators/lollipop_graph.rs @@ -18,7 +18,7 @@ use petgraph::visit::{Data, NodeIndexable}; use super::utils::pairwise; use super::InvalidInputError; -/// Generate a llolipop graph +/// Generate a lollipop graph /// /// Arguments: /// @@ -52,10 +52,10 @@ use super::InvalidInputError; /// # Example /// ```rust /// use rustworkx_core::petgraph; -/// use rustworkx_core::generators::llolipop_graph; +/// use rustworkx_core::generators::lollipop_graph; /// use rustworkx_core::petgraph::visit::EdgeRef; /// -/// let g: petgraph::graph::UnGraph<(), ()> = llolipop_graph( +/// let g: petgraph::graph::UnGraph<(), ()> = lollipop_graph( /// Some(3), /// Some(3), /// None, @@ -71,11 +71,11 @@ use super::InvalidInputError; /// .collect::>(), /// ) /// ``` -pub fn llolipop_graph( +pub fn lollipop_graph( num_mesh_nodes: Option, num_path_nodes: Option, - mesh_weights: Option>, - path_weights: Option>, + mesh_weights: Option>, + path_weights: Option>, mut default_node_weight: F, mut default_edge_weight: H, bidirectional: bool, @@ -84,13 +84,13 @@ where G: Build + Create + Data + NodeIndexable, F: FnMut() -> T, H: FnMut() -> M, - G::NodeId: Eq + Hash; + G::NodeId: Eq + Hash, { let mut graph = complete_graph(num_mesh_nodes, mesh_weights); if num_path_nodes.is_none() && path_weights.is_none() { return Ok(graph); } - let meshlen = graph.num_nodes(); + let meshlen = graph.node_count(); let path_nodes: Vec = match path_weights { Some(path_weights) => path_weights @@ -98,7 +98,7 @@ where .map(|weight| graph.add_node(weight)) .collect(), None => (0..num_path_nodes.unwrap()) - .map(|_| graph.graph.add_node(default_node_weight())) + .map(|_| graph.add_node(default_node_weight())) .collect(), }; @@ -121,14 +121,14 @@ where #[cfg(test)] mod tests { - use crate::generators::llolipop_graph; + use crate::generators::lollipop_graph; use crate::generators::InvalidInputError; use crate::petgraph::visit::EdgeRef; #[test] - fn test_directed_llolipop_simple_row_col() { + fn test_directed_lollipop_simple_row_col() { let g: petgraph::graph::DiGraph<(), ()> = - llolipop_graph(Some(3), Some(3), None, || (), || (), false).unwrap(); + lollipop_graph(Some(3), Some(3), None, || (), || (), false).unwrap(); assert_eq!( vec![ (0, 3), @@ -152,9 +152,9 @@ mod tests { } #[test] - fn test_llolipop_simple_row_col() { + fn test_lollipop_simple_row_col() { let g: petgraph::graph::UnGraph<(), ()> = - llolipop_graph(Some(3), Some(3), None, || (), || (), false).unwrap(); + lollipop_graph(Some(3), Some(3), None, || (), || (), false).unwrap(); assert_eq!( vec![ (0, 3), @@ -178,8 +178,8 @@ mod tests { } #[test] - fn test_directed_llolipop_weights() { - let g: petgraph::graph::DiGraph = llolipop_graph( + fn test_directed_lollipop_weights() { + let g: petgraph::graph::DiGraph = lollipop_graph( Some(2), Some(3), Some(vec![0, 1, 2, 3, 4, 5]), @@ -202,8 +202,8 @@ mod tests { } #[test] - fn test_directed_llolipop_more_weights() { - let g: petgraph::graph::DiGraph = llolipop_graph( + fn test_directed_lollipop_more_weights() { + let g: petgraph::graph::DiGraph = lollipop_graph( Some(2), Some(3), Some(vec![0, 1, 2, 3, 4, 5, 6, 7]), @@ -226,9 +226,9 @@ mod tests { } #[test] - fn test_directed_llolipop_less_weights() { + fn test_directed_lollipop_less_weights() { let g: petgraph::graph::DiGraph = - llolipop_graph(Some(2), Some(3), Some(vec![0, 1, 2, 3]), || 6, || (), false).unwrap(); + lollipop_graph(Some(2), Some(3), Some(vec![0, 1, 2, 3]), || 6, || (), false).unwrap(); assert_eq!( vec![(0, 3), (0, 1), (1, 4), (1, 2), (2, 5), (3, 4), (4, 5),], g.edge_references() @@ -243,9 +243,9 @@ mod tests { } #[test] - fn test_directed_llolipop_bidirectional() { + fn test_directed_lollipop_bidirectional() { let g: petgraph::graph::DiGraph<(), ()> = - llolipop_graph(Some(2), Some(3), None, || (), || (), true).unwrap(); + lollipop_graph(Some(2), Some(3), None, || (), || (), true).unwrap(); assert_eq!( vec![ (0, 3), @@ -271,8 +271,8 @@ mod tests { } #[test] - fn test_llolipop_error() { - match llolipop_graph::, (), _, _, ()>( + fn test_lollipop_error() { + match lollipop_graph::, (), _, _, ()>( None, None, None, diff --git a/rustworkx-core/src/generators/mod.rs b/rustworkx-core/src/generators/mod.rs index b4618ad91..e24325a22 100644 --- a/rustworkx-core/src/generators/mod.rs +++ b/rustworkx-core/src/generators/mod.rs @@ -14,6 +14,7 @@ mod cycle_graph; mod grid_graph; +mod lollipop_graph; mod path_graph; mod star_graph; @@ -36,5 +37,6 @@ impl fmt::Display for InvalidInputError { pub use cycle_graph::cycle_graph; pub use grid_graph::grid_graph; +pub use lollipop_graph::lollipop_graph; pub use path_graph::path_graph; pub use star_graph::star_graph; diff --git a/rustworkx-core/src/generators/utils.rs b/rustworkx-core/src/generators/utils.rs index 9df82e6e4..3eb178fb5 100644 --- a/rustworkx-core/src/generators/utils.rs +++ b/rustworkx-core/src/generators/utils.rs @@ -10,6 +10,8 @@ // License for the specific language governing permissions and limitations // under the License. +use std::iter; + #[inline] pub fn get_num_nodes(num_nodes: &Option, weights: &Option>) -> usize { if weights.is_some() { From 35129e3850a32c35d5dfd36d9e04be7c377deee5 Mon Sep 17 00:00:00 2001 From: Edwin Navarro Date: Mon, 9 Jan 2023 07:08:38 -0800 Subject: [PATCH 3/5] Redo based on barbell --- .../src/generators/lollipop_graph.rs | 250 +++++++----------- rustworkx-core/src/generators/mod.rs | 2 + src/generators.rs | 51 ++-- 3 files changed, 116 insertions(+), 187 deletions(-) diff --git a/rustworkx-core/src/generators/lollipop_graph.rs b/rustworkx-core/src/generators/lollipop_graph.rs index 4bdc8b03f..1d70e59fc 100644 --- a/rustworkx-core/src/generators/lollipop_graph.rs +++ b/rustworkx-core/src/generators/lollipop_graph.rs @@ -13,10 +13,11 @@ use std::hash::Hash; use petgraph::data::{Build, Create}; -use petgraph::visit::{Data, NodeIndexable}; +use petgraph::visit::{Data, GraphProp, NodeIndexable}; use super::utils::pairwise; use super::InvalidInputError; +use crate::generators::complete_graph; /// Generate a lollipop graph /// @@ -55,17 +56,27 @@ use super::InvalidInputError; /// use rustworkx_core::generators::lollipop_graph; /// use rustworkx_core::petgraph::visit::EdgeRef; /// +/// let expected_edge_list = vec![ +/// (0, 1), +/// (0, 2), +/// (0, 3), +/// (1, 2), +/// (1, 3), +/// (2, 3), +/// (3, 4), +/// (4, 5), +/// (5, 6), +/// ]; /// let g: petgraph::graph::UnGraph<(), ()> = lollipop_graph( +/// Some(4), /// Some(3), -/// Some(3), +/// None, /// None, /// || {()}, /// || {()}, -/// false /// ).unwrap(); /// assert_eq!( -/// vec![(0, 3), (0, 1), (1, 4), (1, 2), (2, 5), -/// (3, 6), (3, 4), (4, 7), (4, 5), (5, 8), (6, 7), (7, 8)], +/// expected_edge_list, /// g.edge_references() /// .map(|edge| (edge.source().index(), edge.target().index())) /// .collect::>(), @@ -78,15 +89,70 @@ pub fn lollipop_graph( path_weights: Option>, mut default_node_weight: F, mut default_edge_weight: H, - bidirectional: bool, ) -> Result where - G: Build + Create + Data + NodeIndexable, + G: Build + Create + Data + NodeIndexable + GraphProp, F: FnMut() -> T, H: FnMut() -> M, G::NodeId: Eq + Hash, { - let mut graph = complete_graph(num_mesh_nodes, mesh_weights); + if num_mesh_nodes.is_none() { + return Err(PyIndexError::new_err("num_mesh_nodes not specified")); + } + + let mut left_mesh = StableUnGraph::::default(); + let mesh_nodes: Vec = (0..num_mesh_nodes.unwrap()) + .map(|_| left_mesh.add_node(py.None())) + .collect(); + let mut nodelen = mesh_nodes.len(); + for i in 0..nodelen - 1 { + for j in i + 1..nodelen { + left_mesh.add_edge(mesh_nodes[i], mesh_nodes[j], py.None()); + } + } + + let right_mesh = left_mesh.clone(); + + if let Some(num_nodes) = num_path_nodes { + let path_nodes: Vec = (0..num_nodes) + .map(|_| left_mesh.add_node(py.None())) + .collect(); + left_mesh.add_edge( + NodeIndex::new(nodelen - 1), + NodeIndex::new(nodelen), + py.None(), + ); + + nodelen += path_nodes.len(); + + for (node_a, node_b) in pairwise(path_nodes) { + match node_a { + Some(node_a) => left_mesh.add_edge(node_a, node_b, py.None()), + None => continue, + }; + } + } + + for node in right_mesh.node_indices() { + let new_node = &right_mesh[node]; + left_mesh.add_node(new_node.clone_ref(py)); + } + left_mesh.add_edge( + NodeIndex::new(nodelen - 1), + NodeIndex::new(nodelen), + py.None(), + ); + for edge in right_mesh.edge_references() { + let new_source = NodeIndex::new(nodelen + edge.source().index()); + let new_target = NodeIndex::new(nodelen + edge.target().index()); + let weight = edge.weight(); + left_mesh.add_edge(new_source, new_target, weight.clone_ref(py)); + } + + + + + if num_path_nodes.is_none() && path_weights.is_none() { return Ok(graph); } @@ -124,164 +190,34 @@ mod tests { use crate::generators::lollipop_graph; use crate::generators::InvalidInputError; use crate::petgraph::visit::EdgeRef; - - #[test] - fn test_directed_lollipop_simple_row_col() { - let g: petgraph::graph::DiGraph<(), ()> = - lollipop_graph(Some(3), Some(3), None, || (), || (), false).unwrap(); - assert_eq!( - vec![ - (0, 3), - (0, 1), - (1, 4), - (1, 2), - (2, 5), - (3, 6), - (3, 4), - (4, 7), - (4, 5), - (5, 8), - (6, 7), - (7, 8) - ], - g.edge_references() - .map(|edge| (edge.source().index(), edge.target().index())) - .collect::>(), - ); - assert_eq!(g.edge_count(), 12); - } - #[test] - fn test_lollipop_simple_row_col() { + fn test_lollipop_mesh_path() { + let expected_edge_list = vec![ + (0, 1), + (0, 2), + (0, 3), + (1, 2), + (1, 3), + (2, 3), + (3, 4), + (4, 5), + (5, 6), + ]; let g: petgraph::graph::UnGraph<(), ()> = - lollipop_graph(Some(3), Some(3), None, || (), || (), false).unwrap(); + lollipop_graph(Some(4), Some(3), None, None, || (), || ()).unwrap(); assert_eq!( - vec![ - (0, 3), - (0, 1), - (1, 4), - (1, 2), - (2, 5), - (3, 6), - (3, 4), - (4, 7), - (4, 5), - (5, 8), - (6, 7), - (7, 8) - ], + expected_edge_list, g.edge_references() .map(|edge| (edge.source().index(), edge.target().index())) .collect::>(), ); - assert_eq!(g.edge_count(), 12); } #[test] - fn test_directed_lollipop_weights() { - let g: petgraph::graph::DiGraph = lollipop_graph( - Some(2), - Some(3), - Some(vec![0, 1, 2, 3, 4, 5]), - || 4, - || (), - false, - ) - .unwrap(); - assert_eq!( - vec![(0, 3), (0, 1), (1, 4), (1, 2), (2, 5), (3, 4), (4, 5),], - g.edge_references() - .map(|edge| (edge.source().index(), edge.target().index())) - .collect::>(), - ); - assert_eq!(g.edge_count(), 7); - assert_eq!( - vec![0, 1, 2, 3, 4, 5], - g.node_weights().copied().collect::>(), - ); - } - - #[test] - fn test_directed_lollipop_more_weights() { - let g: petgraph::graph::DiGraph = lollipop_graph( - Some(2), - Some(3), - Some(vec![0, 1, 2, 3, 4, 5, 6, 7]), - || 4, - || (), - false, - ) - .unwrap(); - assert_eq!( - vec![(0, 3), (0, 1), (1, 4), (1, 2), (2, 5), (3, 4), (4, 5),], - g.edge_references() - .map(|edge| (edge.source().index(), edge.target().index())) - .collect::>(), - ); - assert_eq!(g.edge_count(), 7); - assert_eq!( - vec![0, 1, 2, 3, 4, 5], - g.node_weights().copied().collect::>(), - ); - } - - #[test] - fn test_directed_lollipop_less_weights() { - let g: petgraph::graph::DiGraph = - lollipop_graph(Some(2), Some(3), Some(vec![0, 1, 2, 3]), || 6, || (), false).unwrap(); - assert_eq!( - vec![(0, 3), (0, 1), (1, 4), (1, 2), (2, 5), (3, 4), (4, 5),], - g.edge_references() - .map(|edge| (edge.source().index(), edge.target().index())) - .collect::>(), - ); - assert_eq!(g.edge_count(), 7); - assert_eq!( - vec![0, 1, 2, 3, 6, 6], - g.node_weights().copied().collect::>(), - ); - } - - #[test] - fn test_directed_lollipop_bidirectional() { - let g: petgraph::graph::DiGraph<(), ()> = - lollipop_graph(Some(2), Some(3), None, || (), || (), true).unwrap(); - assert_eq!( - vec![ - (0, 3), - (3, 0), - (0, 1), - (1, 0), - (1, 4), - (4, 1), - (1, 2), - (2, 1), - (2, 5), - (5, 2), - (3, 4), - (4, 3), - (4, 5), - (5, 4), - ], - g.edge_references() - .map(|edge| (edge.source().index(), edge.target().index())) - .collect::>(), - ); - assert_eq!(g.edge_count(), 14); - } - - #[test] - fn test_lollipop_error() { - match lollipop_graph::, (), _, _, ()>( - None, - None, - None, - || (), - || (), - false, - ) { - Ok(_) => panic!("Returned a non-error"), - Err(e) => assert_eq!(e, InvalidInputError), - }; + fn test_lollipop_0_mesh_none_path() { + let g: petgraph::graph::UnGraph<(), ()> = + lollipop_graph(Some(0), None, None, None, || (), || ()).unwrap(); + assert_eq!(g.node_count(), 0); + assert_eq!(g.edge_count(), 0); } } diff --git a/rustworkx-core/src/generators/mod.rs b/rustworkx-core/src/generators/mod.rs index e24325a22..5cca999e1 100644 --- a/rustworkx-core/src/generators/mod.rs +++ b/rustworkx-core/src/generators/mod.rs @@ -12,6 +12,7 @@ //! This module contains generator functions for building graphs +mod complete_graph; mod cycle_graph; mod grid_graph; mod lollipop_graph; @@ -35,6 +36,7 @@ impl fmt::Display for InvalidInputError { } } +pub use complete_graph::complete_graph; pub use cycle_graph::cycle_graph; pub use grid_graph::grid_graph; pub use lollipop_graph::lollipop_graph; diff --git a/src/generators.rs b/src/generators.rs index acba83abf..f11310474 100644 --- a/src/generators.rs +++ b/src/generators.rs @@ -1910,37 +1910,28 @@ pub fn lollipop_graph( path_weights: Option>, multigraph: bool, ) -> PyResult { - let mut graph = mesh_graph(py, num_mesh_nodes, mesh_weights, multigraph)?; - if num_path_nodes.is_none() && path_weights.is_none() { - return Ok(graph); - } - let meshlen = graph.num_nodes(); - - let path_nodes: Vec = match path_weights { - Some(path_weights) => path_weights - .into_iter() - .map(|node| graph.graph.add_node(node)) - .collect(), - None => (0..num_path_nodes.unwrap()) - .map(|_| graph.graph.add_node(py.None())) - .collect(), - }; - - let pathlen = path_nodes.len(); - if pathlen > 0 { - graph.graph.add_edge( - NodeIndex::new(meshlen - 1), - NodeIndex::new(meshlen), - py.None(), - ); - for (node_a, node_b) in pairwise(path_nodes) { - match node_a { - Some(node_a) => graph.graph.add_edge(node_a, node_b, py.None()), - None => continue, - }; + let default_fn = || py.None(); + let graph: StablePyGraph = match core_generators::lollipop_graph( + num_mesh_nodes, + num_path_nodes, + mesh_weights, + path_weights, + default_fn, + default_fn, + ) { + Ok(graph) => graph, + Err(_) => { + return Err(PyIndexError::new_err( + "num_nodes and weights list not specified", + )) } - } - Ok(graph) + }; + Ok(graph::PyGraph { + graph, + node_removed: false, + multigraph, + attrs: py.None(), + }) } /// Generate a generalized Petersen graph :math:`G(n, k)` with :math:`2n` From e18677a2a9bb9d717b7caabcdc8303d2387b5d5c Mon Sep 17 00:00:00 2001 From: Edwin Navarro Date: Mon, 9 Jan 2023 09:46:22 -0800 Subject: [PATCH 4/5] Make like barbell and tests --- .../src/generators/lollipop_graph.rs | 111 +++++++----------- rustworkx-core/src/generators/mod.rs | 2 - 2 files changed, 44 insertions(+), 69 deletions(-) diff --git a/rustworkx-core/src/generators/lollipop_graph.rs b/rustworkx-core/src/generators/lollipop_graph.rs index 1d70e59fc..a8004d1c5 100644 --- a/rustworkx-core/src/generators/lollipop_graph.rs +++ b/rustworkx-core/src/generators/lollipop_graph.rs @@ -17,7 +17,6 @@ use petgraph::visit::{Data, GraphProp, NodeIndexable}; use super::utils::pairwise; use super::InvalidInputError; -use crate::generators::complete_graph; /// Generate a lollipop graph /// @@ -32,7 +31,7 @@ use crate::generators::complete_graph; /// /// * `num_mesh_nodes` - The number of nodes to generate the mesh graph /// with. Node weights will be None if this is specified. If both -/// `num_mesh_nodes` and ``mesh_weights`` are set this will be ignored and +/// `num_mesh_nodes` and `mesh_weights` are set this will be ignored and /// `mesh_weights` will be used. /// * `num_path_nodes` - The number of nodes to generate the path /// with. Node weights will be None if this is specified. If both @@ -96,78 +95,49 @@ where H: FnMut() -> M, G::NodeId: Eq + Hash, { - if num_mesh_nodes.is_none() { - return Err(PyIndexError::new_err("num_mesh_nodes not specified")); + if num_mesh_nodes.is_none() && mesh_weights.is_none() { + return Err(InvalidInputError {}); } - - let mut left_mesh = StableUnGraph::::default(); - let mesh_nodes: Vec = (0..num_mesh_nodes.unwrap()) - .map(|_| left_mesh.add_node(py.None())) - .collect(); - let mut nodelen = mesh_nodes.len(); - for i in 0..nodelen - 1 { - for j in i + 1..nodelen { - left_mesh.add_edge(mesh_nodes[i], mesh_nodes[j], py.None()); - } + let num_nodes: usize; + if num_mesh_nodes.is_none() { + num_nodes = mesh_weights.as_ref().unwrap().len(); + } else { + num_nodes = num_mesh_nodes.unwrap(); } + let num_edges = (num_nodes * (num_nodes - 1)) / 2; + let mut graph = G::with_capacity(num_nodes, num_edges); - let right_mesh = left_mesh.clone(); - - if let Some(num_nodes) = num_path_nodes { - let path_nodes: Vec = (0..num_nodes) - .map(|_| left_mesh.add_node(py.None())) - .collect(); - left_mesh.add_edge( - NodeIndex::new(nodelen - 1), - NodeIndex::new(nodelen), - py.None(), - ); - - nodelen += path_nodes.len(); + let mesh_nodes: Vec = match mesh_weights { + Some(mesh_weights) => mesh_weights + .into_iter() + .map(|weight| graph.add_node(weight)) + .collect(), + None => (0..num_mesh_nodes.unwrap()) + .map(|_| graph.add_node(default_node_weight())) + .collect(), + }; - for (node_a, node_b) in pairwise(path_nodes) { - match node_a { - Some(node_a) => left_mesh.add_edge(node_a, node_b, py.None()), - None => continue, - }; + let meshlen = mesh_nodes.len(); + for i in 0..meshlen - 1 { + for j in i + 1..meshlen { + graph.add_edge(mesh_nodes[i], mesh_nodes[j], default_edge_weight()); } } - - for node in right_mesh.node_indices() { - let new_node = &right_mesh[node]; - left_mesh.add_node(new_node.clone_ref(py)); - } - left_mesh.add_edge( - NodeIndex::new(nodelen - 1), - NodeIndex::new(nodelen), - py.None(), - ); - for edge in right_mesh.edge_references() { - let new_source = NodeIndex::new(nodelen + edge.source().index()); - let new_target = NodeIndex::new(nodelen + edge.target().index()); - let weight = edge.weight(); - left_mesh.add_edge(new_source, new_target, weight.clone_ref(py)); - } - - - - - - if num_path_nodes.is_none() && path_weights.is_none() { - return Ok(graph); - } - let meshlen = graph.node_count(); - let path_nodes: Vec = match path_weights { Some(path_weights) => path_weights .into_iter() .map(|weight| graph.add_node(weight)) .collect(), - None => (0..num_path_nodes.unwrap()) - .map(|_| graph.add_node(default_node_weight())) - .collect(), + None => { + if num_path_nodes.is_some() { + (0..num_path_nodes.unwrap()) + .map(|_| graph.add_node(default_node_weight())) + .collect() + } else { + vec![] + } + } }; - let pathlen = path_nodes.len(); if pathlen > 0 { graph.add_edge( @@ -214,10 +184,17 @@ mod tests { } #[test] - fn test_lollipop_0_mesh_none_path() { - let g: petgraph::graph::UnGraph<(), ()> = - lollipop_graph(Some(0), None, None, None, || (), || ()).unwrap(); - assert_eq!(g.node_count(), 0); - assert_eq!(g.edge_count(), 0); + fn test_lollipop_none_mesh() { + match lollipop_graph::, (), _, _, ()>( + None, + None, + None, + None, + || (), + || (), + ) { + Ok(_) => panic!("Returned a non-error"), + Err(e) => assert_eq!(e, InvalidInputError), + }; } } diff --git a/rustworkx-core/src/generators/mod.rs b/rustworkx-core/src/generators/mod.rs index 5cca999e1..e24325a22 100644 --- a/rustworkx-core/src/generators/mod.rs +++ b/rustworkx-core/src/generators/mod.rs @@ -12,7 +12,6 @@ //! This module contains generator functions for building graphs -mod complete_graph; mod cycle_graph; mod grid_graph; mod lollipop_graph; @@ -36,7 +35,6 @@ impl fmt::Display for InvalidInputError { } } -pub use complete_graph::complete_graph; pub use cycle_graph::cycle_graph; pub use grid_graph::grid_graph; pub use lollipop_graph::lollipop_graph; From 9fbb7b38737f8d189ce0493a58f9e769ff073b6a Mon Sep 17 00:00:00 2001 From: Edwin Navarro Date: Mon, 9 Jan 2023 10:29:15 -0800 Subject: [PATCH 5/5] Clippy --- rustworkx-core/src/generators/lollipop_graph.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/rustworkx-core/src/generators/lollipop_graph.rs b/rustworkx-core/src/generators/lollipop_graph.rs index a8004d1c5..afbdfc69e 100644 --- a/rustworkx-core/src/generators/lollipop_graph.rs +++ b/rustworkx-core/src/generators/lollipop_graph.rs @@ -99,10 +99,10 @@ where return Err(InvalidInputError {}); } let num_nodes: usize; - if num_mesh_nodes.is_none() { - num_nodes = mesh_weights.as_ref().unwrap().len(); + if let Some(mesh_nodes) = num_mesh_nodes { + num_nodes = mesh_nodes; } else { - num_nodes = num_mesh_nodes.unwrap(); + num_nodes = mesh_weights.as_ref().unwrap().len(); } let num_edges = (num_nodes * (num_nodes - 1)) / 2; let mut graph = G::with_capacity(num_nodes, num_edges); @@ -129,8 +129,8 @@ where .map(|weight| graph.add_node(weight)) .collect(), None => { - if num_path_nodes.is_some() { - (0..num_path_nodes.unwrap()) + if let Some(num_path) = num_path_nodes { + (0..num_path) .map(|_| graph.add_node(default_node_weight())) .collect() } else {