From 677bbddc8fadb9509997ed5dd1275b8c2ca1ee6d Mon Sep 17 00:00:00 2001 From: nahumsa Date: Thu, 29 Apr 2021 14:57:21 -0300 Subject: [PATCH 01/14] add heavy square graph to generators Added implementation of [heavy hex](https://journals.aps.org/prx/abstract/10.1103/PhysRevX.10.011022), the input is the code distance, d. In the implementation I chose to add nodes referring to each type of qubit used in the Heavy Hex Code (data, syndrome, and flag) which _may cause the node order to be confusing_, but that was the best way that I figure out how to implement. --- src/generators.rs | 134 ++++++++++++++++++++++++++ tests/generators/test_heavy_square.py | 36 +++++++ 2 files changed, 170 insertions(+) create mode 100644 tests/generators/test_heavy_square.py diff --git a/src/generators.rs b/src/generators.rs index 7141bf37ce..0e2acdd8ab 100644 --- a/src/generators.rs +++ b/src/generators.rs @@ -985,6 +985,139 @@ pub fn directed_grid_graph( }) } +/// Generate an undirected heavy square graph. +/// +/// :param int d: distance of the code. +/// :param bool multigraph: When set to False the output +/// :class:`~retworkx.PyGraph` 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: The generated heavy square graph +/// :rtype: PyGraph +/// :raises IndexError: If d is even. +/// +/// .. jupyter-execute:: +/// +/// import os +/// import tempfile +/// +/// import pydot +/// from PIL import Image +/// +/// import retworkx.generators +/// +/// graph = retworkx.generators.heavy_square_graph(3) +/// dot_str = graph.to_dot( +/// lambda node: dict( +/// color='black', fillcolor='lightblue', style='filled')) +/// dot = pydot.graph_from_dot_data(dot_str)[0] +/// +/// with tempfile.TemporaryDirectory() as tmpdirname: +/// tmp_path = os.path.join(tmpdirname, 'dag.png') +/// dot.write_png(tmp_path) +/// image = Image.open(tmp_path) +/// os.remove(tmp_path) +/// image +/// +#[pyfunction(multigraph = true)] +#[text_signature = "(/, d=None, multigraph=True)"] +pub fn heavy_square_graph( + py: Python, + d: usize, + multigraph: bool, +) -> PyResult { + let mut graph = StableUnGraph::::default(); + + if d % 2 == 0 { + return Err(PyIndexError::new_err("d must be odd")); + } + + let num_data = d * d; + let num_syndrome = d * (d - 1); + let num_flag = d * (d - 1); + + let nodes_data: Vec = + (0..num_data).map(|_| graph.add_node(py.None())).collect(); + let nodes_syndrome: Vec = (0..num_syndrome) + .map(|_| graph.add_node(py.None())) + .collect(); + let nodes_flag: Vec = + (0..num_flag).map(|_| graph.add_node(py.None())).collect(); + + // connect data and flags + for (i, flag_chunk) in nodes_flag.chunks(d - 1).enumerate() { + for (j, flag) in flag_chunk.iter().enumerate() { + graph.add_edge(nodes_data[i * d + j], *flag, py.None()); + graph.add_edge(*flag, nodes_data[i * d + j + 1], py.None()); + } + } + + // connect data and syndromes + for (i, syndrome_chunk) in nodes_syndrome.chunks(d).enumerate() { + if i % 2 == 0 { + graph.add_edge( + nodes_data[(i + 1) * d - 1], + syndrome_chunk[syndrome_chunk.len() - 1], + py.None(), + ); + graph.add_edge( + syndrome_chunk[syndrome_chunk.len() - 1], + nodes_data[(i + 2) * d - 1], + py.None(), + ); + } else if i % 2 == 1 { + graph.add_edge(nodes_data[i * d], syndrome_chunk[0], py.None()); + graph.add_edge( + syndrome_chunk[0], + nodes_data[(i + 1) * d], + py.None(), + ); + } + } + + // connect flag and syndromes + for (i, syndrome_chunk) in nodes_syndrome.chunks(d).enumerate() { + if i % 2 == 0 { + for (j, syndrome) in syndrome_chunk.iter().enumerate() { + if j != syndrome_chunk.len() - 1 { + graph.add_edge( + nodes_flag[i * (d - 1) + j], + *syndrome, + py.None(), + ); + graph.add_edge( + *syndrome, + nodes_flag[(i + 1) * (d - 1) + j], + py.None(), + ); + } + } + } else if i % 2 == 1 { + for (j, syndrome) in syndrome_chunk.iter().enumerate() { + if j != 0 { + graph.add_edge( + nodes_flag[i * (d - 1) + j - 1], + *syndrome, + py.None(), + ); + graph.add_edge( + *syndrome, + nodes_flag[(i + 1) * (d - 1) + j - 1], + py.None(), + ); + } + } + } + } + + Ok(graph::PyGraph { + graph, + node_removed: false, + multigraph, + }) +} + #[pymodule] pub fn generators(_py: Python, m: &PyModule) -> PyResult<()> { m.add_wrapped(wrap_pyfunction!(cycle_graph))?; @@ -997,5 +1130,6 @@ pub fn generators(_py: Python, m: &PyModule) -> PyResult<()> { m.add_wrapped(wrap_pyfunction!(directed_mesh_graph))?; m.add_wrapped(wrap_pyfunction!(grid_graph))?; m.add_wrapped(wrap_pyfunction!(directed_grid_graph))?; + m.add_wrapped(wrap_pyfunction!(heavy_square_graph))?; Ok(()) } diff --git a/tests/generators/test_heavy_square.py b/tests/generators/test_heavy_square.py new file mode 100644 index 0000000000..6f454fc745 --- /dev/null +++ b/tests/generators/test_heavy_square.py @@ -0,0 +1,36 @@ +# 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. + +import unittest + +import retworkx + + +class TestHeavyHexGraph(unittest.TestCase): + + def test_heavy_square_graph_5(self): + d = 5 + graph = retworkx.generators.heavy_square_graph(d) + self.assertEqual(len(graph), 3 * d * d - 2 * d) + self.assertEqual(len(graph.edges()), + 2 * d * (d - 1) + 2 * d * (d - 1)) + + def test_heavy_square_graph_3(self): + d = 3 + graph = retworkx.generators.heavy_square_graph(d) + self.assertEqual(len(graph), 3 * d * d - 2 * d) + self.assertEqual(len(graph.edges()), + 2 * d * (d - 1) + 2 * d * (d - 1)) + + def test_heavy_square_graph_no_d(self): + with self.assertRaises(TypeError): + retworkx.generators.heavy_square_graph() From 9f3e46561920840829531215d61879c0b1beaca2 Mon Sep 17 00:00:00 2001 From: nahumsa Date: Thu, 29 Apr 2021 15:09:36 -0300 Subject: [PATCH 02/14] add release notes --- docs/source/api.rst | 1 + releasenotes/notes/add-heavy-square-4e0713e84fd60eb7.yaml | 4 ++++ 2 files changed, 5 insertions(+) create mode 100644 releasenotes/notes/add-heavy-square-4e0713e84fd60eb7.yaml diff --git a/docs/source/api.rst b/docs/source/api.rst index a5cddbbe0e..6c3e2ed761 100644 --- a/docs/source/api.rst +++ b/docs/source/api.rst @@ -30,6 +30,7 @@ Generators retworkx.generators.directed_mesh_graph retworkx.generators.grid_graph retworkx.generators.directed_grid_graph + retworkx.generators.heavy_square_graph Random Circuit Functions ======================== diff --git a/releasenotes/notes/add-heavy-square-4e0713e84fd60eb7.yaml b/releasenotes/notes/add-heavy-square-4e0713e84fd60eb7.yaml new file mode 100644 index 0000000000..5be26a29ba --- /dev/null +++ b/releasenotes/notes/add-heavy-square-4e0713e84fd60eb7.yaml @@ -0,0 +1,4 @@ +--- +features: + - | + Added new generator for constructing a heavy square graph (:func:`retworkx.generators.heavy_square_graph`). From 257748ced055738afbe580b1951193336de98785 Mon Sep 17 00:00:00 2001 From: nahumsa Date: Wed, 12 May 2021 15:52:21 -0300 Subject: [PATCH 03/14] add heavy square edge assertion --- tests/generators/test_heavy_square.py | 117 ++++++++++++++++++++++++-- 1 file changed, 112 insertions(+), 5 deletions(-) diff --git a/tests/generators/test_heavy_square.py b/tests/generators/test_heavy_square.py index 6f454fc745..c50422db29 100644 --- a/tests/generators/test_heavy_square.py +++ b/tests/generators/test_heavy_square.py @@ -16,20 +16,127 @@ class TestHeavyHexGraph(unittest.TestCase): - def test_heavy_square_graph_5(self): d = 5 graph = retworkx.generators.heavy_square_graph(d) self.assertEqual(len(graph), 3 * d * d - 2 * d) - self.assertEqual(len(graph.edges()), - 2 * d * (d - 1) + 2 * d * (d - 1)) + self.assertEqual(len(graph.edges()), 2 * d * (d - 1) + 2 * d * (d - 1)) + expected_edges = [ + (0, 45), + (45, 1), + (1, 46), + (46, 2), + (2, 47), + (47, 3), + (3, 48), + (48, 4), + (5, 49), + (49, 6), + (6, 50), + (50, 7), + (7, 51), + (51, 8), + (8, 52), + (52, 9), + (10, 53), + (53, 11), + (11, 54), + (54, 12), + (12, 55), + (55, 13), + (13, 56), + (56, 14), + (15, 57), + (57, 16), + (16, 58), + (58, 17), + (17, 59), + (59, 18), + (18, 60), + (60, 19), + (20, 61), + (61, 21), + (21, 62), + (62, 22), + (22, 63), + (63, 23), + (23, 64), + (64, 24), + (4, 29), + (29, 9), + (5, 30), + (30, 10), + (14, 39), + (39, 19), + (15, 40), + (40, 20), + (45, 25), + (25, 49), + (46, 26), + (26, 50), + (47, 27), + (27, 51), + (48, 28), + (28, 52), + (49, 31), + (31, 53), + (50, 32), + (32, 54), + (51, 33), + (33, 55), + (52, 34), + (34, 56), + (53, 35), + (35, 57), + (54, 36), + (36, 58), + (55, 37), + (37, 59), + (56, 38), + (38, 60), + (57, 41), + (41, 61), + (58, 42), + (42, 62), + (59, 43), + (43, 63), + (60, 44), + (44, 64), + ] + self.assertEqual(list(graph.edge_list()), expected_edges) def test_heavy_square_graph_3(self): d = 3 graph = retworkx.generators.heavy_square_graph(d) self.assertEqual(len(graph), 3 * d * d - 2 * d) - self.assertEqual(len(graph.edges()), - 2 * d * (d - 1) + 2 * d * (d - 1)) + self.assertEqual(len(graph.edges()), 2 * d * (d - 1) + 2 * d * (d - 1)) + expected_edges = [ + (0, 15), + (15, 1), + (1, 16), + (16, 2), + (3, 17), + (17, 4), + (4, 18), + (18, 5), + (6, 19), + (19, 7), + (7, 20), + (20, 8), + (2, 11), + (11, 5), + (3, 12), + (12, 6), + (15, 9), + (9, 17), + (16, 10), + (10, 18), + (17, 13), + (13, 19), + (18, 14), + (14, 20), + ] + self.assertEqual(list(graph.edge_list()), expected_edges) def test_heavy_square_graph_no_d(self): with self.assertRaises(TypeError): From bee8ec86c73a46abbca9db712e26df330dd52871 Mon Sep 17 00:00:00 2001 From: nahumsa Date: Sat, 24 Jul 2021 14:33:26 -0300 Subject: [PATCH 04/14] add reference on the docs --- src/generators.rs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/generators.rs b/src/generators.rs index f4c6f689e2..35936d97df 100644 --- a/src/generators.rs +++ b/src/generators.rs @@ -1072,7 +1072,25 @@ pub fn directed_binomial_tree_graph( }) } -/// Generate an undirected heavy square graph. +/// Generate an undirected heavy square graph. Fig. 6 of +/// https://arxiv.org/abs/1907.09528. +/// An ASCII diagram of the graph is given by: +/// +/// ... S ... +/// \ / \ +/// ... D D D ... +/// | | | +/// ... F-S-F-S-F-... +/// | | | +/// ... D D D ... +/// | | | +/// ... F-S-F-S-F-... +/// | | | +/// ......... +/// | | | +/// ... D D D ... +/// \ / \ +/// ... S ... /// /// :param int d: distance of the code. /// :param bool multigraph: When set to False the output From a309aa029dc2e3467b1f97df888d7689bac62103 Mon Sep 17 00:00:00 2001 From: nahumsa Date: Sat, 24 Jul 2021 14:45:12 -0300 Subject: [PATCH 05/14] fmt --- src/generators.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/generators.rs b/src/generators.rs index 35936d97df..0c0a7dffc3 100644 --- a/src/generators.rs +++ b/src/generators.rs @@ -1073,7 +1073,7 @@ pub fn directed_binomial_tree_graph( } /// Generate an undirected heavy square graph. Fig. 6 of -/// https://arxiv.org/abs/1907.09528. +/// https://arxiv.org/abs/1907.09528 /// An ASCII diagram of the graph is given by: /// /// ... S ... @@ -1162,13 +1162,13 @@ pub fn heavy_square_graph( for (i, syndrome_chunk) in nodes_syndrome.chunks(d).enumerate() { if i % 2 == 0 { graph.add_edge( - nodes_data[(i + 1) * d - 1], + nodes_data[i * d + (d - 1)], syndrome_chunk[syndrome_chunk.len() - 1], py.None(), ); graph.add_edge( syndrome_chunk[syndrome_chunk.len() - 1], - nodes_data[(i + 2) * d - 1], + nodes_data[i * d + (2 * d - 1)], py.None(), ); } else if i % 2 == 1 { From c06c72a16f1d29123cb30c2f6680492b16937ac3 Mon Sep 17 00:00:00 2001 From: nahumsa Date: Sat, 24 Jul 2021 14:49:54 -0300 Subject: [PATCH 06/14] updated text_signature --- src/generators.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/generators.rs b/src/generators.rs index 0c0a7dffc3..1089fb52c1 100644 --- a/src/generators.rs +++ b/src/generators.rs @@ -1126,7 +1126,9 @@ pub fn directed_binomial_tree_graph( /// image /// #[pyfunction(multigraph = true)] -#[text_signature = "(/, d=None, multigraph=True)"] +#[pyo3( + text_signature = "(d, /, multigraph=True)" +)] pub fn heavy_square_graph( py: Python, d: usize, From c2fc6f567cbd0a892b4c996d698df3a4ef8f6c19 Mon Sep 17 00:00:00 2001 From: nahumsa Date: Sat, 24 Jul 2021 14:54:22 -0300 Subject: [PATCH 07/14] fmt --- src/generators.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/generators.rs b/src/generators.rs index 1089fb52c1..1f4cbc030c 100644 --- a/src/generators.rs +++ b/src/generators.rs @@ -1126,9 +1126,7 @@ pub fn directed_binomial_tree_graph( /// image /// #[pyfunction(multigraph = true)] -#[pyo3( - text_signature = "(d, /, multigraph=True)" -)] +#[pyo3(text_signature = "(d, /, multigraph=True)")] pub fn heavy_square_graph( py: Python, d: usize, From 43044d626f18dcaa2d3a972a4fb4724c0434437b Mon Sep 17 00:00:00 2001 From: nahumsa Date: Sat, 24 Jul 2021 15:18:40 -0300 Subject: [PATCH 08/14] fix doc identation --- src/generators.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/generators.rs b/src/generators.rs index 1f4cbc030c..dac9e31bd3 100644 --- a/src/generators.rs +++ b/src/generators.rs @@ -1076,6 +1076,7 @@ pub fn directed_binomial_tree_graph( /// https://arxiv.org/abs/1907.09528 /// An ASCII diagram of the graph is given by: /// +/// ``` /// ... S ... /// \ / \ /// ... D D D ... @@ -1091,6 +1092,7 @@ pub fn directed_binomial_tree_graph( /// ... D D D ... /// \ / \ /// ... S ... +/// ``` /// /// :param int d: distance of the code. /// :param bool multigraph: When set to False the output From d83835bb474d512a944ac51c14cb331de18f7969 Mon Sep 17 00:00:00 2001 From: nahumsa Date: Sat, 24 Jul 2021 16:59:57 -0300 Subject: [PATCH 09/14] Add code block for ASCII --- src/generators.rs | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/src/generators.rs b/src/generators.rs index dac9e31bd3..9893ba2807 100644 --- a/src/generators.rs +++ b/src/generators.rs @@ -1076,23 +1076,24 @@ pub fn directed_binomial_tree_graph( /// https://arxiv.org/abs/1907.09528 /// An ASCII diagram of the graph is given by: /// -/// ``` -/// ... S ... -/// \ / \ -/// ... D D D ... -/// | | | -/// ... F-S-F-S-F-... -/// | | | -/// ... D D D ... -/// | | | -/// ... F-S-F-S-F-... -/// | | | -/// ......... -/// | | | -/// ... D D D ... -/// \ / \ -/// ... S ... -/// ``` +/// .. code-block:: console +/// +/// ... S ... +/// \ / \ +/// ... D D D ... +/// | | | +/// ... F-S-F-S-F-... +/// | | | +/// ... D D D ... +/// | | | +/// ... F-S-F-S-F-... +/// | | | +/// ......... +/// | | | +/// ... D D D ... +/// \ / \ +/// ... S ... +/// /// /// :param int d: distance of the code. /// :param bool multigraph: When set to False the output From 316005e19e9174c06f4461c43a38e951a7ddd33e Mon Sep 17 00:00:00 2001 From: nahumsa Date: Sat, 24 Jul 2021 17:04:19 -0300 Subject: [PATCH 10/14] lint --- src/generators.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/generators.rs b/src/generators.rs index 9893ba2807..f60ec9a5b2 100644 --- a/src/generators.rs +++ b/src/generators.rs @@ -1093,7 +1093,7 @@ pub fn directed_binomial_tree_graph( /// ... D D D ... /// \ / \ /// ... S ... -/// +/// /// /// :param int d: distance of the code. /// :param bool multigraph: When set to False the output From 60a150b0292713220e21a5d00d00935b736fdab9 Mon Sep 17 00:00:00 2001 From: nahumsa Date: Wed, 28 Jul 2021 16:32:06 -0300 Subject: [PATCH 11/14] add note about the four-frequency variant --- src/generators.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/generators.rs b/src/generators.rs index f60ec9a5b2..9ac36fae59 100644 --- a/src/generators.rs +++ b/src/generators.rs @@ -1073,7 +1073,7 @@ pub fn directed_binomial_tree_graph( } /// Generate an undirected heavy square graph. Fig. 6 of -/// https://arxiv.org/abs/1907.09528 +/// https://arxiv.org/abs/1907.09528. /// An ASCII diagram of the graph is given by: /// /// .. code-block:: console @@ -1094,6 +1094,9 @@ pub fn directed_binomial_tree_graph( /// \ / \ /// ... S ... /// +/// NOTE: This function generates the four-frequency variant of the heavy square code. +/// This function implements Fig 10.b left of the [paper](https://arxiv.org/abs/1907.09528). +/// This function doesn't support the variant Fig 10.b right. /// /// :param int d: distance of the code. /// :param bool multigraph: When set to False the output From fc466997b50b2f66a72f5afb279876909f283dc6 Mon Sep 17 00:00:00 2001 From: nahumsa Date: Sat, 14 Aug 2021 19:51:32 -0300 Subject: [PATCH 12/14] add `directed_heavy_square_code` --- docs/source/api.rst | 1 + .../add-heavy-square-4e0713e84fd60eb7.yaml | 3 +- src/generators.rs | 209 ++++++++++ tests/generators/test_heavy_square.py | 356 ++++++++++++++++++ 4 files changed, 568 insertions(+), 1 deletion(-) diff --git a/docs/source/api.rst b/docs/source/api.rst index 56caa3ffdd..11837ea7d4 100644 --- a/docs/source/api.rst +++ b/docs/source/api.rst @@ -156,6 +156,7 @@ Generators retworkx.generators.hexagonal_lattice_graph retworkx.generators.directed_hexagonal_lattice_graph retworkx.generators.heavy_square_graph + retworkx.generators.directed_heavy_square_graph Random Circuit Functions ======================== diff --git a/releasenotes/notes/add-heavy-square-4e0713e84fd60eb7.yaml b/releasenotes/notes/add-heavy-square-4e0713e84fd60eb7.yaml index 5be26a29ba..924e21ccbe 100644 --- a/releasenotes/notes/add-heavy-square-4e0713e84fd60eb7.yaml +++ b/releasenotes/notes/add-heavy-square-4e0713e84fd60eb7.yaml @@ -1,4 +1,5 @@ --- features: - | - Added new generator for constructing a heavy square graph (:func:`retworkx.generators.heavy_square_graph`). + Added new generator for constructing a heavy square graph (:func:`retworetworkx.generators.heavy_square_graphrkx.generators.heavy_square_graph`) + and a directed heavy square graph (:func:`retworkx.generators.directed_heavy_square_graph`). diff --git a/src/generators.rs b/src/generators.rs index 13de99dd97..8372899e50 100644 --- a/src/generators.rs +++ b/src/generators.rs @@ -1229,6 +1229,214 @@ pub fn heavy_square_graph( }) } +/// Generate an directed heavy square graph. Fig. 6 of +/// https://arxiv.org/abs/1907.09528. +/// An ASCII diagram of the graph is given by: +/// +/// .. code-block:: console +/// +/// ... S ... +/// \ / \ +/// ... D D D ... +/// | | | +/// ... F-S-F-S-F-... +/// | | | +/// ... D D D ... +/// | | | +/// ... F-S-F-S-F-... +/// | | | +/// ......... +/// | | | +/// ... D D D ... +/// \ / \ +/// ... S ... +/// +/// NOTE: This function generates the four-frequency variant of the heavy square code. +/// This function implements Fig 10.b left of the [paper](https://arxiv.org/abs/1907.09528). +/// This function doesn't support the variant Fig 10.b right. +/// +/// :param int d: distance of the code. +/// :param bool multigraph: When set to False the output +/// :class:`~retworkx.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: The generated directed heavy square graph +/// :rtype: PyDiGraph +/// :raises IndexError: If d is even. +/// +/// .. jupyter-execute:: +/// +/// import os +/// import tempfile +/// +/// import pydot +/// from PIL import Image +/// +/// import retworkx.generators +/// +/// graph = retworkx.generators.heavy_square_graph(3) +/// dot_str = graph.to_dot( +/// lambda node: dict( +/// color='black', fillcolor='lightblue', style='filled')) +/// dot = pydot.graph_from_dot_data(dot_str)[0] +/// +/// with tempfile.TemporaryDirectory() as tmpdirname: +/// tmp_path = os.path.join(tmpdirname, 'dag.png') +/// dot.write_png(tmp_path) +/// image = Image.open(tmp_path) +/// os.remove(tmp_path) +/// image +/// +#[pyfunction(bidirectional = false, multigraph = true)] +#[pyo3(text_signature = "(d, /, bidirectional=False, multigraph=True)")] +pub fn directed_heavy_square_graph( + py: Python, + d: usize, + bidirectional: bool, + multigraph: bool, +) -> PyResult { + let mut graph = StableDiGraph::::default(); + + if d % 2 == 0 { + return Err(PyIndexError::new_err("d must be odd")); + } + + let num_data = d * d; + let num_syndrome = d * (d - 1); + let num_flag = d * (d - 1); + + let nodes_data: Vec = + (0..num_data).map(|_| graph.add_node(py.None())).collect(); + let nodes_syndrome: Vec = (0..num_syndrome) + .map(|_| graph.add_node(py.None())) + .collect(); + let nodes_flag: Vec = + (0..num_flag).map(|_| graph.add_node(py.None())).collect(); + + // connect data and flags + for (i, flag_chunk) in nodes_flag.chunks(d - 1).enumerate() { + for (j, flag) in flag_chunk.iter().enumerate() { + graph.add_edge(nodes_data[i * d + j], *flag, py.None()); + graph.add_edge(*flag, nodes_data[i * d + j + 1], py.None()); + if bidirectional { + graph.add_edge(*flag, nodes_data[i * d + j], py.None()); + graph.add_edge(nodes_data[i * d + j + 1], *flag, py.None()); + } + } + } + + // connect data and syndromes + for (i, syndrome_chunk) in nodes_syndrome.chunks(d).enumerate() { + if i % 2 == 0 { + graph.add_edge( + nodes_data[i * d + (d - 1)], + syndrome_chunk[syndrome_chunk.len() - 1], + py.None(), + ); + graph.add_edge( + syndrome_chunk[syndrome_chunk.len() - 1], + nodes_data[i * d + (2 * d - 1)], + py.None(), + ); + if bidirectional { + graph.add_edge( + syndrome_chunk[syndrome_chunk.len() - 1], + nodes_data[i * d + (d - 1)], + py.None(), + ); + graph.add_edge( + nodes_data[i * d + (2 * d - 1)], + syndrome_chunk[syndrome_chunk.len() - 1], + py.None(), + ); + } + } else if i % 2 == 1 { + graph.add_edge(nodes_data[i * d], syndrome_chunk[0], py.None()); + graph.add_edge( + syndrome_chunk[0], + nodes_data[(i + 1) * d], + py.None(), + ); + if bidirectional { + graph.add_edge(syndrome_chunk[0], nodes_data[i * d], py.None()); + graph.add_edge( + nodes_data[(i + 1) * d], + syndrome_chunk[0], + py.None(), + ); + } + } + } + + // connect flag and syndromes + for (i, syndrome_chunk) in nodes_syndrome.chunks(d).enumerate() { + if i % 2 == 0 { + for (j, syndrome) in syndrome_chunk.iter().enumerate() { + if j != syndrome_chunk.len() - 1 { + graph.add_edge( + nodes_flag[i * (d - 1) + j], + *syndrome, + py.None(), + ); + graph.add_edge( + *syndrome, + nodes_flag[(i + 1) * (d - 1) + j], + py.None(), + ); + if bidirectional { + graph.add_edge( + *syndrome, + nodes_flag[i * (d - 1) + j], + py.None(), + ); + graph.add_edge( + nodes_flag[(i + 1) * (d - 1) + j], + *syndrome, + py.None(), + ); + } + } + } + } else if i % 2 == 1 { + for (j, syndrome) in syndrome_chunk.iter().enumerate() { + if j != 0 { + graph.add_edge( + nodes_flag[i * (d - 1) + j - 1], + *syndrome, + py.None(), + ); + graph.add_edge( + *syndrome, + nodes_flag[(i + 1) * (d - 1) + j - 1], + py.None(), + ); + if bidirectional { + graph.add_edge( + *syndrome, + nodes_flag[i * (d - 1) + j - 1], + py.None(), + ); + graph.add_edge( + nodes_flag[(i + 1) * (d - 1) + j - 1], + *syndrome, + py.None(), + ); + } + } + } + } + } + + Ok(digraph::PyDiGraph { + graph, + node_removed: false, + check_cycle: false, + cycle_state: algo::DfsSpace::default(), + multigraph, + }) +} + /// Generate an undirected hexagonal lattice graph. /// /// :param int rows: The number of rows to generate the graph with. @@ -1508,6 +1716,7 @@ pub fn generators(_py: Python, m: &PyModule) -> PyResult<()> { m.add_wrapped(wrap_pyfunction!(grid_graph))?; m.add_wrapped(wrap_pyfunction!(directed_grid_graph))?; m.add_wrapped(wrap_pyfunction!(heavy_square_graph))?; + m.add_wrapped(wrap_pyfunction!(directed_heavy_square_graph))?; m.add_wrapped(wrap_pyfunction!(binomial_tree_graph))?; m.add_wrapped(wrap_pyfunction!(directed_binomial_tree_graph))?; m.add_wrapped(wrap_pyfunction!(hexagonal_lattice_graph))?; diff --git a/tests/generators/test_heavy_square.py b/tests/generators/test_heavy_square.py index c50422db29..ef776c7577 100644 --- a/tests/generators/test_heavy_square.py +++ b/tests/generators/test_heavy_square.py @@ -16,6 +16,268 @@ class TestHeavyHexGraph(unittest.TestCase): + def test_directed_heavy_square_graph_5(self): + d = 5 + graph = retworkx.generators.directed_heavy_square_graph(d) + self.assertEqual(len(graph), 3 * d * d - 2 * d) + self.assertEqual(len(graph.edges()), 2 * d * (d - 1) + 2 * d * (d - 1)) + expected_edges = [ + (0, 45), + (45, 1), + (1, 46), + (46, 2), + (2, 47), + (47, 3), + (3, 48), + (48, 4), + (5, 49), + (49, 6), + (6, 50), + (50, 7), + (7, 51), + (51, 8), + (8, 52), + (52, 9), + (10, 53), + (53, 11), + (11, 54), + (54, 12), + (12, 55), + (55, 13), + (13, 56), + (56, 14), + (15, 57), + (57, 16), + (16, 58), + (58, 17), + (17, 59), + (59, 18), + (18, 60), + (60, 19), + (20, 61), + (61, 21), + (21, 62), + (62, 22), + (22, 63), + (63, 23), + (23, 64), + (64, 24), + (4, 29), + (29, 9), + (5, 30), + (30, 10), + (14, 39), + (39, 19), + (15, 40), + (40, 20), + (45, 25), + (25, 49), + (46, 26), + (26, 50), + (47, 27), + (27, 51), + (48, 28), + (28, 52), + (49, 31), + (31, 53), + (50, 32), + (32, 54), + (51, 33), + (33, 55), + (52, 34), + (34, 56), + (53, 35), + (35, 57), + (54, 36), + (36, 58), + (55, 37), + (37, 59), + (56, 38), + (38, 60), + (57, 41), + (41, 61), + (58, 42), + (42, 62), + (59, 43), + (43, 63), + (60, 44), + (44, 64), + ] + self.assertEqual(list(graph.edge_list()), expected_edges) + + def test_directed_heavy_square_graph_5_bidirectional(self): + d = 5 + graph = retworkx.generators.directed_heavy_square_graph( + d, bidirectional=True + ) + self.assertEqual(len(graph), 3 * d * d - 2 * d) + self.assertEqual( + len(graph.edges()), 2 * (2 * d * (d - 1) + 2 * d * (d - 1)) + ) + expected_edges = [ + (0, 45), + (45, 1), + (45, 0), + (1, 45), + (1, 46), + (46, 2), + (46, 1), + (2, 46), + (2, 47), + (47, 3), + (47, 2), + (3, 47), + (3, 48), + (48, 4), + (48, 3), + (4, 48), + (5, 49), + (49, 6), + (49, 5), + (6, 49), + (6, 50), + (50, 7), + (50, 6), + (7, 50), + (7, 51), + (51, 8), + (51, 7), + (8, 51), + (8, 52), + (52, 9), + (52, 8), + (9, 52), + (10, 53), + (53, 11), + (53, 10), + (11, 53), + (11, 54), + (54, 12), + (54, 11), + (12, 54), + (12, 55), + (55, 13), + (55, 12), + (13, 55), + (13, 56), + (56, 14), + (56, 13), + (14, 56), + (15, 57), + (57, 16), + (57, 15), + (16, 57), + (16, 58), + (58, 17), + (58, 16), + (17, 58), + (17, 59), + (59, 18), + (59, 17), + (18, 59), + (18, 60), + (60, 19), + (60, 18), + (19, 60), + (20, 61), + (61, 21), + (61, 20), + (21, 61), + (21, 62), + (62, 22), + (62, 21), + (22, 62), + (22, 63), + (63, 23), + (63, 22), + (23, 63), + (23, 64), + (64, 24), + (64, 23), + (24, 64), + (4, 29), + (29, 9), + (29, 4), + (9, 29), + (5, 30), + (30, 10), + (30, 5), + (10, 30), + (14, 39), + (39, 19), + (39, 14), + (19, 39), + (15, 40), + (40, 20), + (40, 15), + (20, 40), + (45, 25), + (25, 49), + (25, 45), + (49, 25), + (46, 26), + (26, 50), + (26, 46), + (50, 26), + (47, 27), + (27, 51), + (27, 47), + (51, 27), + (48, 28), + (28, 52), + (28, 48), + (52, 28), + (49, 31), + (31, 53), + (31, 49), + (53, 31), + (50, 32), + (32, 54), + (32, 50), + (54, 32), + (51, 33), + (33, 55), + (33, 51), + (55, 33), + (52, 34), + (34, 56), + (34, 52), + (56, 34), + (53, 35), + (35, 57), + (35, 53), + (57, 35), + (54, 36), + (36, 58), + (36, 54), + (58, 36), + (55, 37), + (37, 59), + (37, 55), + (59, 37), + (56, 38), + (38, 60), + (38, 56), + (60, 38), + (57, 41), + (41, 61), + (41, 57), + (61, 41), + (58, 42), + (42, 62), + (42, 58), + (62, 42), + (59, 43), + (43, 63), + (43, 59), + (63, 43), + (60, 44), + (44, 64), + (44, 60), + (64, 44), + ] + self.assertEqual(list(graph.edge_list()), expected_edges) + def test_heavy_square_graph_5(self): d = 5 graph = retworkx.generators.heavy_square_graph(d) @@ -105,6 +367,100 @@ def test_heavy_square_graph_5(self): ] self.assertEqual(list(graph.edge_list()), expected_edges) + def test_directed_heavy_square_graph_3(self): + d = 3 + graph = retworkx.generators.directed_heavy_square_graph(d) + self.assertEqual(len(graph), 3 * d * d - 2 * d) + self.assertEqual(len(graph.edges()), 2 * d * (d - 1) + 2 * d * (d - 1)) + expected_edges = [ + (0, 15), + (15, 1), + (1, 16), + (16, 2), + (3, 17), + (17, 4), + (4, 18), + (18, 5), + (6, 19), + (19, 7), + (7, 20), + (20, 8), + (2, 11), + (11, 5), + (3, 12), + (12, 6), + (15, 9), + (9, 17), + (16, 10), + (10, 18), + (17, 13), + (13, 19), + (18, 14), + (14, 20), + ] + self.assertEqual(list(graph.edge_list()), expected_edges) + + def test_directed_heavy_square_graph_3_bidirectional(self): + d = 3 + graph = retworkx.generators.directed_heavy_square_graph( + d, bidirectional=True + ) + self.assertEqual(len(graph), 3 * d * d - 2 * d) + self.assertEqual( + len(graph.edges()), 2 * (2 * d * (d - 1) + 2 * d * (d - 1)) + ) + expected_edges = [ + (0, 15), + (15, 1), + (15, 0), + (1, 15), + (1, 16), + (16, 2), + (16, 1), + (2, 16), + (3, 17), + (17, 4), + (17, 3), + (4, 17), + (4, 18), + (18, 5), + (18, 4), + (5, 18), + (6, 19), + (19, 7), + (19, 6), + (7, 19), + (7, 20), + (20, 8), + (20, 7), + (8, 20), + (2, 11), + (11, 5), + (11, 2), + (5, 11), + (3, 12), + (12, 6), + (12, 3), + (6, 12), + (15, 9), + (9, 17), + (9, 15), + (17, 9), + (16, 10), + (10, 18), + (10, 16), + (18, 10), + (17, 13), + (13, 19), + (13, 17), + (19, 13), + (18, 14), + (14, 20), + (14, 18), + (20, 14), + ] + self.assertEqual(list(graph.edge_list()), expected_edges) + def test_heavy_square_graph_3(self): d = 3 graph = retworkx.generators.heavy_square_graph(d) From fe32a81800e53c1de0c5e18dcf4d841e843b2484 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Mon, 23 Aug 2021 15:28:16 -0400 Subject: [PATCH 13/14] Fix edge directions --- src/generators.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/generators.rs b/src/generators.rs index 8372899e50..ce885a911e 100644 --- a/src/generators.rs +++ b/src/generators.rs @@ -1335,8 +1335,8 @@ pub fn directed_heavy_square_graph( py.None(), ); graph.add_edge( - syndrome_chunk[syndrome_chunk.len() - 1], nodes_data[i * d + (2 * d - 1)], + syndrome_chunk[syndrome_chunk.len() - 1], py.None(), ); if bidirectional { @@ -1346,23 +1346,23 @@ pub fn directed_heavy_square_graph( py.None(), ); graph.add_edge( - nodes_data[i * d + (2 * d - 1)], syndrome_chunk[syndrome_chunk.len() - 1], + nodes_data[i * d + (2 * d - 1)], py.None(), ); } } else if i % 2 == 1 { graph.add_edge(nodes_data[i * d], syndrome_chunk[0], py.None()); graph.add_edge( - syndrome_chunk[0], nodes_data[(i + 1) * d], + syndrome_chunk[0], py.None(), ); if bidirectional { graph.add_edge(syndrome_chunk[0], nodes_data[i * d], py.None()); graph.add_edge( - nodes_data[(i + 1) * d], syndrome_chunk[0], + nodes_data[(i + 1) * d], py.None(), ); } @@ -1375,8 +1375,8 @@ pub fn directed_heavy_square_graph( for (j, syndrome) in syndrome_chunk.iter().enumerate() { if j != syndrome_chunk.len() - 1 { graph.add_edge( - nodes_flag[i * (d - 1) + j], *syndrome, + nodes_flag[i * (d - 1) + j], py.None(), ); graph.add_edge( @@ -1386,8 +1386,8 @@ pub fn directed_heavy_square_graph( ); if bidirectional { graph.add_edge( - *syndrome, nodes_flag[i * (d - 1) + j], + *syndrome, py.None(), ); graph.add_edge( @@ -1402,8 +1402,8 @@ pub fn directed_heavy_square_graph( for (j, syndrome) in syndrome_chunk.iter().enumerate() { if j != 0 { graph.add_edge( - nodes_flag[i * (d - 1) + j - 1], *syndrome, + nodes_flag[i * (d - 1) + j - 1], py.None(), ); graph.add_edge( @@ -1413,8 +1413,8 @@ pub fn directed_heavy_square_graph( ); if bidirectional { graph.add_edge( - *syndrome, nodes_flag[i * (d - 1) + j - 1], + *syndrome, py.None(), ); graph.add_edge( From b63af4c97175d07b0db6f61ee74d2d6ffd77465d Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Mon, 23 Aug 2021 15:49:54 -0400 Subject: [PATCH 14/14] Update edge directions in tests --- tests/generators/test_heavy_square.py | 156 +++++++++++++------------- 1 file changed, 78 insertions(+), 78 deletions(-) diff --git a/tests/generators/test_heavy_square.py b/tests/generators/test_heavy_square.py index ef776c7577..3c639f1b6a 100644 --- a/tests/generators/test_heavy_square.py +++ b/tests/generators/test_heavy_square.py @@ -63,44 +63,44 @@ def test_directed_heavy_square_graph_5(self): (23, 64), (64, 24), (4, 29), - (29, 9), + (9, 29), (5, 30), - (30, 10), + (10, 30), (14, 39), - (39, 19), + (19, 39), (15, 40), - (40, 20), - (45, 25), + (20, 40), + (25, 45), (25, 49), - (46, 26), + (26, 46), (26, 50), - (47, 27), + (27, 47), (27, 51), - (48, 28), + (28, 48), (28, 52), - (49, 31), + (31, 49), (31, 53), - (50, 32), + (32, 50), (32, 54), - (51, 33), + (33, 51), (33, 55), - (52, 34), + (34, 52), (34, 56), - (53, 35), + (35, 53), (35, 57), - (54, 36), + (36, 54), (36, 58), - (55, 37), + (37, 55), (37, 59), - (56, 38), + (38, 56), (38, 60), - (57, 41), + (41, 57), (41, 61), - (58, 42), + (42, 58), (42, 62), - (59, 43), + (43, 59), (43, 63), - (60, 44), + (44, 60), (44, 64), ] self.assertEqual(list(graph.edge_list()), expected_edges) @@ -196,84 +196,84 @@ def test_directed_heavy_square_graph_5_bidirectional(self): (64, 23), (24, 64), (4, 29), - (29, 9), - (29, 4), (9, 29), + (29, 4), + (29, 9), (5, 30), - (30, 10), - (30, 5), (10, 30), + (30, 5), + (30, 10), (14, 39), - (39, 19), - (39, 14), (19, 39), + (39, 14), + (39, 19), (15, 40), - (40, 20), - (40, 15), (20, 40), - (45, 25), - (25, 49), + (40, 15), + (40, 20), (25, 45), + (25, 49), + (45, 25), (49, 25), - (46, 26), - (26, 50), (26, 46), + (26, 50), + (46, 26), (50, 26), - (47, 27), - (27, 51), (27, 47), + (27, 51), + (47, 27), (51, 27), - (48, 28), - (28, 52), (28, 48), + (28, 52), + (48, 28), (52, 28), - (49, 31), - (31, 53), (31, 49), + (31, 53), + (49, 31), (53, 31), - (50, 32), - (32, 54), (32, 50), + (32, 54), + (50, 32), (54, 32), - (51, 33), - (33, 55), (33, 51), + (33, 55), + (51, 33), (55, 33), - (52, 34), - (34, 56), (34, 52), + (34, 56), + (52, 34), (56, 34), - (53, 35), - (35, 57), (35, 53), + (35, 57), + (53, 35), (57, 35), - (54, 36), - (36, 58), (36, 54), + (36, 58), + (54, 36), (58, 36), - (55, 37), - (37, 59), (37, 55), + (37, 59), + (55, 37), (59, 37), - (56, 38), - (38, 60), (38, 56), + (38, 60), + (56, 38), (60, 38), - (57, 41), - (41, 61), (41, 57), + (41, 61), + (57, 41), (61, 41), - (58, 42), - (42, 62), (42, 58), + (42, 62), + (58, 42), (62, 42), - (59, 43), - (43, 63), (43, 59), + (43, 63), + (59, 43), (63, 43), - (60, 44), - (44, 64), (44, 60), + (44, 64), + (60, 44), (64, 44), ] self.assertEqual(list(graph.edge_list()), expected_edges) @@ -386,16 +386,16 @@ def test_directed_heavy_square_graph_3(self): (7, 20), (20, 8), (2, 11), - (11, 5), + (5, 11), (3, 12), - (12, 6), - (15, 9), + (6, 12), + (9, 15), (9, 17), - (16, 10), + (10, 16), (10, 18), - (17, 13), + (13, 17), (13, 19), - (18, 14), + (14, 18), (14, 20), ] self.assertEqual(list(graph.edge_list()), expected_edges) @@ -435,28 +435,28 @@ def test_directed_heavy_square_graph_3_bidirectional(self): (20, 7), (8, 20), (2, 11), - (11, 5), - (11, 2), (5, 11), + (11, 2), + (11, 5), (3, 12), - (12, 6), - (12, 3), (6, 12), - (15, 9), - (9, 17), + (12, 3), + (12, 6), (9, 15), + (9, 17), + (15, 9), (17, 9), - (16, 10), - (10, 18), (10, 16), + (10, 18), + (16, 10), (18, 10), - (17, 13), - (13, 19), (13, 17), + (13, 19), + (17, 13), (19, 13), - (18, 14), - (14, 20), (14, 18), + (14, 20), + (18, 14), (20, 14), ] self.assertEqual(list(graph.edge_list()), expected_edges)