From 2835be490c8e15d2ea4edf2cfeb91e1e778ac0b5 Mon Sep 17 00:00:00 2001 From: Matan Cohen Date: Tue, 14 Mar 2023 00:32:51 +0200 Subject: [PATCH 1/9] added a reverse_inplace function in digraph, the function reverses the direction of the edges in the digraph implemented by switching the indices of the nodes in an edge. --- src/digraph.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/digraph.rs b/src/digraph.rs index 9339869fca..152d6f957a 100644 --- a/src/digraph.rs +++ b/src/digraph.rs @@ -2720,6 +2720,19 @@ impl PyDiGraph { self.clone() } + /// Reverse All edges in the graph inplace + #[pyo3(text_signature = "(self)")] + pub fn reverse_inplace(&mut self, py: Python) { + let indices = self.graph.edge_indices().collect::>(); + for idx in indices { + let (source_node, dest_node) = self.graph.edge_endpoints(idx).expect("Edge Index received from iterator"); + let weight = self.graph.edge_weight(idx).expect("Edge Index received from iterator").clone_ref(py); + self.graph.remove_edge(idx); + self.graph.add_edge(dest_node, source_node, weight); + } + } + + /// Return the number of nodes in the graph fn __len__(&self) -> PyResult { Ok(self.graph.node_count()) From e0ab72d1ae4d55e45ada2008e9a9f2d0d7fddd8b Mon Sep 17 00:00:00 2001 From: Matan Cohen Date: Tue, 14 Mar 2023 00:34:01 +0200 Subject: [PATCH 2/9] added python tests for the reverse_inplace function. testing a simple case and a case for a large graph. --- tests/rustworkx_tests/digraph/test_edges.py | 26 +++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tests/rustworkx_tests/digraph/test_edges.py b/tests/rustworkx_tests/digraph/test_edges.py index 54a448bfeb..81ec2b85de 100644 --- a/tests/rustworkx_tests/digraph/test_edges.py +++ b/tests/rustworkx_tests/digraph/test_edges.py @@ -962,3 +962,29 @@ def test_extend_from_weighted_edge_list(self): graph.extend_from_weighted_edge_list(edge_list) self.assertEqual(len(graph), 4) self.assertEqual(["a", "b", "c", "d", "e"], graph.edges()) + + def test_reverse_graph(self): + graph = rustworkx.PyDiGraph() + graph.add_nodes_from([i for i in range(4)]) + edge_list = [ + (0, 1, "a"), + (1, 2, "b"), + (0, 2, "c"), + (2, 3, "d"), + (0, 3, "e"), + ] + graph.add_edges_from(edge_list) + graph.reverse_inplace() + self.assertEqual([(1, 0), (2, 1), (2, 0), (3, 2), (3, 0)], graph.edge_list()) + + def test_reverse_large_graph(self): + LARGE_AMOUNT_OF_NODES = 10000000 + + graph = rustworkx.PyDiGraph() + graph.add_nodes_from(range(LARGE_AMOUNT_OF_NODES)) + edge_list = list(zip(range(LARGE_AMOUNT_OF_NODES), range(1, LARGE_AMOUNT_OF_NODES))) + weighted_edge_list = [(s, d, "a") for s, d in edge_list] + graph.add_edges_from(weighted_edge_list) + graph.reverse_inplace() + reversed_edge_list = [(d, s) for s, d in edge_list] + self.assertEqual(reversed_edge_list, graph.edge_list()) From 175d57f5d2e8c422a08c09848080535d31d8eb14 Mon Sep 17 00:00:00 2001 From: matanco Date: Sun, 2 Apr 2023 20:23:20 +0300 Subject: [PATCH 3/9] ran rust fmt and clippy, also added more detailed documentation --- src/digraph.rs | 45 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/src/digraph.rs b/src/digraph.rs index 152d6f957a..69eeb6273a 100644 --- a/src/digraph.rs +++ b/src/digraph.rs @@ -2720,19 +2720,56 @@ impl PyDiGraph { self.clone() } - /// Reverse All edges in the graph inplace + /// Reverse the direction of all edges in the graph, in place. + /// + /// This method modifies the graph instance to reverse the direction of all edges. + /// It does so by iterating over all edges in the graph and removing each edge, + /// then adding a new edge in the opposite direction with the same weight. + /// + /// # Arguments + /// + /// * `py`: A reference to the Python interpreter. + /// + /// # Returns + /// + /// None. + /// + /// # Panics + /// + /// This method will panic if the edge indices or weights are not valid. + /// + /// # Examples + /// + /// ``` + /// use rustworkx::{DiGraph, WeightedGraph}; + /// + /// let mut graph = DiGraph::<(), i32>::new(); + /// graph.add_edge(0, 1, 3).unwrap(); + /// graph.add_edge(1, 2, 5).unwrap(); + /// graph.add_edge(2, 3, 2).unwrap(); + /// + /// graph.reverse_inplace(py); + /// + /// assert_eq!(graph.edges().collect::>(), vec![(3, 2), (2, 1), (1, 0)]); + /// ``` #[pyo3(text_signature = "(self)")] pub fn reverse_inplace(&mut self, py: Python) { let indices = self.graph.edge_indices().collect::>(); for idx in indices { - let (source_node, dest_node) = self.graph.edge_endpoints(idx).expect("Edge Index received from iterator"); - let weight = self.graph.edge_weight(idx).expect("Edge Index received from iterator").clone_ref(py); + let (source_node, dest_node) = self + .graph + .edge_endpoints(idx) + .expect("Edge Index received from iterator"); + let weight = self + .graph + .edge_weight(idx) + .expect("Edge Index received from iterator") + .clone_ref(py); self.graph.remove_edge(idx); self.graph.add_edge(dest_node, source_node, weight); } } - /// Return the number of nodes in the graph fn __len__(&self) -> PyResult { Ok(self.graph.node_count()) From 082490a61d3613fabe3d5357f9b4599c1852538c Mon Sep 17 00:00:00 2001 From: Matan Cohen Date: Mon, 8 May 2023 23:25:43 +0300 Subject: [PATCH 4/9] rename reverse_inplace to reverse --- src/digraph.rs | 4 ++-- tests/rustworkx_tests/digraph/test_edges.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/digraph.rs b/src/digraph.rs index 69eeb6273a..a45cd6beca 100644 --- a/src/digraph.rs +++ b/src/digraph.rs @@ -2748,12 +2748,12 @@ impl PyDiGraph { /// graph.add_edge(1, 2, 5).unwrap(); /// graph.add_edge(2, 3, 2).unwrap(); /// - /// graph.reverse_inplace(py); + /// graph.reverse(py); /// /// assert_eq!(graph.edges().collect::>(), vec![(3, 2), (2, 1), (1, 0)]); /// ``` #[pyo3(text_signature = "(self)")] - pub fn reverse_inplace(&mut self, py: Python) { + pub fn reverse(&mut self, py: Python) { let indices = self.graph.edge_indices().collect::>(); for idx in indices { let (source_node, dest_node) = self diff --git a/tests/rustworkx_tests/digraph/test_edges.py b/tests/rustworkx_tests/digraph/test_edges.py index 81ec2b85de..6a37e9494a 100644 --- a/tests/rustworkx_tests/digraph/test_edges.py +++ b/tests/rustworkx_tests/digraph/test_edges.py @@ -974,7 +974,7 @@ def test_reverse_graph(self): (0, 3, "e"), ] graph.add_edges_from(edge_list) - graph.reverse_inplace() + graph.reverse() self.assertEqual([(1, 0), (2, 1), (2, 0), (3, 2), (3, 0)], graph.edge_list()) def test_reverse_large_graph(self): @@ -985,6 +985,6 @@ def test_reverse_large_graph(self): edge_list = list(zip(range(LARGE_AMOUNT_OF_NODES), range(1, LARGE_AMOUNT_OF_NODES))) weighted_edge_list = [(s, d, "a") for s, d in edge_list] graph.add_edges_from(weighted_edge_list) - graph.reverse_inplace() + graph.reverse() reversed_edge_list = [(d, s) for s, d in edge_list] self.assertEqual(reversed_edge_list, graph.edge_list()) From d89858a7bba6f87b66dd068c9f759a50b7700694 Mon Sep 17 00:00:00 2001 From: Matan Cohen Date: Mon, 8 May 2023 23:27:00 +0300 Subject: [PATCH 5/9] change excepts to unwraps (If this fails is because of PyO3. It panics and there is not much point in printing a message) --- src/digraph.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/digraph.rs b/src/digraph.rs index a45cd6beca..68267f2b07 100644 --- a/src/digraph.rs +++ b/src/digraph.rs @@ -2759,11 +2759,11 @@ impl PyDiGraph { let (source_node, dest_node) = self .graph .edge_endpoints(idx) - .expect("Edge Index received from iterator"); + .unwrap(); let weight = self .graph .edge_weight(idx) - .expect("Edge Index received from iterator") + .unwrap() .clone_ref(py); self.graph.remove_edge(idx); self.graph.add_edge(dest_node, source_node, weight); From 805f7d68bf62f900badf7d77224823bfc631fb82 Mon Sep 17 00:00:00 2001 From: Matan Cohen Date: Mon, 8 May 2023 23:41:05 +0300 Subject: [PATCH 6/9] added tests for empty graph and graph with node removed in the middle --- tests/rustworkx_tests/digraph/test_edges.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/rustworkx_tests/digraph/test_edges.py b/tests/rustworkx_tests/digraph/test_edges.py index 6a37e9494a..2d9f56ae52 100644 --- a/tests/rustworkx_tests/digraph/test_edges.py +++ b/tests/rustworkx_tests/digraph/test_edges.py @@ -988,3 +988,18 @@ def test_reverse_large_graph(self): graph.reverse() reversed_edge_list = [(d, s) for s, d in edge_list] self.assertEqual(reversed_edge_list, graph.edge_list()) + + def test_reverse_empty_graph(self): + graph = rustworkx.PyDiGraph() + edges_before = graph.edge_list() + graph.reverse() + self.assertEqual(graph.edge_list(), edges_before) + + def test_removed_middle_node_reverse(self): + graph = rustworkx.PyDiGraph() + graph.add_nodes_from(list(range(5))) + edge_list = [(0, 1), (2, 1), (1, 3), (3, 4), (4, 0)] + graph.extend_from_edge_list(edge_list) + graph.remove_node(1) + graph.reverse() + self.assertEqual(graph.edge_list(), [(4, 3), (0, 4)]) From 7babc2df91ff49d440ba73565fcc5028728c441f Mon Sep 17 00:00:00 2001 From: Matan Cohen Date: Mon, 8 May 2023 23:48:52 +0300 Subject: [PATCH 7/9] added interface signature for IDEs --- rustworkx/digraph.pyi | 1 + 1 file changed, 1 insertion(+) diff --git a/rustworkx/digraph.pyi b/rustworkx/digraph.pyi index 56b977d98e..13735b5fc3 100644 --- a/rustworkx/digraph.pyi +++ b/rustworkx/digraph.pyi @@ -166,6 +166,7 @@ class PyDiGraph(Generic[S, T]): deliminator: Optional[str] = ..., weight_fn: Optional[Callable[[T], str]] = ..., ) -> None: ... + def reverse(self) -> None: ... def __delitem__(self, idx: int, /) -> None: ... def __getitem__(self, idx: int, /) -> S: ... def __getstate__(self) -> Any: ... From 6ff32366996d9858f7a88acea48098e5e1d2c915 Mon Sep 17 00:00:00 2001 From: Matan Cohen Date: Tue, 9 May 2023 13:07:12 +0300 Subject: [PATCH 8/9] ran cargo fmt --- src/digraph.rs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/digraph.rs b/src/digraph.rs index 68267f2b07..b4725e267b 100644 --- a/src/digraph.rs +++ b/src/digraph.rs @@ -2756,15 +2756,8 @@ impl PyDiGraph { pub fn reverse(&mut self, py: Python) { let indices = self.graph.edge_indices().collect::>(); for idx in indices { - let (source_node, dest_node) = self - .graph - .edge_endpoints(idx) - .unwrap(); - let weight = self - .graph - .edge_weight(idx) - .unwrap() - .clone_ref(py); + let (source_node, dest_node) = self.graph.edge_endpoints(idx).unwrap(); + let weight = self.graph.edge_weight(idx).unwrap().clone_ref(py); self.graph.remove_edge(idx); self.graph.add_edge(dest_node, source_node, weight); } From 03dc8e2d422e696c54094b378861de56d5c00c80 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 10 May 2023 14:34:47 -0400 Subject: [PATCH 9/9] Fix doc syntax --- src/digraph.rs | 34 ++++++++++++---------------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/src/digraph.rs b/src/digraph.rs index a00ba98e31..c4d7518d09 100644 --- a/src/digraph.rs +++ b/src/digraph.rs @@ -2825,32 +2825,22 @@ impl PyDiGraph { /// It does so by iterating over all edges in the graph and removing each edge, /// then adding a new edge in the opposite direction with the same weight. /// - /// # Arguments + /// For Example:: /// - /// * `py`: A reference to the Python interpreter. - /// - /// # Returns - /// - /// None. - /// - /// # Panics - /// - /// This method will panic if the edge indices or weights are not valid. - /// - /// # Examples - /// - /// ``` - /// use rustworkx::{DiGraph, WeightedGraph}; + /// import rustworkx as rx /// - /// let mut graph = DiGraph::<(), i32>::new(); - /// graph.add_edge(0, 1, 3).unwrap(); - /// graph.add_edge(1, 2, 5).unwrap(); - /// graph.add_edge(2, 3, 2).unwrap(); + /// graph = rx.PyDiGraph() /// - /// graph.reverse(py); + /// # Generate a path directed path graph with weights + /// graph.extend_from_weighted_edge_list([ + /// (0, 1, 3), + /// (1, 2, 5), + /// (2, 3, 2), + /// ]) + /// # Reverse edges + /// graph.reverse() /// - /// assert_eq!(graph.edges().collect::>(), vec![(3, 2), (2, 1), (1, 0)]); - /// ``` + /// assert graph.weighted_edge_list() == [(3, 2, 2), (2, 1, 5), (1, 0, 3)]; #[pyo3(text_signature = "(self)")] pub fn reverse(&mut self, py: Python) { let indices = self.graph.edge_indices().collect::>();