From ef6adcd8abc6ebc2ed10135eef6c504e0a3cd2fd Mon Sep 17 00:00:00 2001 From: Edwin Navarro Date: Sat, 7 Jan 2023 10:18:32 -0800 Subject: [PATCH 1/5] Initial bi tree --- .../src/generators/binomial_tree_graph.rs | 204 ++++++++++++++++++ rustworkx-core/src/generators/mod.rs | 2 + src/generators.rs | 91 ++++---- 3 files changed, 260 insertions(+), 37 deletions(-) create mode 100644 rustworkx-core/src/generators/binomial_tree_graph.rs diff --git a/rustworkx-core/src/generators/binomial_tree_graph.rs b/rustworkx-core/src/generators/binomial_tree_graph.rs new file mode 100644 index 000000000..5ba7b6800 --- /dev/null +++ b/rustworkx-core/src/generators/binomial_tree_graph.rs @@ -0,0 +1,204 @@ +// 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 petgraph::data::{Build, Create}; +use petgraph::visit::{Data, EdgeRef, IntoEdgeReferences, NodeIndexable, IntoEdges, IntoNodeIdentifiers}; + +use super::InvalidInputError; + +/// Generate a binomial tree graph +/// +/// Arguments: +/// +/// * `order` - The order of the binomial tree. +/// * `weights` - A `Vec` of node weight objects. If the number of weights is +/// less than 2**order extra nodes with None will be appended. +/// * `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. +/// * `bidirectional` - Whether edges are added bidirectionally, if set to +/// `true` then for any edge `(u, v)` an edge `(v, u)` will also be added. +/// If the graph is undirected this will result in a pallel edge. +/// +/// # Example +/// ```rust +/// use rustworkx_core::petgraph; +/// use rustworkx_core::generators::binomial_tree_graph; +/// use rustworkx_core::petgraph::visit::EdgeRef; +/// +/// let g: petgraph::graph::UnGraph<(), ()> = binomial_tree_graph( +/// Some(4), +/// None, +/// || {()}, +/// || {()}, +/// false +/// ).unwrap(); +/// assert_eq!( +/// vec![(0, 1), (1, 2), (2, 3)], +/// g.edge_references() +/// .map(|edge| (edge.source().index(), edge.target().index())) +/// .collect::>(), +/// ) +/// ``` +pub fn binomial_tree_graph( + order: u32, + weights: Option>, + mut default_node_weight: F, + mut default_edge_weight: H, + bidirectional: bool, +) -> Result +where + G: Build + Create + Data + NodeIndexable,// + IntoEdges + IntoNodeIdentifiers, + F: FnMut() -> T, + H: FnMut() -> M, + T: Clone, +{ + // if order >= MAX_ORDER { + // return Err(InvalidInputError {}); + // } + let num_nodes = usize::pow(2, order); + let num_edges = usize::pow(2, order) - 1; + let mut graph = G::with_capacity(num_nodes, num_edges); + + for i in 0..num_nodes { + match weights { + Some(ref weights) => { + if weights.len() > num_nodes { + return Err(InvalidInputError {}); + } + if i < weights.len() { + graph.add_node(weights[i].clone()) + } else { + graph.add_node(default_node_weight()) + } + } + None => graph.add_node(default_node_weight()), + }; + } + + // fn find_edge(graph: &mut G, source: usize, target: usize) -> bool + // where + // G: NodeIndexable + IntoEdgeReferences + IntoEdges + IntoNodeIdentifiers, + // { + // let mut found = false; + // for node in graph.node_identifiers() { + // for e in graph.edges(node) { + // if graph.to_index(e.source()) == source && graph.to_index(e.target()) == target { + // found = true; + // break; + // } + // } + // } + // found + // } + let mut n = 1; + let zero_index = 0; + + for _ in 0..order { + let edges: Vec<(usize, usize)> = graph + .edge_references() + .map(|e| (graph.to_index(e.source()), graph.to_index(e.target()))) + .collect(); + + for (source, target) in edges { + let source_index = source + n; + let target_index = target + n; + + if !find_edge(&mut graph, source_index, target_index) { + graph.add_edge( + graph.from_index(source_index), + graph.from_index(target_index), + default_edge_weight(), + ); + } + if bidirectional { + if !find_edge(&mut graph, target_index, source_index) { + graph.add_edge( + graph.from_index(target_index), + graph.from_index(source_index), + default_edge_weight(), + ); + } + } + } + if !find_edge(&mut graph, zero_index, n) { + graph.add_edge( + graph.from_index(zero_index), + graph.from_index(n), + default_edge_weight(), + ); + } + if bidirectional { + if !find_edge(&mut graph, n, zero_index) { + graph.add_edge( + graph.from_index(n), + graph.from_index(zero_index), + default_edge_weight(), + ); + } + } + n *= 2; + } + Ok(graph) +} + +#[cfg(test)] +mod tests { + use crate::generators::binomial_tree_graph; + use crate::generators::InvalidInputError; + use crate::petgraph; + use crate::petgraph::visit::EdgeRef; + + #[test] + fn test_with_weights() { + let g: petgraph::graph::UnGraph = + binomial_tree_graph(None, Some(vec![0, 1, 2, 3]), || 4, || (), false).unwrap(); + assert_eq!( + vec![(0, 1), (1, 2), (2, 3)], + g.edge_references() + .map(|edge| (edge.source().index(), edge.target().index())) + .collect::>(), + ); + assert_eq!( + vec![0, 1, 2, 3], + g.node_weights().copied().collect::>(), + ); + } + + #[test] + fn test_bidirectional() { + let g: petgraph::graph::DiGraph<(), ()> = + binomial_tree_graph(Some(4), None, || (), || (), true).unwrap(); + assert_eq!( + vec![(0, 1), (1, 0), (1, 2), (2, 1), (2, 3), (3, 2),], + g.edge_references() + .map(|edge| (edge.source().index(), edge.target().index())) + .collect::>(), + ); + } + + #[test] + fn test_error() { + match binomial_tree_graph::, (), _, _, ()>( + None, + None, + || (), + || (), + false, + ) { + 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 b4618ad91..0ed8e4039 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 binomial_tree_graph; mod cycle_graph; mod grid_graph; mod path_graph; @@ -34,6 +35,7 @@ impl fmt::Display for InvalidInputError { } } +pub use binomial_tree_graph::binomial_tree_graph; pub use cycle_graph::cycle_graph; pub use grid_graph::grid_graph; pub use path_graph::path_graph; diff --git a/src/generators.rs b/src/generators.rs index acba83abf..36f65776e 100644 --- a/src/generators.rs +++ b/src/generators.rs @@ -732,51 +732,68 @@ pub fn binomial_tree_graph( order ))); } - let num_nodes = usize::pow(2, order); - let num_edges = usize::pow(2, order) - 1; - let mut graph = StablePyGraph::::with_capacity(num_nodes, num_edges); - for i in 0..num_nodes { - match weights { - Some(ref weights) => { - if weights.len() > num_nodes { - return Err(PyIndexError::new_err("weights should be <= 2**order")); - } - if i < weights.len() { - graph.add_node(weights[i].clone_ref(py)) - } else { - graph.add_node(py.None()) - } + let default_fn = || py.None(); + let graph: StablePyGraph = + match core_generators::binomial_tree_graph(order, weights, default_fn, default_fn, false) + { + Ok(graph) => graph, + Err(_) => { + return Err(PyIndexError::new_err( + "num_nodes and weights list not specified", + )) } - None => graph.add_node(py.None()), }; - } - - let mut n = 1; - let zero_index = NodeIndex::new(0); - - for _ in 0..order { - let edges: Vec<(NodeIndex, NodeIndex)> = graph - .edge_references() - .map(|e| (e.source(), e.target())) - .collect(); - for (source, target) in edges { - let source_index = NodeIndex::new(source.index() + n); - let target_index = NodeIndex::new(target.index() + n); - - graph.add_edge(source_index, target_index, py.None()); - } - - graph.add_edge(zero_index, NodeIndex::new(n), py.None()); - - n *= 2; - } - Ok(graph::PyGraph { graph, node_removed: false, multigraph, attrs: py.None(), }) + // let num_nodes = usize::pow(2, order); + // let num_edges = usize::pow(2, order) - 1; + // let mut graph = StablePyGraph::::with_capacity(num_nodes, num_edges); + // for i in 0..num_nodes { + // match weights { + // Some(ref weights) => { + // if weights.len() > num_nodes { + // return Err(PyIndexError::new_err("weights should be <= 2**order")); + // } + // if i < weights.len() { + // graph.add_node(weights[i].clone_ref(py)) + // } else { + // graph.add_node(py.None()) + // } + // } + // None => graph.add_node(py.None()), + // }; + // } + + // let mut n = 1; + // let zero_index = NodeIndex::new(0); + + // for _ in 0..order { + // let edges: Vec<(NodeIndex, NodeIndex)> = graph + // .edge_references() + // .map(|e| (e.source(), e.target())) + // .collect(); + // for (source, target) in edges { + // let source_index = NodeIndex::new(source.index() + n); + // let target_index = NodeIndex::new(target.index() + n); + + // graph.add_edge(source_index, target_index, py.None()); + // } + + // graph.add_edge(zero_index, NodeIndex::new(n), py.None()); + + // n *= 2; + // } + + // Ok(graph::PyGraph { + // graph, + // node_removed: false, + // multigraph, + // attrs: py.None(), + // }) } /// Creates a full r-ary tree of `n` nodes. From e1408fb8ddf173ef416cc805f341297f22575299 Mon Sep 17 00:00:00 2001 From: Edwin Navarro Date: Wed, 11 Jan 2023 11:30:04 -0800 Subject: [PATCH 2/5] Start rework --- .../src/generators/binomial_tree_graph.rs | 33 ++++++++++--------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/rustworkx-core/src/generators/binomial_tree_graph.rs b/rustworkx-core/src/generators/binomial_tree_graph.rs index 5ba7b6800..9b052c7e6 100644 --- a/rustworkx-core/src/generators/binomial_tree_graph.rs +++ b/rustworkx-core/src/generators/binomial_tree_graph.rs @@ -59,7 +59,7 @@ pub fn binomial_tree_graph( bidirectional: bool, ) -> Result where - G: Build + Create + Data + NodeIndexable,// + IntoEdges + IntoNodeIdentifiers, + G: Build + Create + Data + NodeIndexable + IntoEdges + IntoNodeIdentifiers, F: FnMut() -> T, H: FnMut() -> M, T: Clone, @@ -87,23 +87,24 @@ where }; } - // fn find_edge(graph: &mut G, source: usize, target: usize) -> bool - // where - // G: NodeIndexable + IntoEdgeReferences + IntoEdges + IntoNodeIdentifiers, - // { - // let mut found = false; - // for node in graph.node_identifiers() { - // for e in graph.edges(node) { - // if graph.to_index(e.source()) == source && graph.to_index(e.target()) == target { - // found = true; - // break; - // } - // } - // } - // found - // } + fn find_edge(graph: &mut G, source: usize, target: usize) -> bool + where + G: NodeIndexable + IntoEdgeReferences + IntoEdges + IntoNodeIdentifiers, + { + let mut found = false; + for node in graph.node_identifiers() { + for e in graph.edges(node) { + if graph.to_index(e.source()) == source && graph.to_index(e.target()) == target { + found = true; + break; + } + } + } + found + } let mut n = 1; let zero_index = 0; + //let mut edge_map = HashSet<(usize, usize)>.with_capacity(num_edges); for _ in 0..order { let edges: Vec<(usize, usize)> = graph From 4fee4cda8767cbd26e0f4cae381a6d49664e570f Mon Sep 17 00:00:00 2001 From: Edwin Navarro Date: Wed, 11 Jan 2023 14:50:34 -0800 Subject: [PATCH 3/5] First tests --- .../src/generators/binomial_tree_graph.rs | 26 +++++++++++-------- src/generators.rs | 3 +-- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/rustworkx-core/src/generators/binomial_tree_graph.rs b/rustworkx-core/src/generators/binomial_tree_graph.rs index 9b052c7e6..1f13818b7 100644 --- a/rustworkx-core/src/generators/binomial_tree_graph.rs +++ b/rustworkx-core/src/generators/binomial_tree_graph.rs @@ -11,7 +11,7 @@ // under the License. use petgraph::data::{Build, Create}; -use petgraph::visit::{Data, EdgeRef, IntoEdgeReferences, NodeIndexable, IntoEdges, IntoNodeIdentifiers}; +use petgraph::visit::{GraphBase, GraphProp, Data, EdgeRef, IntoEdgeReferences, NodeIndexable}; use super::InvalidInputError; @@ -59,7 +59,9 @@ pub fn binomial_tree_graph( bidirectional: bool, ) -> Result where - G: Build + Create + Data + NodeIndexable + IntoEdges + IntoNodeIdentifiers, + G: Build + Create + Data + NodeIndexable + GraphProp, + for<'b> &'b G: + GraphBase + IntoEdgeReferences + Copy, F: FnMut() -> T, H: FnMut() -> M, T: Clone, @@ -89,23 +91,25 @@ where fn find_edge(graph: &mut G, source: usize, target: usize) -> bool where - G: NodeIndexable + IntoEdgeReferences + IntoEdges + IntoNodeIdentifiers, + G: NodeIndexable + GraphProp, + for<'b> &'b G: GraphBase + IntoEdgeReferences, { let mut found = false; - for node in graph.node_identifiers() { - for e in graph.edges(node) { - if graph.to_index(e.source()) == source && graph.to_index(e.target()) == target { - found = true; - break; - } + for edge in graph.edge_references() { + if graph.to_index(edge.source()) == source && graph.to_index(edge.target()) == target { + found = true; + break; + } + if !graph.is_directed() && graph.to_index(edge.target()) == source && graph.to_index(edge.source()) == target { + found = true; + break; } } found } + let mut n = 1; let zero_index = 0; - //let mut edge_map = HashSet<(usize, usize)>.with_capacity(num_edges); - for _ in 0..order { let edges: Vec<(usize, usize)> = graph .edge_references() diff --git a/src/generators.rs b/src/generators.rs index 36f65776e..d8d9eb121 100644 --- a/src/generators.rs +++ b/src/generators.rs @@ -734,8 +734,7 @@ pub fn binomial_tree_graph( } let default_fn = || py.None(); let graph: StablePyGraph = - match core_generators::binomial_tree_graph(order, weights, default_fn, default_fn, false) - { + match core_generators::binomial_tree_graph(order, weights, default_fn, default_fn, false) { Ok(graph) => graph, Err(_) => { return Err(PyIndexError::new_err( From 4e816e8d67456bc9b2f038d079aea68424771fcd Mon Sep 17 00:00:00 2001 From: Edwin Navarro Date: Wed, 11 Jan 2023 15:29:34 -0800 Subject: [PATCH 4/5] Finish tests --- .../src/generators/binomial_tree_graph.rs | 96 +++++--- src/generators.rs | 222 ++++++------------ 2 files changed, 138 insertions(+), 180 deletions(-) diff --git a/rustworkx-core/src/generators/binomial_tree_graph.rs b/rustworkx-core/src/generators/binomial_tree_graph.rs index 1f13818b7..904a3ef4a 100644 --- a/rustworkx-core/src/generators/binomial_tree_graph.rs +++ b/rustworkx-core/src/generators/binomial_tree_graph.rs @@ -11,7 +11,7 @@ // under the License. use petgraph::data::{Build, Create}; -use petgraph::visit::{GraphBase, GraphProp, Data, EdgeRef, IntoEdgeReferences, NodeIndexable}; +use petgraph::visit::{Data, EdgeRef, GraphBase, IntoEdgeReferences, NodeIndexable}; use super::InvalidInputError; @@ -37,15 +37,32 @@ use super::InvalidInputError; /// use rustworkx_core::generators::binomial_tree_graph; /// use rustworkx_core::petgraph::visit::EdgeRef; /// +/// let expected_edge_list = vec![ +/// (0, 1), +/// (2, 3), +/// (0, 2), +/// (4, 5), +/// (6, 7), +/// (4, 6), +/// (0, 4), +/// (8, 9), +/// (10, 11), +/// (8, 10), +/// (12, 13), +/// (14, 15), +/// (12, 14), +/// (8, 12), +/// (0, 8), +/// ]; /// let g: petgraph::graph::UnGraph<(), ()> = binomial_tree_graph( -/// Some(4), +/// 4, /// None, /// || {()}, /// || {()}, /// false /// ).unwrap(); /// assert_eq!( -/// vec![(0, 1), (1, 2), (2, 3)], +/// expected_edge_list, /// g.edge_references() /// .map(|edge| (edge.source().index(), edge.target().index())) /// .collect::>(), @@ -59,16 +76,15 @@ pub fn binomial_tree_graph( bidirectional: bool, ) -> Result where - G: Build + Create + Data + NodeIndexable + GraphProp, - for<'b> &'b G: - GraphBase + IntoEdgeReferences + Copy, + G: Build + Create + Data + NodeIndexable, + for<'b> &'b G: GraphBase + IntoEdgeReferences + Copy, F: FnMut() -> T, H: FnMut() -> M, T: Clone, { - // if order >= MAX_ORDER { - // return Err(InvalidInputError {}); - // } + if order >= 60 { + return Err(InvalidInputError {}); + } let num_nodes = usize::pow(2, order); let num_edges = usize::pow(2, order) - 1; let mut graph = G::with_capacity(num_nodes, num_edges); @@ -91,7 +107,7 @@ where fn find_edge(graph: &mut G, source: usize, target: usize) -> bool where - G: NodeIndexable + GraphProp, + G: NodeIndexable, for<'b> &'b G: GraphBase + IntoEdgeReferences, { let mut found = false; @@ -100,10 +116,6 @@ where found = true; break; } - if !graph.is_directed() && graph.to_index(edge.target()) == source && graph.to_index(edge.source()) == target { - found = true; - break; - } } found } @@ -166,27 +178,57 @@ mod tests { use crate::petgraph::visit::EdgeRef; #[test] - fn test_with_weights() { - let g: petgraph::graph::UnGraph = - binomial_tree_graph(None, Some(vec![0, 1, 2, 3]), || 4, || (), false).unwrap(); + fn test_binomial_tree_graph() { + let expected_edge_list = vec![ + (0, 1), + (2, 3), + (0, 2), + (4, 5), + (6, 7), + (4, 6), + (0, 4), + (8, 9), + (10, 11), + (8, 10), + (12, 13), + (14, 15), + (12, 14), + (8, 12), + (0, 8), + ]; + let g: petgraph::graph::UnGraph<(), ()> = + binomial_tree_graph(4, None, || (), || (), false).unwrap(); assert_eq!( - vec![(0, 1), (1, 2), (2, 3)], + expected_edge_list, g.edge_references() .map(|edge| (edge.source().index(), edge.target().index())) .collect::>(), ); - assert_eq!( - vec![0, 1, 2, 3], - g.node_weights().copied().collect::>(), - ); } #[test] - fn test_bidirectional() { + fn test_directed_binomial_tree_graph() { + let expected_edge_list = vec![ + (0, 1), + (2, 3), + (0, 2), + (4, 5), + (6, 7), + (4, 6), + (0, 4), + (8, 9), + (10, 11), + (8, 10), + (12, 13), + (14, 15), + (12, 14), + (8, 12), + (0, 8), + ]; let g: petgraph::graph::DiGraph<(), ()> = - binomial_tree_graph(Some(4), None, || (), || (), true).unwrap(); + binomial_tree_graph(4, None, || (), || (), false).unwrap(); assert_eq!( - vec![(0, 1), (1, 0), (1, 2), (2, 1), (2, 3), (3, 2),], + expected_edge_list, g.edge_references() .map(|edge| (edge.source().index(), edge.target().index())) .collect::>(), @@ -194,9 +236,9 @@ mod tests { } #[test] - fn test_error() { + fn test_binomial_tree_error() { match binomial_tree_graph::, (), _, _, ()>( - None, + 75, None, || (), || (), diff --git a/src/generators.rs b/src/generators.rs index e3fbd54f3..4fe607d24 100644 --- a/src/generators.rs +++ b/src/generators.rs @@ -740,51 +740,76 @@ pub fn binomial_tree_graph( multigraph, attrs: py.None(), }) - // let num_nodes = usize::pow(2, order); - // let num_edges = usize::pow(2, order) - 1; - // let mut graph = StablePyGraph::::with_capacity(num_nodes, num_edges); - // for i in 0..num_nodes { - // match weights { - // Some(ref weights) => { - // if weights.len() > num_nodes { - // return Err(PyIndexError::new_err("weights should be <= 2**order")); - // } - // if i < weights.len() { - // graph.add_node(weights[i].clone_ref(py)) - // } else { - // graph.add_node(py.None()) - // } - // } - // None => graph.add_node(py.None()), - // }; - // } - - // let mut n = 1; - // let zero_index = NodeIndex::new(0); - - // for _ in 0..order { - // let edges: Vec<(NodeIndex, NodeIndex)> = graph - // .edge_references() - // .map(|e| (e.source(), e.target())) - // .collect(); - // for (source, target) in edges { - // let source_index = NodeIndex::new(source.index() + n); - // let target_index = NodeIndex::new(target.index() + n); - - // graph.add_edge(source_index, target_index, py.None()); - // } - - // graph.add_edge(zero_index, NodeIndex::new(n), py.None()); - - // n *= 2; - // } +} - // Ok(graph::PyGraph { - // graph, - // node_removed: false, - // multigraph, - // attrs: py.None(), - // }) +/// Generate a directed binomial tree of order n recursively. +/// The edges propagate towards right and bottom direction if ``bidirectional`` is ``false`` +/// +/// :param int order: Order of the binomial tree. The maximum allowed value +/// for order on the platform your running on. If it's a 64bit platform +/// the max value is 59 and on 32bit systems the max value is 29. Any order +/// value above these will raise a ``OverflowError``. +/// :param list weights: A list of node weights. If the number of weights is +/// less than 2**order extra nodes with None will be appended. +/// :param bidirectional: A parameter to indicate if edges should exist in +/// both directions between nodes +/// :param bool multigraph: When set to False the output +/// :class:`~rustworkx.PyDiGraph` object will not be not be a multigraph and +/// won't allow parallel edges to be added. Instead +/// calls which would create a parallel edge will update the existing edge. +/// +/// :returns: A directed binomial tree with 2^n vertices and 2^n - 1 edges. +/// :rtype: PyDiGraph +/// :raises IndexError: If the lenght of ``weights`` is greater that 2^n +/// :raises OverflowError: If the input order exceeds the maximum value for the +/// current platform. +/// +/// .. jupyter-execute:: +/// +/// import rustworkx.generators +/// from rustworkx.visualization import mpl_draw +/// +/// graph = rustworkx.generators.directed_binomial_tree_graph(4) +/// mpl_draw(graph) +/// +#[pyfunction(bidirectional = "false", multigraph = "true")] +#[pyo3(text_signature = "(order, /, weights=None, bidirectional=False, multigraph=True)")] +pub fn directed_binomial_tree_graph( + py: Python, + order: u32, + weights: Option>, + bidirectional: bool, + multigraph: bool, +) -> PyResult { + if order >= MAX_ORDER { + return Err(PyOverflowError::new_err(format!( + "An order of {} exceeds the max allowable size", + order + ))); + } + let default_fn = || py.None(); + let graph: StablePyGraph = match core_generators::binomial_tree_graph( + order, + weights, + default_fn, + default_fn, + bidirectional, + ) { + Ok(graph) => graph, + Err(_) => { + return Err(PyIndexError::new_err( + "order and weights list not specified", + )) + } + }; + Ok(digraph::PyDiGraph { + graph, + node_removed: false, + check_cycle: false, + cycle_state: algo::DfsSpace::default(), + multigraph, + attrs: py.None(), + }) } /// Creates a full r-ary tree of `n` nodes. @@ -866,115 +891,6 @@ pub fn full_rary_tree( }) } -/// Generate an undirected binomial tree of order n recursively. -/// The edges propagate towards right and bottom direction if ``bidirectional`` is ``false`` -/// -/// :param int order: Order of the binomial tree. The maximum allowed value -/// for order on the platform your running on. If it's a 64bit platform -/// the max value is 59 and on 32bit systems the max value is 29. Any order -/// value above these will raise a ``OverflowError``. -/// :param list weights: A list of node weights. If the number of weights is -/// less than 2**order extra nodes with None will be appended. -/// :param bidirectional: A parameter to indicate if edges should exist in -/// both directions between nodes -/// :param bool multigraph: When set to False the output -/// :class:`~rustworkx.PyDiGraph` object will not be not be a multigraph and -/// won't allow parallel edges to be added. Instead -/// calls which would create a parallel edge will update the existing edge. -/// -/// :returns: A directed binomial tree with 2^n vertices and 2^n - 1 edges. -/// :rtype: PyDiGraph -/// :raises IndexError: If the lenght of ``weights`` is greater that 2^n -/// :raises OverflowError: If the input order exceeds the maximum value for the -/// current platform. -/// -/// .. jupyter-execute:: -/// -/// import rustworkx.generators -/// from rustworkx.visualization import mpl_draw -/// -/// graph = rustworkx.generators.directed_binomial_tree_graph(4) -/// mpl_draw(graph) -/// -#[pyfunction(bidirectional = "false", multigraph = "true")] -#[pyo3(text_signature = "(order, /, weights=None, bidirectional=False, multigraph=True)")] -pub fn directed_binomial_tree_graph( - py: Python, - order: u32, - weights: Option>, - bidirectional: bool, - multigraph: bool, -) -> PyResult { - if order >= MAX_ORDER { - return Err(PyOverflowError::new_err(format!( - "An order of {} exceeds the max allowable size", - order - ))); - } - let num_nodes = usize::pow(2, order); - let num_edges = usize::pow(2, order) - 1; - let mut graph = StablePyGraph::::with_capacity(num_nodes, num_edges); - - for i in 0..num_nodes { - match weights { - Some(ref weights) => { - if weights.len() > num_nodes { - return Err(PyIndexError::new_err("weights should be <= 2**order")); - } - if i < weights.len() { - graph.add_node(weights[i].clone_ref(py)) - } else { - graph.add_node(py.None()) - } - } - None => graph.add_node(py.None()), - }; - } - - let mut n = 1; - let zero_index = NodeIndex::new(0); - - for _ in 0..order { - let edges: Vec<(NodeIndex, NodeIndex)> = graph - .edge_references() - .map(|e| (e.source(), e.target())) - .collect(); - - for (source, target) in edges { - let source_index = NodeIndex::new(source.index() + n); - let target_index = NodeIndex::new(target.index() + n); - - if graph.find_edge(source_index, target_index).is_none() { - graph.add_edge(source_index, target_index, py.None()); - } - - if bidirectional && graph.find_edge(target_index, source_index).is_none() { - graph.add_edge(target_index, source_index, py.None()); - } - } - let n_index = NodeIndex::new(n); - - if graph.find_edge(zero_index, n_index).is_none() { - graph.add_edge(zero_index, n_index, py.None()); - } - - if bidirectional && graph.find_edge(n_index, zero_index).is_none() { - graph.add_edge(n_index, zero_index, py.None()); - } - - n *= 2; - } - - Ok(digraph::PyDiGraph { - graph, - node_removed: false, - check_cycle: false, - cycle_state: algo::DfsSpace::default(), - multigraph, - attrs: py.None(), - }) -} - /// Generate an undirected heavy square graph. Fig. 6 of /// https://arxiv.org/abs/1907.09528. /// An ASCII diagram of the graph is given by: From ad936e439bfd72a291c7377c0c6dd0e0e5e6b9c4 Mon Sep 17 00:00:00 2001 From: Edwin Navarro Date: Wed, 11 Jan 2023 15:53:17 -0800 Subject: [PATCH 5/5] Clippy --- .../src/generators/binomial_tree_graph.rs | 28 ++++++++----------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/rustworkx-core/src/generators/binomial_tree_graph.rs b/rustworkx-core/src/generators/binomial_tree_graph.rs index 904a3ef4a..27837be99 100644 --- a/rustworkx-core/src/generators/binomial_tree_graph.rs +++ b/rustworkx-core/src/generators/binomial_tree_graph.rs @@ -139,14 +139,12 @@ where default_edge_weight(), ); } - if bidirectional { - if !find_edge(&mut graph, target_index, source_index) { - graph.add_edge( - graph.from_index(target_index), - graph.from_index(source_index), - default_edge_weight(), - ); - } + if bidirectional && !find_edge(&mut graph, target_index, source_index) { + graph.add_edge( + graph.from_index(target_index), + graph.from_index(source_index), + default_edge_weight(), + ); } } if !find_edge(&mut graph, zero_index, n) { @@ -156,14 +154,12 @@ where default_edge_weight(), ); } - if bidirectional { - if !find_edge(&mut graph, n, zero_index) { - graph.add_edge( - graph.from_index(n), - graph.from_index(zero_index), - default_edge_weight(), - ); - } + if bidirectional && !find_edge(&mut graph, n, zero_index) { + graph.add_edge( + graph.from_index(n), + graph.from_index(zero_index), + default_edge_weight(), + ); } n *= 2; }