From c2ec4004fdf5ee979717c1fee373b5e0c69d358d Mon Sep 17 00:00:00 2001 From: jhenstra <74776892+jhenstra@users.noreply.github.com> Date: Mon, 19 Feb 2024 08:55:29 +0100 Subject: [PATCH 1/8] Fix incorrect mapping update for routing env from grid size (#68). (#69) * Fix incorrect mapping update for routing env from grid size. Relabel the nodes to be indexed when creating connection graph from grid size. * Use networkx builtin function to convert node labels to integers. --- qgym/utils/input_parsing.py | 8 +++++-- qgym/utils/input_validation.py | 7 +++++-- tests/envs/routing/test_routing_env.py | 13 +++++++++--- tests/utils/test_input_validation.py | 29 +++++++++++++++++--------- 4 files changed, 40 insertions(+), 17 deletions(-) diff --git a/qgym/utils/input_parsing.py b/qgym/utils/input_parsing.py index 4434b48b..17823cd2 100644 --- a/qgym/utils/input_parsing.py +++ b/qgym/utils/input_parsing.py @@ -1,7 +1,7 @@ """This module contains function which parse user input. With parsing we mean that the user input is validated and transformed to a predictable -format. In this way, user can give different input formats, but internally we are +format. In this way, user can give different input formats, but internally we are assured that the data has the same format.""" from __future__ import annotations @@ -105,7 +105,11 @@ def parse_connection_graph( if grid_size is not None: # Generate connection grid graph - return nx.grid_graph(grid_size) + graph = nx.grid_graph(grid_size) + + # Relabel the nodes to be integers + graph = nx.convert_node_labels_to_integers(graph) + return graph raise ValueError("No valid arguments for a connection graph were given") diff --git a/qgym/utils/input_validation.py b/qgym/utils/input_validation.py index b753fd9f..3d6229fb 100644 --- a/qgym/utils/input_validation.py +++ b/qgym/utils/input_validation.py @@ -206,8 +206,8 @@ def check_adjacency_matrix(adjacency_matrix: ArrayLike) -> NDArray[Any]: def check_graph_is_valid_topology(graph: nx.Graph, name: str) -> None: - """Check if `graph` with name 'name' is an instance of ``networkx.Graph`` and check - if the graph is valid topology graph. + """Check if `graph` with name 'name' is an instance of ``networkx.Graph``, check + if the graph is valid topology graph and check if the nodes are integers. Args: graph: Graph to check. @@ -226,6 +226,9 @@ def check_graph_is_valid_topology(graph: nx.Graph, name: str) -> None: if len(graph) == 0: raise ValueError(f"'{name}' has no nodes") + if not all(isinstance(node, int) for node in graph.nodes()): + raise TypeError(f"'{name}' has nodes that are not integers") + def check_instance(x: Any, name: str, dtype: type) -> None: """Check if `x` with name 'name' is an instance of dtype. diff --git a/tests/envs/routing/test_routing_env.py b/tests/envs/routing/test_routing_env.py index 20f8704c..c27e8866 100644 --- a/tests/envs/routing/test_routing_env.py +++ b/tests/envs/routing/test_routing_env.py @@ -1,5 +1,6 @@ from __future__ import annotations +import numpy as np import pytest from stable_baselines3.common.env_checker import check_env @@ -19,6 +20,12 @@ }, ], ) -def test_validity(kwargs: dict[str, tuple[int, int] | bool]) -> None: - env = Routing(**kwargs) # type: ignore[arg-type] - check_env(env, warn=True) # todo: maybe switch this to the gym env checker +class TestEnvironment: + def test_validity(self, kwargs: dict[str, tuple[int, int] | bool]) -> None: + env = Routing(**kwargs) # type: ignore[arg-type] + check_env(env, warn=True) # todo: maybe switch this to the gym env checker + + def test_step(self, kwargs): + env = Routing(**kwargs) # type: ignore[arg-type] + obs = env.step(0)[0] + assert np.array_equal(obs["mapping"], [2, 1, 0, 3]) diff --git a/tests/utils/test_input_validation.py b/tests/utils/test_input_validation.py index b09c2547..d5dec165 100644 --- a/tests/utils/test_input_validation.py +++ b/tests/utils/test_input_validation.py @@ -153,19 +153,28 @@ def test_check_adjacency_matrix_errors(self, arg: Any) -> None: check_adjacency_matrix(arg) -def test_check_graph_is_valid_topology() -> None: - graph = nx.Graph() - msg = "'test' has no nodes" - with pytest.raises(ValueError, match=msg): +class TestGraphValidTopology: + def test_check_graph_is_valid_topology(self) -> None: + graph = nx.Graph() + msg = "'test' has no nodes" + with pytest.raises(ValueError, match=msg): + check_graph_is_valid_topology(graph, "test") + + graph.add_edge(1, 2) check_graph_is_valid_topology(graph, "test") - graph.add_edge(1, 2) - check_graph_is_valid_topology(graph, "test") + graph.add_edge(1, 1) + msg = "'test' contains self-loops" + with pytest.raises(ValueError, match=msg): + check_graph_is_valid_topology(graph, "test") + + def test_check_graph_is_valid_topology_nodes(self) -> None: + graph = nx.Graph() + graph.add_node((0, 0)) - graph.add_edge(1, 1) - msg = "'test' contains self-loops" - with pytest.raises(ValueError, match=msg): - check_graph_is_valid_topology(graph, "test") + msg = "'test' has nodes that are not integers" + with pytest.raises(TypeError, match=msg): + check_graph_is_valid_topology(graph, "test") class TestCheckInstance: From 9b61b574000f044f131e07363d48f50cbb543b87 Mon Sep 17 00:00:00 2001 From: Stan van der Linde Date: Mon, 19 Feb 2024 09:09:09 +0100 Subject: [PATCH 2/8] Styling --- qgym/utils/input_validation.py | 2 +- tests/utils/test_input_validation.py | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/qgym/utils/input_validation.py b/qgym/utils/input_validation.py index 3d6229fb..9e13cf58 100644 --- a/qgym/utils/input_validation.py +++ b/qgym/utils/input_validation.py @@ -226,7 +226,7 @@ def check_graph_is_valid_topology(graph: nx.Graph, name: str) -> None: if len(graph) == 0: raise ValueError(f"'{name}' has no nodes") - if not all(isinstance(node, int) for node in graph.nodes()): + if any(not isinstance(node, int) for node in graph): raise TypeError(f"'{name}' has nodes that are not integers") diff --git a/tests/utils/test_input_validation.py b/tests/utils/test_input_validation.py index d5dec165..bfb8bdc3 100644 --- a/tests/utils/test_input_validation.py +++ b/tests/utils/test_input_validation.py @@ -154,21 +154,24 @@ def test_check_adjacency_matrix_errors(self, arg: Any) -> None: class TestGraphValidTopology: - def test_check_graph_is_valid_topology(self) -> None: + def test_empty_graph(self) -> None: graph = nx.Graph() msg = "'test' has no nodes" with pytest.raises(ValueError, match=msg): check_graph_is_valid_topology(graph, "test") - graph.add_edge(1, 2) + def test_line_graph(self) -> None: + graph = nx.cycle_graph(2) check_graph_is_valid_topology(graph, "test") + def test_self_loop(self) -> None: + graph = nx.Graph() graph.add_edge(1, 1) msg = "'test' contains self-loops" with pytest.raises(ValueError, match=msg): check_graph_is_valid_topology(graph, "test") - def test_check_graph_is_valid_topology_nodes(self) -> None: + def test_non_int_label(self) -> None: graph = nx.Graph() graph.add_node((0, 0)) From 07ee7d9472e835b96a84a6d3e59ac599bb5a3f57 Mon Sep 17 00:00:00 2001 From: Stan van der Linde Date: Mon, 19 Feb 2024 09:19:40 +0100 Subject: [PATCH 3/8] Bugfix initial mappong truncation --- .../initial_mapping/initial_mapping_state.py | 2 +- .../test_initial_mapping_env.py | 31 ++++++++++++++++--- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/qgym/envs/initial_mapping/initial_mapping_state.py b/qgym/envs/initial_mapping/initial_mapping_state.py index c745dc40..b2c714dd 100644 --- a/qgym/envs/initial_mapping/initial_mapping_state.py +++ b/qgym/envs/initial_mapping/initial_mapping_state.py @@ -210,7 +210,7 @@ def is_truncated(self) -> bool: is truncated if the number of steps in the current episode is more than 10 times the number of nodes in the connection graph. """ - return bool(self.steps_done < self.n_nodes * 10) + return bool(self.steps_done > self.n_nodes * 10) def obtain_info(self) -> dict[str, Any]: """Obtain additional information. diff --git a/tests/envs/initial_mapping/test_initial_mapping_env.py b/tests/envs/initial_mapping/test_initial_mapping_env.py index b90b48c5..e5d57997 100644 --- a/tests/envs/initial_mapping/test_initial_mapping_env.py +++ b/tests/envs/initial_mapping/test_initial_mapping_env.py @@ -29,11 +29,32 @@ def small_env(small_graph: nx.Graph) -> InitialMapping: return InitialMapping(0.5, connection_graph=small_graph) -def test_validity(small_env: InitialMapping) -> None: - check_env( - small_env, warn=True - ) # todo: maybe switch this to the gymnasium env checker - assert True +class TestEnvironment: + + def test_validity(self, small_env: InitialMapping) -> None: + # todo: maybe switch this to the gymnasium env checker + check_env(small_env, warn=True) + + def test_episode(self, small_env: InitialMapping): + obs, reward, is_done, truncated, _ = small_env.step(np.array([0, 1])) + assert np.array_equal(obs["mapping"], [1, 2]) + assert reward == 0 + assert not is_done + assert not truncated + + obs, reward, is_done, truncated, _ = small_env.step(np.array([1, 0])) + assert np.array_equal(obs["mapping"], [1, 0]) + assert is_done + assert not truncated + + def test_truncation(self, small_env: InitialMapping): + truncated = False + for _ in range(10000): + _, _, is_done, truncated, _ = small_env.step(np.array([0, 0])) + if is_done or truncated: + break + assert not is_done + assert truncated @pytest.mark.parametrize( From ec308bd0f248f954d3def4fae54c13b47d353f12 Mon Sep 17 00:00:00 2001 From: Stan van der Linde Date: Mon, 19 Feb 2024 09:22:28 +0100 Subject: [PATCH 4/8] Update tests --- tests/envs/initial_mapping/test_initial_mapping_env.py | 4 ++-- tests/envs/routing/test_routing_env.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/envs/initial_mapping/test_initial_mapping_env.py b/tests/envs/initial_mapping/test_initial_mapping_env.py index e5d57997..3b948e58 100644 --- a/tests/envs/initial_mapping/test_initial_mapping_env.py +++ b/tests/envs/initial_mapping/test_initial_mapping_env.py @@ -37,13 +37,13 @@ def test_validity(self, small_env: InitialMapping) -> None: def test_episode(self, small_env: InitialMapping): obs, reward, is_done, truncated, _ = small_env.step(np.array([0, 1])) - assert np.array_equal(obs["mapping"], [1, 2]) + np.testing.assert_array_equal(obs["mapping"], [1, 2]) assert reward == 0 assert not is_done assert not truncated obs, reward, is_done, truncated, _ = small_env.step(np.array([1, 0])) - assert np.array_equal(obs["mapping"], [1, 0]) + np.testing.assert_array_equal(obs["mapping"], [1, 0]) assert is_done assert not truncated diff --git a/tests/envs/routing/test_routing_env.py b/tests/envs/routing/test_routing_env.py index c27e8866..31384c84 100644 --- a/tests/envs/routing/test_routing_env.py +++ b/tests/envs/routing/test_routing_env.py @@ -28,4 +28,4 @@ def test_validity(self, kwargs: dict[str, tuple[int, int] | bool]) -> None: def test_step(self, kwargs): env = Routing(**kwargs) # type: ignore[arg-type] obs = env.step(0)[0] - assert np.array_equal(obs["mapping"], [2, 1, 0, 3]) + np.testing.assert_array_equal(obs["mapping"], [2, 1, 0, 3]) From 5760eb90fae6e7704a67dc886e2aff2f7fd1cf54 Mon Sep 17 00:00:00 2001 From: Stan van der Linde Date: Mon, 19 Feb 2024 09:23:30 +0100 Subject: [PATCH 5/8] Bump version number --- qgym/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qgym/__init__.py b/qgym/__init__.py index 910eaca6..8b7667cd 100644 --- a/qgym/__init__.py +++ b/qgym/__init__.py @@ -25,4 +25,4 @@ """ -__version__ = "0.2.0" +__version__ = "0.2.1" From 6341959816bcc37960d64afdc6a0f9807ff7c9bf Mon Sep 17 00:00:00 2001 From: Willem de kok Date: Fri, 23 Feb 2024 15:42:25 +0100 Subject: [PATCH 6/8] update details Rares/Joris --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c944cdc8..945aca5c 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,6 @@ Building qgym is a joint effort. - [Tariq Bontekoe](https://nl.linkedin.com/in/tariq-bontekoe-53214817a) - [Sebastian Feld](https://nl.linkedin.com/in/sebastian-feld?) -### Power users -- Joris Henstra -- Rares Oancea +### Contributors and Power Users +- [Joris Henstra](https://www.linkedin.com/in/jorishenstra/) +- [Rares Oancea](https://www.linkedin.com/in/rares-adrian-oancea-8a67b0204/) From e0ca4ebeb632a38b6b03afe6358b3660e69ca7ed Mon Sep 17 00:00:00 2001 From: Willem de kok Date: Fri, 23 Feb 2024 15:47:08 +0100 Subject: [PATCH 7/8] update paper publication details. --- README.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 945aca5c..2a5540a6 100644 --- a/README.md +++ b/README.md @@ -27,14 +27,18 @@ Currently `qgym` has support for Python 3.7, 3.8, 3.9, 3.10 and 3.11. ## Publication The paper on `qgym` has been presented in the [1st International Workshop on Quantum Machine Learning: From Foundations to Applications (QML@QCE'23)](https://qml.lfdr.de/2023/). +The publication can be found on [computer.org](https://www.computer.org/csdl/proceedings-article/qce/2023/432302a026/1SuQRWR5uCI) You can find the preprint of the paper on [arxiv](https://arxiv.org/pdf/2308.02536.pdf). ```terminal -@article{van2023qgym, +@inproceedings{van2023qgym, title={qgym: A Gym for training and benchmarking RL-based quantum compilation}, - author={van der Linde, Stan and de Kok, Willem and Bontekoe, Tariq and Feld, Sebastian}, - journal={arXiv preprint arXiv:2308.02536}, - year={2023} + author={Van Der Linde, Stan and De Kok, Willem and Bontekoe, Tariq and Feld, Sebastian}, + booktitle={2023 IEEE International Conference on Quantum Computing and Engineering (QCE)}, + volume={2}, + pages={26--30}, + year={2023}, + organization={IEEE} } ``` ## Team From 005e25bd3df9a680fe9fb835562247833d9bdb5d Mon Sep 17 00:00:00 2001 From: Stan van der Linde Date: Fri, 23 Feb 2024 16:49:33 +0100 Subject: [PATCH 8/8] Update root_docs --- docs_files/templates/root_doc.rst_t | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/docs_files/templates/root_doc.rst_t b/docs_files/templates/root_doc.rst_t index e42597e2..4c9860a3 100644 --- a/docs_files/templates/root_doc.rst_t +++ b/docs_files/templates/root_doc.rst_t @@ -45,15 +45,19 @@ Currently :obj:`~qgym` has support for Python 3.7, 3.8, 3.9, 3.10 and 3.11. Publication ============ The paper on :obj:`~qgym` has been presented in the `1st International Workshop on Quantum Machine Learning: From Foundations to Applications (QML@QCE'23) `_. +The publication can be found on `computer.org `_. You can find the preprint of the paper on `arxiv `_. .. code-block:: console - @article{van2023qgym, + @inproceedings{van2023qgym, title={qgym: A Gym for training and benchmarking RL-based quantum compilation}, - author={van der Linde, Stan and de Kok, Willem and Bontekoe, Tariq and Feld, Sebastian}, - journal={arXiv preprint arXiv:2308.02536}, - year={2023} + author={Van Der Linde, Stan and De Kok, Willem and Bontekoe, Tariq and Feld, Sebastian}, + booktitle={2023 IEEE International Conference on Quantum Computing and Engineering (QCE)}, + volume={2}, + pages={26--30}, + year={2023}, + organization={IEEE} } Team @@ -67,11 +71,10 @@ Core developers - `Tariq Bontekoe `_ - `Sebastian Feld `_ -Power users ------------- -- Joris Henstra -- Rares Oancea - +Contributors and Power Users +----------------------------- +- `Joris Henstra `_ +- `Rares Oancea `_ .. toctree:: :maxdepth: {{ mastertocmaxdepth }}