From 6895b4c4852f3d3db0ec054a6772bcea911632ca Mon Sep 17 00:00:00 2001 From: David Bakker Date: Thu, 9 Jul 2026 07:06:19 +0200 Subject: [PATCH 1/5] Merge develop into local branch --- opensquirrel/circuit_builder.py | 4 + .../ir/default_gates/single_qubit_gates.py | 1 - opensquirrel/ir/ir.py | 4 + uv.lock | 374 ++++++++---------- 4 files changed, 177 insertions(+), 206 deletions(-) diff --git a/opensquirrel/circuit_builder.py b/opensquirrel/circuit_builder.py index 860cbcda..e6d476b1 100644 --- a/opensquirrel/circuit_builder.py +++ b/opensquirrel/circuit_builder.py @@ -190,6 +190,10 @@ def _expand_sgmq_args(self, attr: str, args: tuple[Any, ...]) -> list[tuple[Any, remaining_args = args[1:] return [(first, *remaining_args) for first in expanded_first] + def clear(self) -> None: + """Clears all statements from the IR.""" + self.ir.clear() + def to_circuit(self) -> Circuit: """Build the circuit. diff --git a/opensquirrel/ir/default_gates/single_qubit_gates.py b/opensquirrel/ir/default_gates/single_qubit_gates.py index 8fed12fc..593aa859 100644 --- a/opensquirrel/ir/default_gates/single_qubit_gates.py +++ b/opensquirrel/ir/default_gates/single_qubit_gates.py @@ -65,7 +65,6 @@ def __init__(self, qubit: QubitLike) -> None: qubit=qubit, gate_semantic=BsrNoParams(axis=(1, 0, 0), angle=-pi / 2, phase=-pi / 4), name="mX90" ) - class Y(SingleQubitGate): def __init__(self, qubit: QubitLike) -> None: super().__init__(qubit=qubit, gate_semantic=BsrNoParams(axis=(0, 1, 0), angle=pi, phase=pi / 2), name="Y") diff --git a/opensquirrel/ir/ir.py b/opensquirrel/ir/ir.py index 1140846e..e3b06052 100644 --- a/opensquirrel/ir/ir.py +++ b/opensquirrel/ir/ir.py @@ -162,3 +162,7 @@ def __eq__(self, other: object) -> bool: def __repr__(self) -> str: return f"IR: {self.statements}" + + def clear(self) -> None: + """Clears all statements from the IR.""" + self.statements.clear() diff --git a/uv.lock b/uv.lock index 909d5ed1..c4f02646 100644 --- a/uv.lock +++ b/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 3 +revision = 2 requires-python = ">=3.10.0" resolution-markers = [ "python_full_version >= '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", @@ -7,13 +7,17 @@ resolution-markers = [ "python_full_version >= '3.14' and sys_platform == 'emscripten'", "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", "python_full_version == '3.13.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.13' and platform_machine == 'ARM64' and sys_platform == 'win32'", + "python_full_version == '3.12.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", "python_full_version == '3.13.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.13' and platform_machine != 'ARM64' and sys_platform == 'win32'", + "python_full_version == '3.12.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", "python_full_version == '3.13.*' and sys_platform == 'emscripten'", - "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform == 'emscripten'", + "python_full_version == '3.12.*' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.12.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", "python_full_version < '3.11' and platform_machine == 'ARM64' and sys_platform == 'win32'", "(python_full_version < '3.11' and platform_machine != 'ARM64') or (python_full_version < '3.11' and sys_platform != 'win32')", ] @@ -49,13 +53,17 @@ resolution-markers = [ "python_full_version >= '3.14' and sys_platform == 'emscripten'", "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", "python_full_version == '3.13.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.13' and platform_machine == 'ARM64' and sys_platform == 'win32'", + "python_full_version == '3.12.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", "python_full_version == '3.13.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.13' and platform_machine != 'ARM64' and sys_platform == 'win32'", + "python_full_version == '3.12.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", "python_full_version == '3.13.*' and sys_platform == 'emscripten'", - "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform == 'emscripten'", + "python_full_version == '3.12.*' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.12.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ { name = "cloudpickle", marker = "python_full_version >= '3.11'" }, @@ -334,13 +342,17 @@ resolution-markers = [ "python_full_version >= '3.14' and sys_platform == 'emscripten'", "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", "python_full_version == '3.13.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.13' and platform_machine == 'ARM64' and sys_platform == 'win32'", + "python_full_version == '3.12.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", "python_full_version == '3.13.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.13' and platform_machine != 'ARM64' and sys_platform == 'win32'", + "python_full_version == '3.12.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", "python_full_version == '3.13.*' and sys_platform == 'emscripten'", - "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform == 'emscripten'", + "python_full_version == '3.12.*' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.12.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ { name = "xarray", version = "2026.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, @@ -719,13 +731,17 @@ resolution-markers = [ "python_full_version >= '3.14' and sys_platform == 'emscripten'", "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", "python_full_version == '3.13.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.13' and platform_machine == 'ARM64' and sys_platform == 'win32'", + "python_full_version == '3.12.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", "python_full_version == '3.13.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.13' and platform_machine != 'ARM64' and sys_platform == 'win32'", + "python_full_version == '3.12.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", "python_full_version == '3.13.*' and sys_platform == 'emscripten'", - "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform == 'emscripten'", + "python_full_version == '3.12.*' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.12.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, @@ -2009,33 +2025,33 @@ wheels = [ [[package]] name = "libqasm" -version = "1.3.0" +version = "1.4.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/f6/01/8e5abbbd316a9e91e205d19b6d0a35c4adb2b1143370ad5d366770bfaad1/libqasm-1.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3679f0ad1566d2596aa97c7937662a13fad8849cff6cc5c538996b779ef74c6c", size = 1202546, upload-time = "2026-04-18T15:10:49.897Z" }, - { url = "https://files.pythonhosted.org/packages/18/78/f37860fcc8297a52cd88f981935b1839f0b55f4cf3b05d39e7821d270774/libqasm-1.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:87256faa4356044e554221463cd3f6ef93ef822c577a2836b0789aac1777d992", size = 1157794, upload-time = "2026-04-18T15:10:51.868Z" }, - { url = "https://files.pythonhosted.org/packages/b9/47/4a92ca3fc52b68bf108a4603a429e8d3489ccd3d0ae1cef5fefeb31fda84/libqasm-1.3.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:56ff14d68210843cccb5b98da5e35a63a0de368a4978a9b32c13e0f78afa8bfa", size = 1769351, upload-time = "2026-04-18T15:10:53.126Z" }, - { url = "https://files.pythonhosted.org/packages/53/77/2734032eddbea1685bbdfd9e1cc2faeeb964f9948c38a00fa81e9e989f5d/libqasm-1.3.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f4ba2510b5bbb5fbfbb2606085ccf0579497aeb3f23fa686be824461593f63b3", size = 1927785, upload-time = "2026-04-18T15:10:54.858Z" }, - { url = "https://files.pythonhosted.org/packages/d5/f8/a1f3aa90c971ab868630f1815f6d9f14612fa53c5daf6444748a12a044da/libqasm-1.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:dbee296d5507fff771b2294533fb90c45597e51c42297d3366b8fb5ae8abb0c4", size = 806275, upload-time = "2026-04-18T15:10:56.007Z" }, - { url = "https://files.pythonhosted.org/packages/62/cf/dda7795e1f695fd19bcc90ea28fab32f9d26160074d2127d1bbc25b329b3/libqasm-1.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1ab828b0c21733930d40dea9eeb64bf5c88afc9c5913dfcb9c98b73a9ebb8faa", size = 1202545, upload-time = "2026-04-18T15:10:58.452Z" }, - { url = "https://files.pythonhosted.org/packages/cf/16/ed30dbd1e535a75acc53b6379f73ee6990bb885834cc4c72291c5b6037a4/libqasm-1.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5e2883faa4545e6f6da07bea663a92d0da1860082f8e883da66b7d9c8644a118", size = 1157793, upload-time = "2026-04-18T15:11:00.059Z" }, - { url = "https://files.pythonhosted.org/packages/cf/5a/e131649679a706be834b6b53657b642097216aecc682143f30c14c187ddd/libqasm-1.3.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cbd2c6ba2216def6658416e8f05aa80668d33f8703a5c91e360018d5c63390cc", size = 1769376, upload-time = "2026-04-18T15:11:01.983Z" }, - { url = "https://files.pythonhosted.org/packages/9d/00/7ef36af6f15ba804a315371b33b49e8d23ed4cc236cd5ca9e803e72c05ea/libqasm-1.3.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0330475e7c72d4d76ac212180c783a939a93235f761c251df95529e61c479d29", size = 1927790, upload-time = "2026-04-18T15:11:03.535Z" }, - { url = "https://files.pythonhosted.org/packages/58/1b/ca7eb4eeb04b2d4280e62d0ab6af8b2efe731d01cb6c916b8af1e74efae2/libqasm-1.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:a7964c8fae178e522bffc7fed4665923840837d9ccf19bb81f0972f9e4d6cf12", size = 806402, upload-time = "2026-04-18T15:11:05.072Z" }, - { url = "https://files.pythonhosted.org/packages/97/fd/1fcb8182c16a1dbf803a486765e48f5ab925eb195bd29bb2e90d40040866/libqasm-1.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:48cbea57f69097e7c2b0de1c7a705c20d73e5c5264dc4b752524383abb21b625", size = 1202347, upload-time = "2026-04-18T15:11:06.575Z" }, - { url = "https://files.pythonhosted.org/packages/ff/79/ad66f23f86681f7e8c2ce34a75426de9b065c012eb502fd1f9c33ffed5f4/libqasm-1.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8310d6eebf778047a3c40624be5feccbfe41f149ec5961dfa0787cc208992fa9", size = 1158835, upload-time = "2026-04-18T15:11:08.195Z" }, - { url = "https://files.pythonhosted.org/packages/c0/34/1e3fea6cce99becc7a746367117ad6acc41e9eacfddae5a65a9875c61dfd/libqasm-1.3.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:06303f882c6cde3eb6d06cf9b941a6d6a986162eb0c7abf3962ea578a36dc3c9", size = 1769228, upload-time = "2026-04-18T15:11:09.375Z" }, - { url = "https://files.pythonhosted.org/packages/45/33/ac0432025860a76e3c0ce0d9b0843412c65ffe7738e1bfea3e65153900d7/libqasm-1.3.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:53f223a76430a2c553eb597009b3e74092ffab517e504443cd82b7baec79d3d4", size = 1928021, upload-time = "2026-04-18T15:11:10.691Z" }, - { url = "https://files.pythonhosted.org/packages/47/80/418cc656acd2fd691bb0146886ade8b1639adf5f9cd8c9e8f1f31dba2b51/libqasm-1.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:d84fbb9a72e0613831c300c78e8461f1024af54c83c8e7a30a41c09db62962c9", size = 807002, upload-time = "2026-04-18T15:11:12.638Z" }, - { url = "https://files.pythonhosted.org/packages/59/77/6175ac7a3f2110d3bda1761e516918d6c6abf9af411867cf53c71ada8361/libqasm-1.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7d9ef2ce88fab529c3778160a9dca0e25a9b8cc0547ba53b4f776a3896ef4be2", size = 1202102, upload-time = "2026-04-18T15:11:13.975Z" }, - { url = "https://files.pythonhosted.org/packages/2e/80/2ebe36a5e155e81ac58dde0c453d7d98ccd64d03722b2f1a66ab2369939b/libqasm-1.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2af7874a8de5ffafcef2e487be623051207a6ba4bdb02f6d0962bcd3367ab7df", size = 1158643, upload-time = "2026-04-18T15:11:15.527Z" }, - { url = "https://files.pythonhosted.org/packages/79/bb/a4df48441f9c2878cb50c222b423ae7af7e566d74eb17a55dd7433388384/libqasm-1.3.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f98dc80b28398fa2d26aa96e8c7e3264c683da85d099cbaf144b11995731be75", size = 1768980, upload-time = "2026-04-18T15:11:17.588Z" }, - { url = "https://files.pythonhosted.org/packages/ef/37/5dc8e9a12e69d0f08ab2301541602e04a288bd26b13ca612086318849c48/libqasm-1.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2fb0b050169331f422d0772f12ea79ae3c17734ae657e6a51d3c2b8c55dafefa", size = 1927058, upload-time = "2026-04-18T15:11:19.108Z" }, - { url = "https://files.pythonhosted.org/packages/24/f7/ac9f64d5da0d1beb38895156c66aa9212343ac8bca4ec1dd3721e538b047/libqasm-1.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:7e5098d54f82bb28215b920e12e6d172c86f0847a6e2a8dbee1307c0f40bd607", size = 806459, upload-time = "2026-04-18T15:11:21.947Z" }, + { url = "https://files.pythonhosted.org/packages/20/9e/210d0a80d4466527f9528f0036adcc54ab52743f68fb6d0ad799dfb77217/libqasm-1.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d1b49b2fd0fd9c1deaf29bfa807ba228283227b2563ad324e50d49825b8402be", size = 1208679, upload-time = "2026-06-15T11:36:39.527Z" }, + { url = "https://files.pythonhosted.org/packages/dc/e1/3112d30b26992a33e08bb80983f5c0c2f9097f62d1400b22f5f640ee6c81/libqasm-1.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:09d98c4e398c5668ec5023487ec52505644508e1626b090f4e9f0bf580d0ab62", size = 1163770, upload-time = "2026-06-15T11:36:41.227Z" }, + { url = "https://files.pythonhosted.org/packages/9e/71/9c5b9c7eb6a389570445199f105585470306323c066826dba88cae785cbd/libqasm-1.4.1-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:794edfdf45567d58b11cc82b7687621703ae6a972a595427f46af98c29024553", size = 1774396, upload-time = "2026-06-15T11:36:43.341Z" }, + { url = "https://files.pythonhosted.org/packages/a0/0e/4b48025e00cdfde73c35f2002ea5ed0cf7a0ba85e645f422e353bce3391c/libqasm-1.4.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d546a49b0aa9799e941f34bfb835478e55dcf40e626ce8aa4191f90b6198799d", size = 1933511, upload-time = "2026-06-15T11:36:45.117Z" }, + { url = "https://files.pythonhosted.org/packages/aa/80/1a44e550d0194d0c8e9cfe8f3a903c6d102a4582aadd3f6978a79c79a088/libqasm-1.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:15de3df1afa1b68e6a0ca0b6a5bb824796c18085f86c7c5b8fbdf650b1f11f9d", size = 798493, upload-time = "2026-06-15T11:36:46.5Z" }, + { url = "https://files.pythonhosted.org/packages/c3/f2/55f8161ba2745d0fb0a90ee63761adb669c2b9fe3c4c27485b6e63026dcc/libqasm-1.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7cc92f4de7c056ffac4d0787095407ecf25b0bb08b1c19b38bbbe2dd47507cd4", size = 1208678, upload-time = "2026-06-15T11:36:48.239Z" }, + { url = "https://files.pythonhosted.org/packages/02/7d/7cc1bb7a6892921bfb00c0110115468984276069cd5f236272b547d8abbb/libqasm-1.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:da41eca44b765108ce817856e9e9e3c56cb9147ff5e4dc0783845164b38fbfa1", size = 1163764, upload-time = "2026-06-15T11:36:49.843Z" }, + { url = "https://files.pythonhosted.org/packages/9b/78/d364c3d9d40474d74677874963c2e4aa9949ca23a6fa4c32ecb680520340/libqasm-1.4.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:65765d22aa91b6c9a814981ce10bd53f365714b8e844175b59ac6a5a2dbb9cfa", size = 1774414, upload-time = "2026-06-15T11:36:51.838Z" }, + { url = "https://files.pythonhosted.org/packages/31/a8/59f0296b5aaa92763c9bde635f6e3c5f33c90e70f58752b8ca57efe0820a/libqasm-1.4.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:df260702453d1c72c7ec020a32277ffc29f9a5a5a32ce607fa390bd840c9619a", size = 1933509, upload-time = "2026-06-15T11:36:54.199Z" }, + { url = "https://files.pythonhosted.org/packages/b6/8a/3ede0c82b530ee9fc2c1159e5452c6bae0474e9535db504373c55d65250c/libqasm-1.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:bced406f8a983234d1c5f02b35195125ea07f59352ddcdb4ea5b96eeabcf6e40", size = 798518, upload-time = "2026-06-15T11:36:56.38Z" }, + { url = "https://files.pythonhosted.org/packages/2a/db/af17c235408b2a55f0ef2a6e766c91276957da05c352daf161c42976f92a/libqasm-1.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:207d7e2c9509d5d0f5b7409dff4d1a455cbe4ef7e3654f743794bf366cabf834", size = 1208370, upload-time = "2026-06-15T11:36:58.51Z" }, + { url = "https://files.pythonhosted.org/packages/35/82/1cf6be1efaed9eaa705a3fc80230af878714747976cc9f84476e861b7d31/libqasm-1.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:671073b95e175138fb349045303c9576a11024ecc13ad4357ab28810cb5915fe", size = 1164710, upload-time = "2026-06-15T11:37:00.229Z" }, + { url = "https://files.pythonhosted.org/packages/90/7d/1fdbc0ab5ef9f0a82a6ae6b81c64066026df09e5656d22df62287dd4010d/libqasm-1.4.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5b0a36a2db04680cc1fd80f87f3ee83b741670caf2c6751defd6200501a1ab15", size = 1774298, upload-time = "2026-06-15T11:37:01.829Z" }, + { url = "https://files.pythonhosted.org/packages/4a/04/4a73079af8dc53087009371ea548537e1c13dcd3c61d989b2afbff287bdc/libqasm-1.4.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f6544d80363e8676b704cdfad41b6a812b6263806d262acc8024fd815a8bcc59", size = 1933834, upload-time = "2026-06-15T11:37:03.502Z" }, + { url = "https://files.pythonhosted.org/packages/fd/f5/183cfd1bdfad0a1454e017473c9c67606bf2cba069ad6501f7b33ea07d1e/libqasm-1.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:42860d8195b7a48021fadc6dc4ad331c50b0bace4794bf301dc123a5fa78ea5b", size = 799690, upload-time = "2026-06-15T11:37:05.052Z" }, + { url = "https://files.pythonhosted.org/packages/6e/3b/0ddedcffd54cbbd92e9f4a8efb708e1d5635acf71e9478c495aced527d9a/libqasm-1.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4f48ec76263495d9e17356c7390b246b422b3853f9687108b517e72cd9895521", size = 1208344, upload-time = "2026-06-15T11:37:06.88Z" }, + { url = "https://files.pythonhosted.org/packages/24/70/de3d0313a47bf91424622aa355ea3660fe9576080082fb16a15e5e66a05b/libqasm-1.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7437c8c12fdc58fd7f00a72c3b528ffa172e6206425d037acee727ea1b4cf4fc", size = 1164573, upload-time = "2026-06-15T11:37:08.395Z" }, + { url = "https://files.pythonhosted.org/packages/5b/da/16b8c500afc67b41d6a0d875e747d6bdc478a520b9b9feb8d21d8e9e3d47/libqasm-1.4.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f1e5de4f3faa3b10f8fb89dba3e753e3d05684896848aad01eb6886907589c15", size = 1773815, upload-time = "2026-06-15T11:37:10.01Z" }, + { url = "https://files.pythonhosted.org/packages/9d/87/7aff2de11e7e51adfcb6c95a73435c651460cf68fc1297c02de1ce42d7d2/libqasm-1.4.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dabbc6d84b99ec5de34ebf735c3dd3d210bc9ea2d968660071076a2034738060", size = 1933184, upload-time = "2026-06-15T11:37:11.703Z" }, + { url = "https://files.pythonhosted.org/packages/e4/81/6a323ac1c1f8dee4435d166e65aa345b375a7eb338f3d5caf79f5af1e1c2/libqasm-1.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:87735047e8bcfe06d2e2398d18a4134b0b7ac9519752e29ae04fdf3e028a8daf", size = 799833, upload-time = "2026-06-15T11:37:13.253Z" }, ] [[package]] @@ -2752,13 +2768,17 @@ resolution-markers = [ "python_full_version >= '3.14' and sys_platform == 'emscripten'", "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", "python_full_version == '3.13.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.13' and platform_machine == 'ARM64' and sys_platform == 'win32'", + "python_full_version == '3.12.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", "python_full_version == '3.13.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.13' and platform_machine != 'ARM64' and sys_platform == 'win32'", + "python_full_version == '3.12.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", "python_full_version == '3.13.*' and sys_platform == 'emscripten'", - "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform == 'emscripten'", + "python_full_version == '3.12.*' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.12.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", "(python_full_version < '3.11' and platform_machine != 'ARM64') or (python_full_version < '3.11' and sys_platform != 'win32')", ] dependencies = [ @@ -2817,13 +2837,17 @@ resolution-markers = [ "python_full_version >= '3.14' and sys_platform == 'emscripten'", "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", "python_full_version == '3.13.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.13' and platform_machine == 'ARM64' and sys_platform == 'win32'", + "python_full_version == '3.12.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", "python_full_version == '3.13.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.13' and platform_machine != 'ARM64' and sys_platform == 'win32'", + "python_full_version == '3.12.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", "python_full_version == '3.13.*' and sys_platform == 'emscripten'", - "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform == 'emscripten'", + "python_full_version == '3.12.*' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.12.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] sdist = { url = "https://files.pythonhosted.org/packages/6a/51/63fe664f3908c97be9d2e4f1158eb633317598cfa6e1fc14af5383f17512/networkx-3.6.1.tar.gz", hash = "sha256:26b7c357accc0c8cde558ad486283728b65b6a95d85ee1cd66bafab4c8168509", size = 2517025, upload-time = "2025-12-08T17:02:39.908Z" } wheels = [ @@ -2899,13 +2923,17 @@ resolution-markers = [ "python_full_version >= '3.14' and sys_platform == 'emscripten'", "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", "python_full_version == '3.13.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.13' and platform_machine == 'ARM64' and sys_platform == 'win32'", + "python_full_version == '3.12.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", "python_full_version == '3.13.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.13' and platform_machine != 'ARM64' and sys_platform == 'win32'", + "python_full_version == '3.12.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", "python_full_version == '3.13.*' and sys_platform == 'emscripten'", - "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform == 'emscripten'", + "python_full_version == '3.12.*' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.12.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, @@ -3003,13 +3031,17 @@ resolution-markers = [ "python_full_version >= '3.14' and sys_platform == 'emscripten'", "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", "python_full_version == '3.13.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.13' and platform_machine == 'ARM64' and sys_platform == 'win32'", + "python_full_version == '3.12.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", "python_full_version == '3.13.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.13' and platform_machine != 'ARM64' and sys_platform == 'win32'", + "python_full_version == '3.12.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", "python_full_version == '3.13.*' and sys_platform == 'emscripten'", - "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform == 'emscripten'", + "python_full_version == '3.12.*' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.12.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] sdist = { url = "https://files.pythonhosted.org/packages/d7/9f/b8cef5bffa569759033adda9481211426f12f53299629b410340795c2514/numpy-2.4.4.tar.gz", hash = "sha256:2d390634c5182175533585cc89f3608a4682ccb173cc9bb940b2881c8d6f8fa0", size = 20731587, upload-time = "2026-03-29T13:22:01.298Z" } wheels = [ @@ -3279,8 +3311,7 @@ examples = [ ] export = [ { name = "pyqt5-qt5", marker = "sys_platform != 'darwin'" }, - { name = "qcodes", version = "0.52.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' and sys_platform != 'darwin'" }, - { name = "qcodes", version = "0.56.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' and sys_platform != 'darwin'" }, + { name = "qcodes", marker = "sys_platform != 'darwin'" }, { name = "quantify-scheduler", marker = "sys_platform != 'darwin'" }, { name = "spirack", marker = "sys_platform != 'darwin'" }, ] @@ -3292,7 +3323,7 @@ qgym-mapper = [ [package.metadata] requires-dist = [ - { name = "libqasm", specifier = "==1.3.0" }, + { name = "libqasm", specifier = "==1.4.1" }, { name = "networkx", specifier = ">=3.4.2" }, { name = "numpy", specifier = ">=2.2.6" }, { name = "scipy", specifier = ">=1.15.3" }, @@ -3327,13 +3358,13 @@ examples = [{ name = "jupyter", specifier = ">=1.0.0,<2" }] export = [ { name = "pyqt5-qt5", marker = "sys_platform != 'darwin'", specifier = "==5.15.2" }, { name = "qcodes", marker = "sys_platform != 'darwin'", specifier = "<0.57.0" }, - { name = "quantify-scheduler", marker = "sys_platform != 'darwin'", specifier = "==0.28.0" }, - { name = "spirack", marker = "sys_platform != 'darwin'", specifier = "<=0.2.17" }, + { name = "quantify-scheduler", marker = "sys_platform != 'darwin'", specifier = "==0.28.1" }, + { name = "spirack", marker = "sys_platform != 'darwin'", specifier = "<=0.2.19" }, ] qgym-mapper = [ { name = "qgym", specifier = "==0.3.1" }, { name = "sb3-contrib", specifier = "==2.8.0" }, - { name = "stable-baselines3", specifier = "==2.8.0" }, + { name = "stable-baselines3", specifier = "==2.9.0" }, ] [[package]] @@ -3466,13 +3497,17 @@ resolution-markers = [ "python_full_version >= '3.14' and sys_platform == 'emscripten'", "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", "python_full_version == '3.13.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.13' and platform_machine == 'ARM64' and sys_platform == 'win32'", + "python_full_version == '3.12.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", "python_full_version == '3.13.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.13' and platform_machine != 'ARM64' and sys_platform == 'win32'", + "python_full_version == '3.12.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", "python_full_version == '3.13.*' and sys_platform == 'emscripten'", - "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform == 'emscripten'", + "python_full_version == '3.12.*' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.12.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, @@ -4257,44 +4292,14 @@ wheels = [ name = "pyvisa" version = "1.15.0" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.11' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "(python_full_version < '3.11' and platform_machine != 'ARM64') or (python_full_version < '3.11' and sys_platform != 'win32')", -] dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/52/04/1833acfa4ddefc5cdcfd76607f1bf494e7b7b658d890626e23026655d599/pyvisa-1.15.0.tar.gz", hash = "sha256:cec3cb91703a2849f6faa42b1ecd7689a0175baabff8ca33fce9f45934ce45e6", size = 236289, upload-time = "2025-04-01T15:52:25.377Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/a1/12/4979772f36acceb57664bde282fc7bd3e67f8d0ce85f2a521a05e90baaa5/pyvisa-1.15.0-py3-none-any.whl", hash = "sha256:e3ac8d9e863fbdbbe7e6d4d91401bceb7914d1c4a558a89b2cc755789f1e8309", size = 179199, upload-time = "2025-04-01T15:52:23.631Z" }, ] -[[package]] -name = "pyvisa" -version = "1.16.2" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.13.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.13' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.13.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.13' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.13.*' and sys_platform == 'emscripten'", - "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform == 'emscripten'", - "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32'", -] -dependencies = [ - { name = "typing-extensions", marker = "python_full_version >= '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/13/72/a50adff945e66f2161a44b243970f809de19152a2e7da0473ae8ff96d642/pyvisa-1.16.2.tar.gz", hash = "sha256:75beb93eeafe20a50be5726fa4e3a645948c93d86819f9c1f8c542efa4085a38", size = 238729, upload-time = "2026-02-27T12:14:08.54Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e4/a9/776da4f397003cca093659524c3526590522f81b642936838428c11274e6/pyvisa-1.16.2-py3-none-any.whl", hash = "sha256:54f034adafd3e8d1858d57cdafec64e920444f4b84b31c9fd17487fbad0a197a", size = 181413, upload-time = "2026-02-27T12:14:07.301Z" }, -] - [[package]] name = "pywinpty" version = "3.0.3" @@ -4477,8 +4482,7 @@ dependencies = [ { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "pysquashfsimage" }, - { name = "qcodes", version = "0.52.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "qcodes", version = "0.56.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "qcodes" }, { name = "spirack" }, { name = "strenum" }, { name = "typing-extensions" }, @@ -4489,94 +4493,44 @@ sdist = { url = "https://files.pythonhosted.org/packages/bf/1d/d0cb501ba547e375f name = "qcodes" version = "0.52.0" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.11' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "(python_full_version < '3.11' and platform_machine != 'ARM64') or (python_full_version < '3.11' and sys_platform != 'win32')", -] dependencies = [ - { name = "broadbean", marker = "python_full_version < '3.11'" }, + { name = "broadbean" }, { name = "cf-xarray", version = "0.10.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "dask", marker = "python_full_version < '3.11'" }, - { name = "h5netcdf", marker = "python_full_version < '3.11'" }, - { name = "h5py", marker = "python_full_version < '3.11'" }, - { name = "ipykernel", marker = "python_full_version < '3.11'" }, - { name = "ipython", marker = "python_full_version < '3.11'" }, - { name = "ipywidgets", marker = "python_full_version < '3.11'" }, - { name = "jsonschema", marker = "python_full_version < '3.11'" }, - { name = "matplotlib", marker = "python_full_version < '3.11'" }, + { name = "cf-xarray", version = "0.10.11", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "dask" }, + { name = "h5netcdf" }, + { name = "h5py" }, + { name = "ipykernel" }, + { name = "ipython" }, + { name = "ipywidgets" }, + { name = "jsonschema" }, + { name = "matplotlib" }, { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "opentelemetry-api", marker = "python_full_version < '3.11'" }, - { name = "packaging", marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "opentelemetry-api" }, + { name = "packaging" }, { name = "pandas", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "pillow", marker = "python_full_version < '3.11'" }, - { name = "pyarrow", marker = "python_full_version < '3.11'" }, - { name = "pyvisa", version = "1.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "ruamel-yaml", marker = "python_full_version < '3.11'" }, - { name = "tabulate", marker = "python_full_version < '3.11'" }, - { name = "tornado", marker = "python_full_version < '3.11'" }, - { name = "tqdm", marker = "python_full_version < '3.11'" }, - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, - { name = "uncertainties", marker = "python_full_version < '3.11'" }, - { name = "versioningit", marker = "python_full_version < '3.11'" }, - { name = "websockets", marker = "python_full_version < '3.11'" }, - { name = "wrapt", marker = "python_full_version < '3.11'" }, + { name = "pandas", version = "3.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "pillow" }, + { name = "pyarrow" }, + { name = "pyvisa" }, + { name = "ruamel-yaml" }, + { name = "tabulate" }, + { name = "tornado" }, + { name = "tqdm" }, + { name = "typing-extensions" }, + { name = "uncertainties" }, + { name = "versioningit" }, + { name = "websockets" }, + { name = "wrapt" }, { name = "xarray", version = "2025.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "xarray", version = "2026.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/7c/d5/2e8c3b36c6fd4969911ad905814320f7ca2c541bafa8d7199e35d8143002/qcodes-0.52.0.tar.gz", hash = "sha256:add998d3817ff5bbc96665291a615bc230fa39fcac52b19ef315c8f1553c0a6e", size = 800095, upload-time = "2025-04-04T07:33:11.013Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/c0/30/1525fb29910d159145b873737d0ef54a8d00c6044ed9d5b55712d93e0bda/qcodes-0.52.0-py3-none-any.whl", hash = "sha256:de11fb04626b36503cca19f075bb7d73c4c6c16d78906fa5e4f3e938910b211e", size = 953160, upload-time = "2025-04-04T07:33:08.855Z" }, ] -[[package]] -name = "qcodes" -version = "0.56.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.13.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.13' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.13.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.13' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.13.*' and sys_platform == 'emscripten'", - "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform == 'emscripten'", - "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32'", -] -dependencies = [ - { name = "broadbean", marker = "python_full_version >= '3.11'" }, - { name = "cf-xarray", version = "0.10.11", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "dask", marker = "python_full_version >= '3.11'" }, - { name = "h5netcdf", marker = "python_full_version >= '3.11'" }, - { name = "h5py", marker = "python_full_version >= '3.11'" }, - { name = "ipykernel", marker = "python_full_version >= '3.11'" }, - { name = "ipywidgets", marker = "python_full_version >= '3.11'" }, - { name = "jsonschema", marker = "python_full_version >= '3.11'" }, - { name = "matplotlib", marker = "python_full_version >= '3.11'" }, - { name = "networkx", version = "3.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "opentelemetry-api", marker = "python_full_version >= '3.11'" }, - { name = "packaging", marker = "python_full_version >= '3.11'" }, - { name = "pandas", version = "3.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "pillow", marker = "python_full_version >= '3.11'" }, - { name = "pyarrow", marker = "python_full_version >= '3.11'" }, - { name = "pyvisa", version = "1.16.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "ruamel-yaml", marker = "python_full_version >= '3.11'" }, - { name = "tabulate", marker = "python_full_version >= '3.11'" }, - { name = "tqdm", marker = "python_full_version >= '3.11'" }, - { name = "typing-extensions", marker = "python_full_version >= '3.11'" }, - { name = "uncertainties", marker = "python_full_version >= '3.11'" }, - { name = "versioningit", marker = "python_full_version >= '3.11'" }, - { name = "websockets", marker = "python_full_version >= '3.11'" }, - { name = "xarray", version = "2026.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ad/cd/4a639af0bdbaf6632467fd442ee1800ab722365c73c29e8f004b86d0a964/qcodes-0.56.0.tar.gz", hash = "sha256:f25b07044571c14f5ea84056da57db71210138ff109144efa0c8f70440d64773", size = 830852, upload-time = "2026-03-16T11:22:47.574Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/cd/e48a589e56de608b0b5a8e71afbc9742b822f4c50fec171fcff6c2c41c9c/qcodes-0.56.0-py3-none-any.whl", hash = "sha256:cafb7efd761b7cb7c4c8197e3cd55f559bdb35c5228766f1e806a7f4817b5a4f", size = 993343, upload-time = "2026-03-16T11:22:45.837Z" }, -] - [[package]] name = "qcodes-loop" version = "0.1.3" @@ -4591,8 +4545,7 @@ dependencies = [ { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "pandas", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "pandas", version = "3.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "qcodes", version = "0.52.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "qcodes", version = "0.56.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "qcodes" }, { name = "versioningit" }, { name = "xarray", version = "2025.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "xarray", version = "2026.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, @@ -4640,8 +4593,7 @@ dependencies = [ { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "pyqt5" }, { name = "pyqtgraph" }, - { name = "qcodes", version = "0.52.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "qcodes", version = "0.56.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "qcodes" }, { name = "qcodes-loop" }, { name = "scikit-learn", version = "1.7.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "scikit-learn", version = "1.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, @@ -4659,7 +4611,7 @@ wheels = [ [[package]] name = "quantify-scheduler" -version = "0.28.0" +version = "0.28.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "columnar" }, @@ -4678,8 +4630,7 @@ dependencies = [ { name = "pydantic" }, { name = "pyqt5-qt5", marker = "sys_platform == 'win32'" }, { name = "qblox-instruments" }, - { name = "qcodes", version = "0.52.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "qcodes", version = "0.56.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "qcodes" }, { name = "quantify-core" }, { name = "rich", extra = ["jupyter"] }, { name = "ruamel-yaml" }, @@ -4687,9 +4638,9 @@ dependencies = [ { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "xxhash" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d1/f5/7d8d692f2cdd68f22bea297feb9ab681cb7717be4db6fe4bae343d363cd5/quantify_scheduler-0.28.0.tar.gz", hash = "sha256:f1c2e32083c55555270bc4b4a96dc3f19b7555e0ab1a296c89f65bb7cd5b4c01", size = 1923152, upload-time = "2026-03-19T14:41:12.093Z" } +sdist = { url = "https://files.pythonhosted.org/packages/44/97/16a39b59694c057c7b8dd1bfb6d7ec057846b76e6f5810dfb8308e57c82b/quantify_scheduler-0.28.1.tar.gz", hash = "sha256:6e5abfa54d553851d7ad1b75ae0fdfecd4b5f164962df0ad995fa0d9934dd8c0", size = 1923845, upload-time = "2026-06-19T13:59:43.802Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/06/1c/980fd2709977e12cbbfd996e94315b47a06af6d756e0d63df2467731dd7d/quantify_scheduler-0.28.0-py3-none-any.whl", hash = "sha256:1e5ffe6c700cfacb5039af49e4d6ff3ffc47d6e0384ebbec0c73ad0dbd800693", size = 454981, upload-time = "2026-03-19T14:41:10.499Z" }, + { url = "https://files.pythonhosted.org/packages/13/52/6bba8bb0da25dd175dad575f4f1b3398610bc480994b1dd70ea3f38251b1/quantify_scheduler-0.28.1-py3-none-any.whl", hash = "sha256:fc9256bb27ad92450c6c14908d4ed9ed18b56ee5624a4262ecab9f5da163cc62", size = 455075, upload-time = "2026-06-19T13:59:42.212Z" }, ] [[package]] @@ -5012,13 +4963,17 @@ resolution-markers = [ "python_full_version >= '3.14' and sys_platform == 'emscripten'", "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", "python_full_version == '3.13.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.13' and platform_machine == 'ARM64' and sys_platform == 'win32'", + "python_full_version == '3.12.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", "python_full_version == '3.13.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.13' and platform_machine != 'ARM64' and sys_platform == 'win32'", + "python_full_version == '3.12.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", "python_full_version == '3.13.*' and sys_platform == 'emscripten'", - "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform == 'emscripten'", + "python_full_version == '3.12.*' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.12.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ { name = "joblib", marker = "python_full_version >= '3.11'" }, @@ -5124,13 +5079,17 @@ resolution-markers = [ "python_full_version >= '3.14' and sys_platform == 'emscripten'", "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", "python_full_version == '3.13.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.13' and platform_machine == 'ARM64' and sys_platform == 'win32'", + "python_full_version == '3.12.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", "python_full_version == '3.13.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.13' and platform_machine != 'ARM64' and sys_platform == 'win32'", + "python_full_version == '3.12.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", "python_full_version == '3.13.*' and sys_platform == 'emscripten'", - "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform == 'emscripten'", + "python_full_version == '3.12.*' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.12.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, @@ -5334,21 +5293,18 @@ wheels = [ [[package]] name = "stable-baselines3" -version = "2.8.0" +version = "2.9.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cloudpickle" }, { name = "gymnasium" }, - { name = "matplotlib" }, { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "pandas", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "pandas", version = "3.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "torch" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e2/07/01a2aa6a6a58f911085981dad1af2f1877d04886768c0df33930945fb98c/stable_baselines3-2.8.0.tar.gz", hash = "sha256:fe976d102b596c8001ca619638901721bcf97e8934a397e235b2d277fa9216c2", size = 220224, upload-time = "2026-04-01T10:49:29.738Z" } +sdist = { url = "https://files.pythonhosted.org/packages/89/f8/9c1901a42e55b21b9e9559a720cb33dffdeb4c4215b1dfd10d63224b19c0/stable_baselines3-2.9.0.tar.gz", hash = "sha256:92b46c6099a0e8f99163ff09e26729e4d0a68b33dc8598626ca13ade3c0b3a61", size = 221345, upload-time = "2026-06-15T17:00:30.713Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/85/1b/8046baf1e7756006eab991cd3260c4342c515dd6b536bc2d03df4b6f35aa/stable_baselines3-2.8.0-py3-none-any.whl", hash = "sha256:8c19d960b534a909f46dac5227662fc2d6be380e5c66cb04e1ad23edb23dc5a2", size = 187458, upload-time = "2026-04-01T10:49:28.132Z" }, + { url = "https://files.pythonhosted.org/packages/a6/70/d18c8278223d4928378c4af822b66d3c87e19e73bcb011e05e2f0b43f4e5/stable_baselines3-2.9.0-py3-none-any.whl", hash = "sha256:95f39a473dce081d1abe31acaf7cee446dcf223dcd74093d6ea460bc37e2e748", size = 187606, upload-time = "2026-06-15T17:00:29.331Z" }, ] [[package]] @@ -6041,13 +5997,17 @@ resolution-markers = [ "python_full_version >= '3.14' and sys_platform == 'emscripten'", "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", "python_full_version == '3.13.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.13' and platform_machine == 'ARM64' and sys_platform == 'win32'", + "python_full_version == '3.12.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", "python_full_version == '3.13.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.13' and platform_machine != 'ARM64' and sys_platform == 'win32'", + "python_full_version == '3.12.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", "python_full_version == '3.13.*' and sys_platform == 'emscripten'", - "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform == 'emscripten'", + "python_full_version == '3.12.*' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.12.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, @@ -6203,13 +6163,17 @@ resolution-markers = [ "python_full_version >= '3.14' and sys_platform == 'emscripten'", "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", "python_full_version == '3.13.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.13' and platform_machine == 'ARM64' and sys_platform == 'win32'", + "python_full_version == '3.12.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", "python_full_version == '3.13.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.13' and platform_machine != 'ARM64' and sys_platform == 'win32'", + "python_full_version == '3.12.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", "python_full_version == '3.13.*' and sys_platform == 'emscripten'", - "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform == 'emscripten'", + "python_full_version == '3.12.*' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.12.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ { name = "donfig", marker = "python_full_version >= '3.11'" }, From b6848079c5ca6d97cd76c31f81b92c9256416f1a Mon Sep 17 00:00:00 2001 From: David Bakker Date: Thu, 9 Jul 2026 07:06:48 +0200 Subject: [PATCH 2/5] Add two qubit merger --- .../passes/merger/two_qubit_gates_merger.py | 101 ++++++++++++++++++ .../merger/test_two_qubit_gates_merger.py | 64 +++++++++++ 2 files changed, 165 insertions(+) create mode 100644 opensquirrel/passes/merger/two_qubit_gates_merger.py create mode 100644 tests/passes/merger/test_two_qubit_gates_merger.py diff --git a/opensquirrel/passes/merger/two_qubit_gates_merger.py b/opensquirrel/passes/merger/two_qubit_gates_merger.py new file mode 100644 index 00000000..f0503c03 --- /dev/null +++ b/opensquirrel/passes/merger/two_qubit_gates_merger.py @@ -0,0 +1,101 @@ +from opensquirrel.passes.merger.general_merger import Merger +from opensquirrel.ir import IR, Instruction, Gate +from opensquirrel.ir.two_qubit_gate import TwoQubitGate +from opensquirrel.ir.single_qubit_gate import SingleQubitGate +from opensquirrel.circuit import Circuit +from opensquirrel.register_manager import RegisterManager, QubitRegister, BitRegister +from typing import cast + +import numpy as np +from copy import copy +from collections import OrderedDict +from opensquirrel.circuit_matrix_calculator import get_circuit_matrix +from functools import reduce +from opensquirrel.ir.semantics.matrix_gate import MatrixGateSemantic + +from opensquirrel.circuit_builder import CircuitBuilder + +def group_gates(gates: list[list[int]]) -> list[tuple[tuple[int, int], list[int]]]: + groups = [] + group = set() + statement_indices = [] + for i, qubit_indices in enumerate(gates): + + # Reset group if current statement is not a Gate + if not qubit_indices: + if group: + groups.append((tuple(group), copy(statement_indices))) + group.clear() + statement_indices.clear() + continue + + # If the group has more than 2 qubits, it means that we have encountered a new group of gates. + if len(group | set(qubit_indices)) > 2: + if group: + groups.append((tuple(group), copy(statement_indices))) + group = set(qubit_indices) + statement_indices.clear() + + group.update(qubit_indices) + statement_indices.append(i) + + if group: + groups.append((tuple(group), copy(statement_indices))) + return groups + + +class TwoQubitGatesMerger(Merger): + def merge(self, ir: IR, qubit_register_size: int) -> None: + """Merge all consecutive two-qubit gates in the circuit. + + Args: + ir (IR): Intermediate representation of the circuit. + qubit_register_size (int): Size of the qubit register + + """ + + gates = [statement.qubit_indices if isinstance(statement, Gate) else [] for statement in ir.statements] + groups = group_gates(gates) + + new_gates = [] + + for qubit_indices, statement_indices in groups: + if len(statement_indices) == 1: + new_gates.append(ir.statements[statement_indices[0]]) + continue + + sub_circuit_matrix = np.eye(1 << len(qubit_indices), dtype=np.complex128) + + qubit0, qubit1 = qubit_indices + for statement_index in statement_indices: + gate = cast("Gate", ir.statements[statement_index]) + if isinstance(gate, TwoQubitGate): + sub_circuit_matrix = sub_circuit_matrix @ gate.matrix + + if isinstance(gate, SingleQubitGate): + if gate.qubit == qubit0: + sub_circuit_matrix = sub_circuit_matrix @ np.kron(gate.matrix, np.eye(2, dtype=np.complex128)) + if gate.qubit == qubit1: + sub_circuit_matrix = sub_circuit_matrix @ np.kron(np.eye(2, dtype=np.complex128), gate.matrix) + + + gate = TwoQubitGate(*qubit_indices, gate_semantic=MatrixGateSemantic(sub_circuit_matrix)) + new_gates.append(gate) + + # Replace the original statements with the merged gates + new_statements = [] + current_index = 0 + for new_gate, (_, statement_indices) in zip(new_gates, groups, strict=True): + + if current_index < statement_indices[0]: + new_statements.extend(ir.statements[current_index:statement_indices[0]]) + + new_statements.append(new_gate) + current_index = statement_indices[-1] + 1 + + # Replace the original statements with the merged gates + ir.statements = new_statements + + + + \ No newline at end of file diff --git a/tests/passes/merger/test_two_qubit_gates_merger.py b/tests/passes/merger/test_two_qubit_gates_merger.py new file mode 100644 index 00000000..390c89e8 --- /dev/null +++ b/tests/passes/merger/test_two_qubit_gates_merger.py @@ -0,0 +1,64 @@ +import pytest +from typing import TYPE_CHECKING +from opensquirrel.passes.merger.two_qubit_gates_merger import TwoQubitGatesMerger, group_gates +from opensquirrel.circuit_builder import CircuitBuilder +from opensquirrel.circuit_matrix_calculator import get_circuit_matrix + +from tests.ir.ir_equality_test_base import modify_circuit_and_check +from opensquirrel.common import are_matrices_equivalent_up_to_global_phase +# if TYPE_CHECKING: +# from opensquirrel.circuit import Circuit + +@pytest.fixture +def merger() -> TwoQubitGatesMerger: + return TwoQubitGatesMerger() + +@pytest.mark.parametrize( + "gates, expected_groups", + [ + ([[0]], [((0,), [0])]), + ([[0, 1]], [((0, 1), [0])]), + ([[0, 1], [0, 1], [1, 2], [1, 2]], [((0, 1), [0, 1]), ((1, 2), [2, 3])]), + ([[0, 1], [], [1, 2], [1, 2]], [((0, 1), [0]), ((1, 2), [2, 3])]), + ([[0, 1], [0, 1], [], [1, 2], [1, 2]], [((0, 1), [0, 1]), ((1, 2), [3, 4])]), + ([[0, 1], [2, 1], [1, 2], [1], [1], [0, 1], [0], [1]], [((0, 1), [0]), ((1, 2), [1, 2, 3, 4]), ((0, 1), [5, 6, 7])]), + ] +) +def test_group_gates(gates: list[list[int]], expected_groups: list[tuple[tuple[int, int], list[int]]]) -> None: + assert group_gates(gates) == expected_groups + +@pytest.mark.parametrize( + "circuit, expected_circuit", + [ + (CircuitBuilder(1).H(0).to_circuit(), CircuitBuilder(1).H(0).to_circuit()), + (CircuitBuilder(2).CNOT(0,1).to_circuit(), CircuitBuilder(2).CNOT(0, 1).to_circuit()), + (CircuitBuilder(2).CNOT(0,1).CNOT(1, 0).CNOT(0, 1).to_circuit(), CircuitBuilder(2).SWAP(0, 1).to_circuit()), + (CircuitBuilder(2).SWAP(0, 1).SWAP(0, 1).to_circuit(), CircuitBuilder(2).I(0).to_circuit()), + (CircuitBuilder(2).SWAP(0, 1).CZ(0, 1).S(0).S(1).to_circuit(), CircuitBuilder(2).ISWAP(0, 1).to_circuit()), + (CircuitBuilder(2).H(1).CNOT(0, 1).H(1).to_circuit(), CircuitBuilder(2).CZ(0, 1).to_circuit()), + (CircuitBuilder(2).CV(0, 1).CV(0, 1).to_circuit(), CircuitBuilder(2).CNOT(0, 1).to_circuit()), + (CircuitBuilder(2).H(0).H(1).CNOT(0, 1).H(0).H(1).to_circuit(), CircuitBuilder(2).CNOT(1, 0).to_circuit()), + ] +) +def test_two_qubit_gates_merger(circuit, expected_circuit, merger: TwoQubitGatesMerger) -> None: + modify_circuit_and_check(circuit, merger.merge, expected_circuit) + + + +@pytest.mark.parametrize( + "circuit, expected_circuit", + [ + (CircuitBuilder(1).H(0).to_circuit(), CircuitBuilder(1).H(0).to_circuit()), + (CircuitBuilder(2).CNOT(0, 1).to_circuit(), CircuitBuilder(2).CNOT(0, 1).to_circuit()), + (CircuitBuilder(2).CNOT(0, 1).CNOT(1, 0).CNOT(0, 1).to_circuit(), CircuitBuilder(2).SWAP(0, 1).to_circuit()), + (CircuitBuilder(2).SWAP(0, 1).SWAP(0, 1).to_circuit(), CircuitBuilder(2).I(0).to_circuit()), + (CircuitBuilder(2).SWAP(0, 1).CZ(0, 1).S(0).S(1).to_circuit(), CircuitBuilder(2).ISWAP(0, 1).to_circuit()), + (CircuitBuilder(2).H(1).CNOT(0, 1).H(1).to_circuit(), CircuitBuilder(2).CZ(0, 1).to_circuit()), + (CircuitBuilder(2).CV(0, 1).CV(0, 1).to_circuit(), CircuitBuilder(2).CNOT(0, 1).to_circuit()), + (CircuitBuilder(2).H(0).H(1).CNOT(0, 1).H(0).H(1).to_circuit(), CircuitBuilder(2).CNOT(1, 0).to_circuit()), + ] +) +def test_two_qubit_gates_merger_2(circuit, expected_circuit) -> None: + expected_matrix = get_circuit_matrix(circuit) + actual_matrix = get_circuit_matrix(expected_circuit) + assert are_matrices_equivalent_up_to_global_phase(actual_matrix, expected_matrix) \ No newline at end of file From 17bebe8da91796d3f54315e5b25e9fa01be72851 Mon Sep 17 00:00:00 2001 From: David Bakker Date: Thu, 9 Jul 2026 08:36:19 +0200 Subject: [PATCH 3/5] More tests --- .../ir/default_gates/single_qubit_gates.py | 1 + .../passes/merger/two_qubit_gates_merger.py | 79 ++++++++--------- .../merger/test_two_qubit_gates_merger.py | 86 ++++++++++++------- uv.lock | 62 ++++++------- 4 files changed, 127 insertions(+), 101 deletions(-) diff --git a/opensquirrel/ir/default_gates/single_qubit_gates.py b/opensquirrel/ir/default_gates/single_qubit_gates.py index 593aa859..8fed12fc 100644 --- a/opensquirrel/ir/default_gates/single_qubit_gates.py +++ b/opensquirrel/ir/default_gates/single_qubit_gates.py @@ -65,6 +65,7 @@ def __init__(self, qubit: QubitLike) -> None: qubit=qubit, gate_semantic=BsrNoParams(axis=(1, 0, 0), angle=-pi / 2, phase=-pi / 4), name="mX90" ) + class Y(SingleQubitGate): def __init__(self, qubit: QubitLike) -> None: super().__init__(qubit=qubit, gate_semantic=BsrNoParams(axis=(0, 1, 0), angle=pi, phase=pi / 2), name="Y") diff --git a/opensquirrel/passes/merger/two_qubit_gates_merger.py b/opensquirrel/passes/merger/two_qubit_gates_merger.py index f0503c03..88046791 100644 --- a/opensquirrel/passes/merger/two_qubit_gates_merger.py +++ b/opensquirrel/passes/merger/two_qubit_gates_merger.py @@ -1,26 +1,21 @@ -from opensquirrel.passes.merger.general_merger import Merger -from opensquirrel.ir import IR, Instruction, Gate -from opensquirrel.ir.two_qubit_gate import TwoQubitGate -from opensquirrel.ir.single_qubit_gate import SingleQubitGate -from opensquirrel.circuit import Circuit -from opensquirrel.register_manager import RegisterManager, QubitRegister, BitRegister -from typing import cast +from copy import copy import numpy as np -from copy import copy -from collections import OrderedDict + +from opensquirrel.circuit_builder import CircuitBuilder from opensquirrel.circuit_matrix_calculator import get_circuit_matrix -from functools import reduce +from opensquirrel.ir import IR, Gate, Qubit from opensquirrel.ir.semantics.matrix_gate import MatrixGateSemantic +from opensquirrel.ir.single_qubit_gate import SingleQubitGate +from opensquirrel.ir.two_qubit_gate import TwoQubitGate +from opensquirrel.passes.merger.general_merger import Merger -from opensquirrel.circuit_builder import CircuitBuilder def group_gates(gates: list[list[int]]) -> list[tuple[tuple[int, int], list[int]]]: groups = [] group = set() statement_indices = [] for i, qubit_indices in enumerate(gates): - # Reset group if current statement is not a Gate if not qubit_indices: if group: @@ -29,13 +24,13 @@ def group_gates(gates: list[list[int]]) -> list[tuple[tuple[int, int], list[int] statement_indices.clear() continue - # If the group has more than 2 qubits, it means that we have encountered a new group of gates. + # If the group has more than 2 qubits, it means that we have encountered a new group of gates. if len(group | set(qubit_indices)) > 2: - if group: + if group: groups.append((tuple(group), copy(statement_indices))) group = set(qubit_indices) statement_indices.clear() - + group.update(qubit_indices) statement_indices.append(i) @@ -44,6 +39,20 @@ def group_gates(gates: list[list[int]]) -> list[tuple[tuple[int, int], list[int] return groups +def normalize_gate_indices(gate: Gate) -> Gate: + if isinstance(gate, TwoQubitGate): + gate.qubit0 = Qubit(gate.qubit0.index % 2) + gate.qubit1 = Qubit(gate.qubit1.index % 2) + return gate + + if isinstance(gate, SingleQubitGate): + gate.qubit = Qubit(gate.qubit.index % 2) + return gate + + msg = f"Unsupported gate type: {type(gate)}" + raise TypeError(msg) + + class TwoQubitGatesMerger(Merger): def merge(self, ir: IR, qubit_register_size: int) -> None: """Merge all consecutive two-qubit gates in the circuit. @@ -56,29 +65,26 @@ def merge(self, ir: IR, qubit_register_size: int) -> None: gates = [statement.qubit_indices if isinstance(statement, Gate) else [] for statement in ir.statements] groups = group_gates(gates) - + new_gates = [] - - for qubit_indices, statement_indices in groups: + + swap = np.array([[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]]) + for qubit_indices, statement_indices in groups: if len(statement_indices) == 1: new_gates.append(ir.statements[statement_indices[0]]) continue - sub_circuit_matrix = np.eye(1 << len(qubit_indices), dtype=np.complex128) - - qubit0, qubit1 = qubit_indices + sub_circuit = CircuitBuilder(len(qubit_indices)) for statement_index in statement_indices: - gate = cast("Gate", ir.statements[statement_index]) - if isinstance(gate, TwoQubitGate): - sub_circuit_matrix = sub_circuit_matrix @ gate.matrix - - if isinstance(gate, SingleQubitGate): - if gate.qubit == qubit0: - sub_circuit_matrix = sub_circuit_matrix @ np.kron(gate.matrix, np.eye(2, dtype=np.complex128)) - if gate.qubit == qubit1: - sub_circuit_matrix = sub_circuit_matrix @ np.kron(np.eye(2, dtype=np.complex128), gate.matrix) - - + statement = ir.statements[statement_index] + if isinstance(statement, Gate): + statement = normalize_gate_indices(statement) + sub_circuit.add_instruction(statement) + + # `get_circuit_matrix` uses the convention of the first qubit being the most significant bit, + # so we need to swap the qubits before and after calculating the matrix + sub_circuit_matrix = swap @ get_circuit_matrix(sub_circuit.to_circuit()) @ swap + gate = TwoQubitGate(*qubit_indices, gate_semantic=MatrixGateSemantic(sub_circuit_matrix)) new_gates.append(gate) @@ -86,16 +92,11 @@ def merge(self, ir: IR, qubit_register_size: int) -> None: new_statements = [] current_index = 0 for new_gate, (_, statement_indices) in zip(new_gates, groups, strict=True): - if current_index < statement_indices[0]: - new_statements.extend(ir.statements[current_index:statement_indices[0]]) - + new_statements.extend(ir.statements[current_index : statement_indices[0]]) + new_statements.append(new_gate) current_index = statement_indices[-1] + 1 # Replace the original statements with the merged gates ir.statements = new_statements - - - - \ No newline at end of file diff --git a/tests/passes/merger/test_two_qubit_gates_merger.py b/tests/passes/merger/test_two_qubit_gates_merger.py index 390c89e8..58d973c6 100644 --- a/tests/passes/merger/test_two_qubit_gates_merger.py +++ b/tests/passes/merger/test_two_qubit_gates_merger.py @@ -1,64 +1,88 @@ import pytest -from typing import TYPE_CHECKING -from opensquirrel.passes.merger.two_qubit_gates_merger import TwoQubitGatesMerger, group_gates -from opensquirrel.circuit_builder import CircuitBuilder -from opensquirrel.circuit_matrix_calculator import get_circuit_matrix -from tests.ir.ir_equality_test_base import modify_circuit_and_check +from opensquirrel import circuit_matrix_calculator +from opensquirrel.circuit import Circuit +from opensquirrel.circuit_builder import CircuitBuilder from opensquirrel.common import are_matrices_equivalent_up_to_global_phase -# if TYPE_CHECKING: -# from opensquirrel.circuit import Circuit +from opensquirrel.passes.merger.two_qubit_gates_merger import TwoQubitGatesMerger, group_gates + @pytest.fixture def merger() -> TwoQubitGatesMerger: return TwoQubitGatesMerger() + @pytest.mark.parametrize( - "gates, expected_groups", + ("gates", "expected_groups"), [ ([[0]], [((0,), [0])]), ([[0, 1]], [((0, 1), [0])]), ([[0, 1], [0, 1], [1, 2], [1, 2]], [((0, 1), [0, 1]), ((1, 2), [2, 3])]), ([[0, 1], [], [1, 2], [1, 2]], [((0, 1), [0]), ((1, 2), [2, 3])]), - ([[0, 1], [0, 1], [], [1, 2], [1, 2]], [((0, 1), [0, 1]), ((1, 2), [3, 4])]), - ([[0, 1], [2, 1], [1, 2], [1], [1], [0, 1], [0], [1]], [((0, 1), [0]), ((1, 2), [1, 2, 3, 4]), ((0, 1), [5, 6, 7])]), - ] + ([[0, 1], [0, 1], [], [1, 2], [1, 2]], [((0, 1), [0, 1]), ((1, 2), [3, 4])]), + ( + [[0, 1], [2, 1], [1, 2], [1], [1], [0, 1], [0], [1]], + [((0, 1), [0]), ((1, 2), [1, 2, 3, 4]), ((0, 1), [5, 6, 7])], + ), + ], ) def test_group_gates(gates: list[list[int]], expected_groups: list[tuple[tuple[int, int], list[int]]]) -> None: assert group_gates(gates) == expected_groups + @pytest.mark.parametrize( - "circuit, expected_circuit", + ("circuit", "expected_circuit"), [ (CircuitBuilder(1).H(0).to_circuit(), CircuitBuilder(1).H(0).to_circuit()), - (CircuitBuilder(2).CNOT(0,1).to_circuit(), CircuitBuilder(2).CNOT(0, 1).to_circuit()), - (CircuitBuilder(2).CNOT(0,1).CNOT(1, 0).CNOT(0, 1).to_circuit(), CircuitBuilder(2).SWAP(0, 1).to_circuit()), + (CircuitBuilder(2).CNOT(0, 1).to_circuit(), CircuitBuilder(2).CNOT(0, 1).to_circuit()), + (CircuitBuilder(2).CNOT(0, 1).CNOT(1, 0).CNOT(0, 1).to_circuit(), CircuitBuilder(2).SWAP(0, 1).to_circuit()), (CircuitBuilder(2).SWAP(0, 1).SWAP(0, 1).to_circuit(), CircuitBuilder(2).I(0).to_circuit()), (CircuitBuilder(2).SWAP(0, 1).CZ(0, 1).S(0).S(1).to_circuit(), CircuitBuilder(2).ISWAP(0, 1).to_circuit()), (CircuitBuilder(2).H(1).CNOT(0, 1).H(1).to_circuit(), CircuitBuilder(2).CZ(0, 1).to_circuit()), (CircuitBuilder(2).CV(0, 1).CV(0, 1).to_circuit(), CircuitBuilder(2).CNOT(0, 1).to_circuit()), (CircuitBuilder(2).H(0).H(1).CNOT(0, 1).H(0).H(1).to_circuit(), CircuitBuilder(2).CNOT(1, 0).to_circuit()), - ] + ( + CircuitBuilder(2).CV(0, 1).to_circuit(), + CircuitBuilder(2).T(0).H(1).CNOT(1, 0).Tdag(0).T(1).CNOT(1, 0).H(1).to_circuit(), + ), + ], ) -def test_two_qubit_gates_merger(circuit, expected_circuit, merger: TwoQubitGatesMerger) -> None: - modify_circuit_and_check(circuit, merger.merge, expected_circuit) +def test_two_qubit_gates_merger_with_two_qubits( + circuit: Circuit, expected_circuit: Circuit, merger: TwoQubitGatesMerger +) -> None: + expected_matrix = circuit_matrix_calculator.get_circuit_matrix(expected_circuit) + pre_merge_matrix = circuit_matrix_calculator.get_circuit_matrix(circuit) + + merger.merge(circuit.ir, circuit.qubit_register_size) + + actual_matrix = circuit_matrix_calculator.get_circuit_matrix(circuit) + + assert are_matrices_equivalent_up_to_global_phase(actual_matrix, pre_merge_matrix) + assert are_matrices_equivalent_up_to_global_phase(actual_matrix, expected_matrix) + # Since we are only dealing two qubits, we can check that the number of statements is 1, + # which means that the two-qubit gates have been merged into a single gate. + assert len(circuit.ir.statements) == 1 @pytest.mark.parametrize( - "circuit, expected_circuit", + ("circuit", "expected_circuit"), [ - (CircuitBuilder(1).H(0).to_circuit(), CircuitBuilder(1).H(0).to_circuit()), - (CircuitBuilder(2).CNOT(0, 1).to_circuit(), CircuitBuilder(2).CNOT(0, 1).to_circuit()), - (CircuitBuilder(2).CNOT(0, 1).CNOT(1, 0).CNOT(0, 1).to_circuit(), CircuitBuilder(2).SWAP(0, 1).to_circuit()), - (CircuitBuilder(2).SWAP(0, 1).SWAP(0, 1).to_circuit(), CircuitBuilder(2).I(0).to_circuit()), - (CircuitBuilder(2).SWAP(0, 1).CZ(0, 1).S(0).S(1).to_circuit(), CircuitBuilder(2).ISWAP(0, 1).to_circuit()), - (CircuitBuilder(2).H(1).CNOT(0, 1).H(1).to_circuit(), CircuitBuilder(2).CZ(0, 1).to_circuit()), - (CircuitBuilder(2).CV(0, 1).CV(0, 1).to_circuit(), CircuitBuilder(2).CNOT(0, 1).to_circuit()), - (CircuitBuilder(2).H(0).H(1).CNOT(0, 1).H(0).H(1).to_circuit(), CircuitBuilder(2).CNOT(1, 0).to_circuit()), - ] + ( + CircuitBuilder(3).H(1).CNOT(0, 1).H(1).H(2).CNOT(1, 2).H(2).to_circuit(), + CircuitBuilder(3).CZ(0, 1).CZ(1, 2).to_circuit(), + ), + ], ) -def test_two_qubit_gates_merger_2(circuit, expected_circuit) -> None: - expected_matrix = get_circuit_matrix(circuit) - actual_matrix = get_circuit_matrix(expected_circuit) - assert are_matrices_equivalent_up_to_global_phase(actual_matrix, expected_matrix) \ No newline at end of file +def test_two_qubit_gates_merger_with_multiple_qubits( + circuit: Circuit, expected_circuit: Circuit, merger: TwoQubitGatesMerger +) -> None: + expected_matrix = circuit_matrix_calculator.get_circuit_matrix(expected_circuit) + pre_merge_matrix = circuit_matrix_calculator.get_circuit_matrix(circuit) + + merger.merge(circuit.ir, circuit.qubit_register_size) + + actual_matrix = circuit_matrix_calculator.get_circuit_matrix(circuit) + + assert are_matrices_equivalent_up_to_global_phase(actual_matrix, pre_merge_matrix) + assert are_matrices_equivalent_up_to_global_phase(actual_matrix, expected_matrix) diff --git a/uv.lock b/uv.lock index 250db0a9..7fb57f60 100644 --- a/uv.lock +++ b/uv.lock @@ -2025,33 +2025,33 @@ wheels = [ [[package]] name = "libqasm" -version = "1.4.0" +version = "1.4.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/b1/f7/ed42142b9b99774f3ca6303521a08733ca7455e02825822d969f2cf12160/libqasm-1.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ea9fef74c62192a33de2f9cc5ddf0058e26d72971ff9478c4578c5d6971d7966", size = 1208780, upload-time = "2026-06-05T14:44:35.585Z" }, - { url = "https://files.pythonhosted.org/packages/55/b7/03d22d9784ff429e5530c5144773b94f95bd648449a94950a34188def827/libqasm-1.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:349bb6de44d753e41cbf95741878bd45ce627cf4c9986044f0f08edea968bff8", size = 1163891, upload-time = "2026-06-05T14:44:37.604Z" }, - { url = "https://files.pythonhosted.org/packages/29/a3/6b22637e65831f971abcdbd5b070f4b45d1b13d8bd3a9c6f6f29871689ee/libqasm-1.4.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e758d46009151d1ae949c56172b067cde29bfcbd5a1ebf11f222651d441792ff", size = 1774355, upload-time = "2026-06-05T14:44:39.333Z" }, - { url = "https://files.pythonhosted.org/packages/24/a4/1fb4b8a43227285d709577213706f341a9007dc14182fbebbe7937baac2b/libqasm-1.4.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:77ac4d176eaadd5b144c8713521f6c578d6595c72cb48ba0b49be798c9dd684b", size = 1933525, upload-time = "2026-06-05T14:44:41.167Z" }, - { url = "https://files.pythonhosted.org/packages/e0/11/e614bf3d1dd429268aab9be42e7abf835b8f0465e7ade2a811ebf3d2dcb3/libqasm-1.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:994aca1e985a1f33ff4bdccbffa11fc589fcf932b45c7e8890de3b5122f7beac", size = 809795, upload-time = "2026-06-05T14:44:43.705Z" }, - { url = "https://files.pythonhosted.org/packages/ed/e5/3f83be60bb0524cc6a6e93ebf126887d5911f0a5bb08049f53094597b75d/libqasm-1.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c6a82ded6d6fa1a4c7dcf9658052e6a687aa276a090b7b94ecb0f642f679800", size = 1208779, upload-time = "2026-06-05T14:44:45.226Z" }, - { url = "https://files.pythonhosted.org/packages/5a/9e/0f3ffb3078844b463dc3b38ed9cc9df21582d9c63e583d286cbdd255ec70/libqasm-1.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:49a5b1b57e4d4f01a63efe45e2dbcc1f9e9bbb09b98ff994339848479afa9941", size = 1163890, upload-time = "2026-06-05T14:44:46.858Z" }, - { url = "https://files.pythonhosted.org/packages/47/21/c6cd328feaebfa6b3db3df9e47be5f454d5ad38678ba60b05b0004dc08c5/libqasm-1.4.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:36caf75d05da56574fae9ed3f4833c117c4f5fa5ed91cbc8b97c7d4156d60d96", size = 1774333, upload-time = "2026-06-05T14:44:48.665Z" }, - { url = "https://files.pythonhosted.org/packages/79/1c/a5f1029fabe7d96f5b19d648c0af638417133d12bfdeaa0f67076d1f7499/libqasm-1.4.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d7c150105a5b8af5af1b8aa92d0848fe33ab74a8e299ac67fc5609526c71f821", size = 1933529, upload-time = "2026-06-05T14:44:50.295Z" }, - { url = "https://files.pythonhosted.org/packages/a0/ee/9c1ec350d131153cd6397539f1debc21c70c61b22df29be5891c7b652232/libqasm-1.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:ef209761553757ea07f45a6f7e451b51ec84b1a2011562602c8dbd8fbb507f43", size = 809910, upload-time = "2026-06-05T14:44:52.006Z" }, - { url = "https://files.pythonhosted.org/packages/62/f9/2babfcb39a824346998ca812c66dc2b7161a6ad6f3ab92f8739c2b36d2c1/libqasm-1.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:71e88bfdc4622cf81cb1fa89ef5e24bede2116e052c42dba385d6ec49cf8ab43", size = 1208285, upload-time = "2026-06-05T14:44:53.694Z" }, - { url = "https://files.pythonhosted.org/packages/d3/29/78c78fc5f9c30da423eca97bf5547df72dd38177467cd54fbaffa62ac76f/libqasm-1.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b6b8bc1ac4da98751a07f9b97270d4b31134234466ff15b68f6a69ccf38ae8cc", size = 1164874, upload-time = "2026-06-05T14:44:55.389Z" }, - { url = "https://files.pythonhosted.org/packages/e3/f0/8b23e8adf1a3f849f9361ed6ad1fda25182cf417037082c7eaca0e692d52/libqasm-1.4.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a9790048d4923e673da48b0952756539fc26ad6eccd96520819aca8577c05dc", size = 1774262, upload-time = "2026-06-05T14:44:57.053Z" }, - { url = "https://files.pythonhosted.org/packages/6c/d6/4837f671f8a529ffae0936eb5c644c16a1145a88584da7da34b87c9f597f/libqasm-1.4.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:44b7b1dbf3f68380dd1622db846f2519376187b7c1c566b5760e52a1fbc394c1", size = 1933829, upload-time = "2026-06-05T14:44:58.985Z" }, - { url = "https://files.pythonhosted.org/packages/2a/ef/2d6a9218a474aea98378d61cee87924341661b20d01194ceff392f21f521/libqasm-1.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:1bb5bb87bb01a81de0bc8f716d2b03e44d83288c7f093e455ea9f725e0a3c51f", size = 810741, upload-time = "2026-06-05T14:45:00.673Z" }, - { url = "https://files.pythonhosted.org/packages/26/81/ea230d4fd19c18b5986bc9d9c61aaf36db50fcf204a96fd73dfea2fb8eed/libqasm-1.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8d98ae67f3d13877ccca26b1349906cf906b45becfdd603225f612b29e215d59", size = 1208426, upload-time = "2026-06-05T14:45:02.278Z" }, - { url = "https://files.pythonhosted.org/packages/d2/c1/2f4da51f9cde413151328cc2f44674ecb01e0b1725b43c4958d18a0861ae/libqasm-1.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3a57e9cd04fa131830b189ef226d44ffce895f7752fb22191c17e5c8dd436461", size = 1164545, upload-time = "2026-06-05T14:45:03.802Z" }, - { url = "https://files.pythonhosted.org/packages/70/f1/77d5ed0054e83942773e840d69539f9c012d736e8d66d6056ddd5edef88e/libqasm-1.4.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e56ee1784f234678d4eddd56748045fade9be4704871cad0c067e8dc94f40c55", size = 1773824, upload-time = "2026-06-05T14:45:05.547Z" }, - { url = "https://files.pythonhosted.org/packages/c9/12/4fd3324fcdcd59a18c3d309347059ea9f83e85d7351f4a85d1d34f00152d/libqasm-1.4.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0f4047cc7f3c373d380952c8de46481ed2cb24a7209f32981fb76274ea3956a", size = 1933102, upload-time = "2026-06-05T14:45:07.172Z" }, - { url = "https://files.pythonhosted.org/packages/df/ca/237c4fbf3d925d446dbb0e19d2cb0f8a615b823b1a8b7634df2c6406562e/libqasm-1.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:f843fa46b226a17576ddd641cb8c1c65c0956dc50c8b51d1f34276c1df4ccc9a", size = 809949, upload-time = "2026-06-05T14:45:08.867Z" }, + { url = "https://files.pythonhosted.org/packages/20/9e/210d0a80d4466527f9528f0036adcc54ab52743f68fb6d0ad799dfb77217/libqasm-1.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d1b49b2fd0fd9c1deaf29bfa807ba228283227b2563ad324e50d49825b8402be", size = 1208679, upload-time = "2026-06-15T11:36:39.527Z" }, + { url = "https://files.pythonhosted.org/packages/dc/e1/3112d30b26992a33e08bb80983f5c0c2f9097f62d1400b22f5f640ee6c81/libqasm-1.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:09d98c4e398c5668ec5023487ec52505644508e1626b090f4e9f0bf580d0ab62", size = 1163770, upload-time = "2026-06-15T11:36:41.227Z" }, + { url = "https://files.pythonhosted.org/packages/9e/71/9c5b9c7eb6a389570445199f105585470306323c066826dba88cae785cbd/libqasm-1.4.1-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:794edfdf45567d58b11cc82b7687621703ae6a972a595427f46af98c29024553", size = 1774396, upload-time = "2026-06-15T11:36:43.341Z" }, + { url = "https://files.pythonhosted.org/packages/a0/0e/4b48025e00cdfde73c35f2002ea5ed0cf7a0ba85e645f422e353bce3391c/libqasm-1.4.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d546a49b0aa9799e941f34bfb835478e55dcf40e626ce8aa4191f90b6198799d", size = 1933511, upload-time = "2026-06-15T11:36:45.117Z" }, + { url = "https://files.pythonhosted.org/packages/aa/80/1a44e550d0194d0c8e9cfe8f3a903c6d102a4582aadd3f6978a79c79a088/libqasm-1.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:15de3df1afa1b68e6a0ca0b6a5bb824796c18085f86c7c5b8fbdf650b1f11f9d", size = 798493, upload-time = "2026-06-15T11:36:46.5Z" }, + { url = "https://files.pythonhosted.org/packages/c3/f2/55f8161ba2745d0fb0a90ee63761adb669c2b9fe3c4c27485b6e63026dcc/libqasm-1.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7cc92f4de7c056ffac4d0787095407ecf25b0bb08b1c19b38bbbe2dd47507cd4", size = 1208678, upload-time = "2026-06-15T11:36:48.239Z" }, + { url = "https://files.pythonhosted.org/packages/02/7d/7cc1bb7a6892921bfb00c0110115468984276069cd5f236272b547d8abbb/libqasm-1.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:da41eca44b765108ce817856e9e9e3c56cb9147ff5e4dc0783845164b38fbfa1", size = 1163764, upload-time = "2026-06-15T11:36:49.843Z" }, + { url = "https://files.pythonhosted.org/packages/9b/78/d364c3d9d40474d74677874963c2e4aa9949ca23a6fa4c32ecb680520340/libqasm-1.4.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:65765d22aa91b6c9a814981ce10bd53f365714b8e844175b59ac6a5a2dbb9cfa", size = 1774414, upload-time = "2026-06-15T11:36:51.838Z" }, + { url = "https://files.pythonhosted.org/packages/31/a8/59f0296b5aaa92763c9bde635f6e3c5f33c90e70f58752b8ca57efe0820a/libqasm-1.4.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:df260702453d1c72c7ec020a32277ffc29f9a5a5a32ce607fa390bd840c9619a", size = 1933509, upload-time = "2026-06-15T11:36:54.199Z" }, + { url = "https://files.pythonhosted.org/packages/b6/8a/3ede0c82b530ee9fc2c1159e5452c6bae0474e9535db504373c55d65250c/libqasm-1.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:bced406f8a983234d1c5f02b35195125ea07f59352ddcdb4ea5b96eeabcf6e40", size = 798518, upload-time = "2026-06-15T11:36:56.38Z" }, + { url = "https://files.pythonhosted.org/packages/2a/db/af17c235408b2a55f0ef2a6e766c91276957da05c352daf161c42976f92a/libqasm-1.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:207d7e2c9509d5d0f5b7409dff4d1a455cbe4ef7e3654f743794bf366cabf834", size = 1208370, upload-time = "2026-06-15T11:36:58.51Z" }, + { url = "https://files.pythonhosted.org/packages/35/82/1cf6be1efaed9eaa705a3fc80230af878714747976cc9f84476e861b7d31/libqasm-1.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:671073b95e175138fb349045303c9576a11024ecc13ad4357ab28810cb5915fe", size = 1164710, upload-time = "2026-06-15T11:37:00.229Z" }, + { url = "https://files.pythonhosted.org/packages/90/7d/1fdbc0ab5ef9f0a82a6ae6b81c64066026df09e5656d22df62287dd4010d/libqasm-1.4.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5b0a36a2db04680cc1fd80f87f3ee83b741670caf2c6751defd6200501a1ab15", size = 1774298, upload-time = "2026-06-15T11:37:01.829Z" }, + { url = "https://files.pythonhosted.org/packages/4a/04/4a73079af8dc53087009371ea548537e1c13dcd3c61d989b2afbff287bdc/libqasm-1.4.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f6544d80363e8676b704cdfad41b6a812b6263806d262acc8024fd815a8bcc59", size = 1933834, upload-time = "2026-06-15T11:37:03.502Z" }, + { url = "https://files.pythonhosted.org/packages/fd/f5/183cfd1bdfad0a1454e017473c9c67606bf2cba069ad6501f7b33ea07d1e/libqasm-1.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:42860d8195b7a48021fadc6dc4ad331c50b0bace4794bf301dc123a5fa78ea5b", size = 799690, upload-time = "2026-06-15T11:37:05.052Z" }, + { url = "https://files.pythonhosted.org/packages/6e/3b/0ddedcffd54cbbd92e9f4a8efb708e1d5635acf71e9478c495aced527d9a/libqasm-1.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4f48ec76263495d9e17356c7390b246b422b3853f9687108b517e72cd9895521", size = 1208344, upload-time = "2026-06-15T11:37:06.88Z" }, + { url = "https://files.pythonhosted.org/packages/24/70/de3d0313a47bf91424622aa355ea3660fe9576080082fb16a15e5e66a05b/libqasm-1.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7437c8c12fdc58fd7f00a72c3b528ffa172e6206425d037acee727ea1b4cf4fc", size = 1164573, upload-time = "2026-06-15T11:37:08.395Z" }, + { url = "https://files.pythonhosted.org/packages/5b/da/16b8c500afc67b41d6a0d875e747d6bdc478a520b9b9feb8d21d8e9e3d47/libqasm-1.4.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f1e5de4f3faa3b10f8fb89dba3e753e3d05684896848aad01eb6886907589c15", size = 1773815, upload-time = "2026-06-15T11:37:10.01Z" }, + { url = "https://files.pythonhosted.org/packages/9d/87/7aff2de11e7e51adfcb6c95a73435c651460cf68fc1297c02de1ce42d7d2/libqasm-1.4.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dabbc6d84b99ec5de34ebf735c3dd3d210bc9ea2d968660071076a2034738060", size = 1933184, upload-time = "2026-06-15T11:37:11.703Z" }, + { url = "https://files.pythonhosted.org/packages/e4/81/6a323ac1c1f8dee4435d166e65aa345b375a7eb338f3d5caf79f5af1e1c2/libqasm-1.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:87735047e8bcfe06d2e2398d18a4134b0b7ac9519752e29ae04fdf3e028a8daf", size = 799833, upload-time = "2026-06-15T11:37:13.253Z" }, ] [[package]] @@ -3323,7 +3323,7 @@ qgym-mapper = [ [package.metadata] requires-dist = [ - { name = "libqasm", specifier = "==1.4.0" }, + { name = "libqasm", specifier = "==1.4.1" }, { name = "networkx", specifier = ">=3.4.2" }, { name = "numpy", specifier = ">=2.2.6" }, { name = "scipy", specifier = ">=1.15.3" }, @@ -3352,18 +3352,18 @@ docs = [ { name = "mkdocs-material-extensions", specifier = "==1.3.1" }, { name = "mkdocs-render-swagger-plugin", specifier = "==0.1.2" }, { name = "mkdocstrings", extras = ["python"], specifier = ">=0.24,<1.1" }, - { name = "pymdown-extensions", specifier = ">=10.7.1,<11" }, + { name = "pymdown-extensions", specifier = ">=10.7.1,<12" }, ] examples = [{ name = "jupyter", specifier = ">=1.0.0,<2" }] export = [ { name = "pyqt5-qt5", marker = "sys_platform != 'darwin'", specifier = "==5.15.2" }, { name = "qcodes", marker = "sys_platform != 'darwin'", specifier = "<0.57.0" }, - { name = "quantify-scheduler", marker = "sys_platform != 'darwin'", specifier = "==0.28.0.post2" }, + { name = "quantify-scheduler", marker = "sys_platform != 'darwin'", specifier = "==0.28.1" }, { name = "spirack", marker = "sys_platform != 'darwin'", specifier = "<=0.2.19" }, ] qgym-mapper = [ { name = "qgym", specifier = "==0.3.1" }, - { name = "sb3-contrib", specifier = "==2.8.0" }, + { name = "sb3-contrib", specifier = "==2.9.0" }, { name = "stable-baselines3", specifier = "==2.9.0" }, ] @@ -4611,7 +4611,7 @@ wheels = [ [[package]] name = "quantify-scheduler" -version = "0.28.0.post2" +version = "0.28.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "columnar" }, @@ -4638,9 +4638,9 @@ dependencies = [ { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "xxhash" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fa/87/5fcf4c1d3cd75fd2f92064ca0ce4a408f6b8139850a607d368a144d41437/quantify_scheduler-0.28.0.post2.tar.gz", hash = "sha256:70d31822071feecf6d5127a12e4c63a8d4d29ba8009954846b03670a8ba3fed0", size = 1923486, upload-time = "2026-05-05T17:48:49.731Z" } +sdist = { url = "https://files.pythonhosted.org/packages/44/97/16a39b59694c057c7b8dd1bfb6d7ec057846b76e6f5810dfb8308e57c82b/quantify_scheduler-0.28.1.tar.gz", hash = "sha256:6e5abfa54d553851d7ad1b75ae0fdfecd4b5f164962df0ad995fa0d9934dd8c0", size = 1923845, upload-time = "2026-06-19T13:59:43.802Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/04/06/75c5f81558f1770fcf3a4de809ea99d911ef0f83068c8bdfa6f152afc8fb/quantify_scheduler-0.28.0.post2-py3-none-any.whl", hash = "sha256:b7634d9a49ba0a412392cd6f3f4b80eff7d9f9176a6b404dfd76d09ce4bce992", size = 455071, upload-time = "2026-05-05T17:48:48.058Z" }, + { url = "https://files.pythonhosted.org/packages/13/52/6bba8bb0da25dd175dad575f4f1b3398610bc480994b1dd70ea3f38251b1/quantify_scheduler-0.28.1-py3-none-any.whl", hash = "sha256:fc9256bb27ad92450c6c14908d4ed9ed18b56ee5624a4262ecab9f5da163cc62", size = 455075, upload-time = "2026-06-19T13:59:42.212Z" }, ] [[package]] @@ -4898,14 +4898,14 @@ wheels = [ [[package]] name = "sb3-contrib" -version = "2.8.0" +version = "2.9.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "stable-baselines3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/13/c1/d50b8f886c586864fbda8855c03b0da235dd345ff66e05500ef887711efe/sb3_contrib-2.8.0.tar.gz", hash = "sha256:25656d093169239db1081b7665091f168f6113776878387387fdf4e3e894c7f1", size = 90527, upload-time = "2026-04-01T10:59:58.367Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8f/8e/476222d88ba09dce0feebf6fb30b55e4dd0c80be5b19dc7b40d12d70752a/sb3_contrib-2.9.0.tar.gz", hash = "sha256:6e839c669552ecb3deb616a42ea98ed5e6c599eb5ac5f86232415b87a3873699", size = 90312, upload-time = "2026-06-15T17:06:08.8Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8c/17/3a00211e7453d382b98783995174e1f587c461719ac916078bac26dbe597/sb3_contrib-2.8.0-py3-none-any.whl", hash = "sha256:14c81ce4e15c747a13e2b1a8945e7658da6e320b1341b9e971358c35837598b2", size = 93043, upload-time = "2026-04-01T10:59:56.704Z" }, + { url = "https://files.pythonhosted.org/packages/c3/06/add3f1d6250f58ca493830ddb2118975aa3c4433bc07662d5ac5089eb691/sb3_contrib-2.9.0-py3-none-any.whl", hash = "sha256:b492b2be792f8f8214ff6b984e94812dc877f809ae82302cbe00626442ae316a", size = 93042, upload-time = "2026-06-15T17:06:07.452Z" }, ] [[package]] From f69945336eda2fe323fba73d330fc423352acb9d Mon Sep 17 00:00:00 2001 From: David Bakker Date: Fri, 24 Jul 2026 16:48:54 +0200 Subject: [PATCH 4/5] Add graph builder + tests --- opensquirrel/ir/node.py | 42 +++++++++++++++++ .../passes/merger/two_qubit_gates_merger.py | 17 ++++--- tests/ir/test_node.py | 47 +++++++++++++++++++ 3 files changed, 99 insertions(+), 7 deletions(-) create mode 100644 opensquirrel/ir/node.py create mode 100644 tests/ir/test_node.py diff --git a/opensquirrel/ir/node.py b/opensquirrel/ir/node.py new file mode 100644 index 00000000..87141c6b --- /dev/null +++ b/opensquirrel/ir/node.py @@ -0,0 +1,42 @@ +from __future__ import annotations + +from opensquirrel.ir import IR, Instruction, AsmDeclaration +from opensquirrel.ir.single_qubit_gate import SingleQubitGate +from opensquirrel.ir.two_qubit_gate import TwoQubitGate +from collections import deque +from opensquirrel.ir.statement import Statement +from dataclasses import dataclass, field + +import networkx as nx + +def build_graph(ir: IR) -> nx.DiGraph: + n = len(ir.statements) + graph = nx.DiGraph() + graph.add_nodes_from(range(n)) + + for i, statement in enumerate(ir.statements): + if not isinstance(statement, Instruction): + continue + + qubit_indices = set(statement.qubit_indices) + for j in range(i + 1, n): + other_statement = ir.statements[j] + if isinstance(other_statement, Instruction): + other_qubit_indices = set(other_statement.qubit_indices) + + if inter := qubit_indices.intersection(other_qubit_indices): + graph.add_edge(i, j, qubit_index=tuple(inter)) + qubit_indices = qubit_indices.difference(inter) + + if not qubit_indices: + break + + if not nx.is_directed_acyclic_graph(graph): + raise ValueError + + return graph + + +def f(graph: nx.DiGraph): + qubit_indices = nx.get_edge_attributes(graph, "qubit_index", default=None) + diff --git a/opensquirrel/passes/merger/two_qubit_gates_merger.py b/opensquirrel/passes/merger/two_qubit_gates_merger.py index 88046791..fc500f01 100644 --- a/opensquirrel/passes/merger/two_qubit_gates_merger.py +++ b/opensquirrel/passes/merger/two_qubit_gates_merger.py @@ -2,6 +2,7 @@ import numpy as np +from opensquirrel.circuit import Circuit from opensquirrel.circuit_builder import CircuitBuilder from opensquirrel.circuit_matrix_calculator import get_circuit_matrix from opensquirrel.ir import IR, Gate, Qubit @@ -52,6 +53,11 @@ def normalize_gate_indices(gate: Gate) -> Gate: msg = f"Unsupported gate type: {type(gate)}" raise TypeError(msg) +def _get_sub_circuit_matrix(circuit: Circuit) -> np.ndarray: + # `get_circuit_matrix` uses the convention of the first qubit being the most significant bit, + # so we need to swap the qubits before and after calculating the matrix + swap = np.array([[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]]) + return swap @ get_circuit_matrix(circuit) @ swap class TwoQubitGatesMerger(Merger): def merge(self, ir: IR, qubit_register_size: int) -> None: @@ -68,23 +74,19 @@ def merge(self, ir: IR, qubit_register_size: int) -> None: new_gates = [] - swap = np.array([[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]]) for qubit_indices, statement_indices in groups: if len(statement_indices) == 1: new_gates.append(ir.statements[statement_indices[0]]) continue - sub_circuit = CircuitBuilder(len(qubit_indices)) + builder = CircuitBuilder(len(qubit_indices)) for statement_index in statement_indices: statement = ir.statements[statement_index] if isinstance(statement, Gate): statement = normalize_gate_indices(statement) - sub_circuit.add_instruction(statement) - - # `get_circuit_matrix` uses the convention of the first qubit being the most significant bit, - # so we need to swap the qubits before and after calculating the matrix - sub_circuit_matrix = swap @ get_circuit_matrix(sub_circuit.to_circuit()) @ swap + builder.add_instruction(statement) + sub_circuit_matrix = _get_sub_circuit_matrix(builder.to_circuit()) gate = TwoQubitGate(*qubit_indices, gate_semantic=MatrixGateSemantic(sub_circuit_matrix)) new_gates.append(gate) @@ -100,3 +102,4 @@ def merge(self, ir: IR, qubit_register_size: int) -> None: # Replace the original statements with the merged gates ir.statements = new_statements + diff --git a/tests/ir/test_node.py b/tests/ir/test_node.py new file mode 100644 index 00000000..aeefb047 --- /dev/null +++ b/tests/ir/test_node.py @@ -0,0 +1,47 @@ +import pytest + +from opensquirrel.ir import IR, Qubit, Statement +from opensquirrel.ir.node import build_graph +from opensquirrel.ir.statement import Instruction +from opensquirrel import H, CNOT + + +class TestNodeGraph: + @pytest.mark.parametrize( + "statements, expected_edges", + [ + ( + [H(0), CNOT(0, 1), H(1)], + {(0, 1), (1, 2)}, + ), + ( + [H(1), CNOT(0, 1), H(1), H(2), CNOT(1, 2), H(2)], + {(0, 1), (1, 2), (2, 4), (3, 4), (4, 5)}, + ), + ( + [H(3), CNOT(2,3), H(1), H(2), CNOT(0, 1), H(2), H(1), H(3), CNOT(1, 2), H(2)], + {(0,1), (1,3), (1, 7), (6, 8), (2, 4), (4, 6), (3, 5), (5, 8), (8, 9)} + ) + ], + ) + def test_build_graph_creates_dependencies(self, statements: list[Statement], expected_edges) -> None: + ir = IR() + for statement in statements: + ir.add_statement(statement) + + graph = build_graph(ir) + + assert set(graph.edges()) == expected_edges + + + def test_build_graph_does_not_create_edges_for_unrelated_instructions(self) -> None: + ir = IR() + ir.add_statement(H(0)) + ir.add_statement(H(2)) + ir.add_statement(H(1)) + + graph = build_graph(ir) + + assert list(graph.edges()) == [] + + \ No newline at end of file From be923badd2616694df96e3812e10379131ff8580 Mon Sep 17 00:00:00 2001 From: David Bakker Date: Mon, 27 Jul 2026 15:57:32 +0200 Subject: [PATCH 5/5] TwoQubitGatesMerger now passes all tests --- opensquirrel/ir/node.py | 42 ----- .../passes/merger/two_qubit_gates_merger.py | 155 +++++++++++------- tests/ir/test_node.py | 47 ------ .../merger/test_two_qubit_gates_merger.py | 85 +++++++--- 4 files changed, 160 insertions(+), 169 deletions(-) delete mode 100644 opensquirrel/ir/node.py delete mode 100644 tests/ir/test_node.py diff --git a/opensquirrel/ir/node.py b/opensquirrel/ir/node.py deleted file mode 100644 index 87141c6b..00000000 --- a/opensquirrel/ir/node.py +++ /dev/null @@ -1,42 +0,0 @@ -from __future__ import annotations - -from opensquirrel.ir import IR, Instruction, AsmDeclaration -from opensquirrel.ir.single_qubit_gate import SingleQubitGate -from opensquirrel.ir.two_qubit_gate import TwoQubitGate -from collections import deque -from opensquirrel.ir.statement import Statement -from dataclasses import dataclass, field - -import networkx as nx - -def build_graph(ir: IR) -> nx.DiGraph: - n = len(ir.statements) - graph = nx.DiGraph() - graph.add_nodes_from(range(n)) - - for i, statement in enumerate(ir.statements): - if not isinstance(statement, Instruction): - continue - - qubit_indices = set(statement.qubit_indices) - for j in range(i + 1, n): - other_statement = ir.statements[j] - if isinstance(other_statement, Instruction): - other_qubit_indices = set(other_statement.qubit_indices) - - if inter := qubit_indices.intersection(other_qubit_indices): - graph.add_edge(i, j, qubit_index=tuple(inter)) - qubit_indices = qubit_indices.difference(inter) - - if not qubit_indices: - break - - if not nx.is_directed_acyclic_graph(graph): - raise ValueError - - return graph - - -def f(graph: nx.DiGraph): - qubit_indices = nx.get_edge_attributes(graph, "qubit_index", default=None) - diff --git a/opensquirrel/passes/merger/two_qubit_gates_merger.py b/opensquirrel/passes/merger/two_qubit_gates_merger.py index fc500f01..eccce851 100644 --- a/opensquirrel/passes/merger/two_qubit_gates_merger.py +++ b/opensquirrel/passes/merger/two_qubit_gates_merger.py @@ -1,43 +1,95 @@ -from copy import copy +from __future__ import annotations +from typing import TYPE_CHECKING + +import networkx as nx import numpy as np -from opensquirrel.circuit import Circuit from opensquirrel.circuit_builder import CircuitBuilder from opensquirrel.circuit_matrix_calculator import get_circuit_matrix -from opensquirrel.ir import IR, Gate, Qubit +from opensquirrel.ir import IR, Gate, Instruction, Qubit from opensquirrel.ir.semantics.matrix_gate import MatrixGateSemantic from opensquirrel.ir.single_qubit_gate import SingleQubitGate from opensquirrel.ir.two_qubit_gate import TwoQubitGate from opensquirrel.passes.merger.general_merger import Merger +if TYPE_CHECKING: + from opensquirrel.circuit import Circuit + + +def build_graph(ir: IR) -> nx.DiGraph: + n = len(ir.statements) + graph = nx.DiGraph() + graph.add_nodes_from( + (i, {"qubit_indices": statement.qubit_indices}) + for i, statement in enumerate(ir.statements) + if isinstance(statement, Instruction) + ) -def group_gates(gates: list[list[int]]) -> list[tuple[tuple[int, int], list[int]]]: - groups = [] - group = set() - statement_indices = [] - for i, qubit_indices in enumerate(gates): - # Reset group if current statement is not a Gate - if not qubit_indices: - if group: - groups.append((tuple(group), copy(statement_indices))) - group.clear() - statement_indices.clear() + for i, statement in enumerate(ir.statements): + if not isinstance(statement, Instruction): continue - # If the group has more than 2 qubits, it means that we have encountered a new group of gates. - if len(group | set(qubit_indices)) > 2: - if group: - groups.append((tuple(group), copy(statement_indices))) - group = set(qubit_indices) - statement_indices.clear() + qubit_indices = set(statement.qubit_indices) + for j in range(i + 1, n): + other_statement = ir.statements[j] + if isinstance(other_statement, Instruction): + other_qubit_indices = set(other_statement.qubit_indices) + + if inter := qubit_indices.intersection(other_qubit_indices): + graph.add_edge(i, j, qubit_index=tuple(inter)) + qubit_indices = qubit_indices.difference(inter) + + if not qubit_indices: + break + + if not nx.is_directed_acyclic_graph(graph): + raise ValueError + + return graph + + +def group_gates(graph: nx.DiGraph) -> list[tuple[set[int], set[int]]]: + groups: list[tuple[set, set]] = [] + available_nodes = set(graph.nodes) + + if len(available_nodes) == 1: + return [(available_nodes, set(graph.nodes[0]["qubit_indices"]))] + + for edge in graph.edges(): + source_node = graph.nodes[edge[0]] + target_node = graph.nodes[edge[1]] + + edge_nodes = {edge[0], edge[1]} + active_nodes = edge_nodes & available_nodes + qubit_indices = set(source_node["qubit_indices"]) | set(target_node["qubit_indices"]) + + matched_group = False + for group, indices in groups: + if not (group & edge_nodes): + continue + + if len(indices) == 1: + indices.update(qubit_indices) + + if qubit_indices.issubset(indices): + group.update(active_nodes) + available_nodes.difference_update(active_nodes) + + matched_group = True + break - group.update(qubit_indices) - statement_indices.append(i) + if not matched_group: + groups.append((active_nodes, set(qubit_indices))) + available_nodes.difference_update(active_nodes) - if group: - groups.append((tuple(group), copy(statement_indices))) - return groups + # Sort the groups, such that the order of the two qubit gates is preserved. + return sorted(groups, key=lambda x: _first_two_qubit_gate(graph, x[0])) + + +def _first_two_qubit_gate(graph: nx.DiGraph, group: set[int]) -> int: + """Return the first statement index pointing to a two qubit gate.""" + return min(i for i in group if len(graph.nodes[i]["qubit_indices"]) == 2) def normalize_gate_indices(gate: Gate) -> Gate: @@ -53,12 +105,26 @@ def normalize_gate_indices(gate: Gate) -> Gate: msg = f"Unsupported gate type: {type(gate)}" raise TypeError(msg) + +def _merge_gate_group(ir: IR, group: set[int], qubit_indices: set[int]) -> TwoQubitGate: + builder = CircuitBuilder(len(qubit_indices)) + for index in group: + statement = ir.statements[index] + if isinstance(statement, Gate): + statement = normalize_gate_indices(statement) + builder.add_instruction(statement) + + sub_circuit_matrix = _get_sub_circuit_matrix(builder.to_circuit()) + return TwoQubitGate(*qubit_indices, gate_semantic=MatrixGateSemantic(sub_circuit_matrix)) + + def _get_sub_circuit_matrix(circuit: Circuit) -> np.ndarray: # `get_circuit_matrix` uses the convention of the first qubit being the most significant bit, # so we need to swap the qubits before and after calculating the matrix swap = np.array([[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]]) return swap @ get_circuit_matrix(circuit) @ swap + class TwoQubitGatesMerger(Merger): def merge(self, ir: IR, qubit_register_size: int) -> None: """Merge all consecutive two-qubit gates in the circuit. @@ -68,38 +134,9 @@ def merge(self, ir: IR, qubit_register_size: int) -> None: qubit_register_size (int): Size of the qubit register """ + graph = build_graph(ir) + if len(graph.nodes) == 1: + return - gates = [statement.qubit_indices if isinstance(statement, Gate) else [] for statement in ir.statements] - groups = group_gates(gates) - - new_gates = [] - - for qubit_indices, statement_indices in groups: - if len(statement_indices) == 1: - new_gates.append(ir.statements[statement_indices[0]]) - continue - - builder = CircuitBuilder(len(qubit_indices)) - for statement_index in statement_indices: - statement = ir.statements[statement_index] - if isinstance(statement, Gate): - statement = normalize_gate_indices(statement) - builder.add_instruction(statement) - - sub_circuit_matrix = _get_sub_circuit_matrix(builder.to_circuit()) - gate = TwoQubitGate(*qubit_indices, gate_semantic=MatrixGateSemantic(sub_circuit_matrix)) - new_gates.append(gate) - - # Replace the original statements with the merged gates - new_statements = [] - current_index = 0 - for new_gate, (_, statement_indices) in zip(new_gates, groups, strict=True): - if current_index < statement_indices[0]: - new_statements.extend(ir.statements[current_index : statement_indices[0]]) - - new_statements.append(new_gate) - current_index = statement_indices[-1] + 1 - - # Replace the original statements with the merged gates - ir.statements = new_statements - + groups = group_gates(graph) + ir.statements = [_merge_gate_group(ir, group, qubit_indices) for group, qubit_indices in groups] diff --git a/tests/ir/test_node.py b/tests/ir/test_node.py deleted file mode 100644 index aeefb047..00000000 --- a/tests/ir/test_node.py +++ /dev/null @@ -1,47 +0,0 @@ -import pytest - -from opensquirrel.ir import IR, Qubit, Statement -from opensquirrel.ir.node import build_graph -from opensquirrel.ir.statement import Instruction -from opensquirrel import H, CNOT - - -class TestNodeGraph: - @pytest.mark.parametrize( - "statements, expected_edges", - [ - ( - [H(0), CNOT(0, 1), H(1)], - {(0, 1), (1, 2)}, - ), - ( - [H(1), CNOT(0, 1), H(1), H(2), CNOT(1, 2), H(2)], - {(0, 1), (1, 2), (2, 4), (3, 4), (4, 5)}, - ), - ( - [H(3), CNOT(2,3), H(1), H(2), CNOT(0, 1), H(2), H(1), H(3), CNOT(1, 2), H(2)], - {(0,1), (1,3), (1, 7), (6, 8), (2, 4), (4, 6), (3, 5), (5, 8), (8, 9)} - ) - ], - ) - def test_build_graph_creates_dependencies(self, statements: list[Statement], expected_edges) -> None: - ir = IR() - for statement in statements: - ir.add_statement(statement) - - graph = build_graph(ir) - - assert set(graph.edges()) == expected_edges - - - def test_build_graph_does_not_create_edges_for_unrelated_instructions(self) -> None: - ir = IR() - ir.add_statement(H(0)) - ir.add_statement(H(2)) - ir.add_statement(H(1)) - - graph = build_graph(ir) - - assert list(graph.edges()) == [] - - \ No newline at end of file diff --git a/tests/passes/merger/test_two_qubit_gates_merger.py b/tests/passes/merger/test_two_qubit_gates_merger.py index 58d973c6..e2063c99 100644 --- a/tests/passes/merger/test_two_qubit_gates_merger.py +++ b/tests/passes/merger/test_two_qubit_gates_merger.py @@ -1,10 +1,71 @@ import pytest -from opensquirrel import circuit_matrix_calculator +from opensquirrel import CNOT, H, circuit_matrix_calculator from opensquirrel.circuit import Circuit from opensquirrel.circuit_builder import CircuitBuilder from opensquirrel.common import are_matrices_equivalent_up_to_global_phase -from opensquirrel.passes.merger.two_qubit_gates_merger import TwoQubitGatesMerger, group_gates +from opensquirrel.ir import IR, Statement +from opensquirrel.passes.merger.two_qubit_gates_merger import TwoQubitGatesMerger, build_graph, group_gates + + +class TestNodeGraph: + @pytest.mark.parametrize( + ("statements", "expected_edges"), + [ + ( + [H(0), H(1), H(2)], + set(), + ), + ( + [H(0), CNOT(0, 1), H(1)], + {(0, 1), (1, 2)}, + ), + ( + [H(1), CNOT(0, 1), H(1), H(2), CNOT(1, 2), H(2)], + {(0, 1), (1, 2), (2, 4), (3, 4), (4, 5)}, + ), + ( + [H(3), CNOT(2, 3), H(1), H(2), CNOT(0, 1), H(2), H(1), H(3), CNOT(1, 2), H(2)], + {(0, 1), (1, 3), (1, 7), (6, 8), (2, 4), (4, 6), (3, 5), (5, 8), (8, 9)}, + ), + ], + ) + def test_build_graph_creates_dependencies( + self, statements: list[Statement], expected_edges: set[tuple[int, int]] + ) -> None: + ir = IR() + for statement in statements: + ir.add_statement(statement) + + graph = build_graph(ir) + + assert set(graph.edges()) == expected_edges + + @pytest.mark.parametrize( + ("statements", "expected"), + [ + ([H(0)], [({0}, {0})]), + ([CNOT(0, 1)], [({0}, {0, 1})]), + ([H(0), CNOT(0, 1), H(1)], [({0, 1, 2}, {0, 1})]), + ([H(1), CNOT(0, 1), H(1), H(2), CNOT(1, 2), H(2)], [({0, 1, 2}, {0, 1}), ({3, 4, 5}, {1, 2})]), + ( + [H(3), CNOT(2, 3), H(1), H(2), CNOT(0, 1), H(2), H(1), H(3), CNOT(1, 2), H(2)], + [({0, 1, 3, 5, 7}, {2, 3}), ({2, 4, 6}, {0, 1}), ({8, 9}, {1, 2})], + ), + ( + [H(2), H(0), CNOT(0, 1), CNOT(1, 2), CNOT(2, 3), H(1), H(3), H(0), H(2)], + [({1, 2, 7}, {0, 1}), ({0, 3, 5}, {1, 2}), ({4, 6, 8}, {2, 3})], + ), + ], + ) + def test_group_gates(self, statements: list[Statement], expected: list[tuple[set[int], set[int]]]) -> None: + ir = IR() + for statement in statements: + ir.add_statement(statement) + + graph = build_graph(ir) + + assert group_gates(graph) == expected @pytest.fixture @@ -12,24 +73,6 @@ def merger() -> TwoQubitGatesMerger: return TwoQubitGatesMerger() -@pytest.mark.parametrize( - ("gates", "expected_groups"), - [ - ([[0]], [((0,), [0])]), - ([[0, 1]], [((0, 1), [0])]), - ([[0, 1], [0, 1], [1, 2], [1, 2]], [((0, 1), [0, 1]), ((1, 2), [2, 3])]), - ([[0, 1], [], [1, 2], [1, 2]], [((0, 1), [0]), ((1, 2), [2, 3])]), - ([[0, 1], [0, 1], [], [1, 2], [1, 2]], [((0, 1), [0, 1]), ((1, 2), [3, 4])]), - ( - [[0, 1], [2, 1], [1, 2], [1], [1], [0, 1], [0], [1]], - [((0, 1), [0]), ((1, 2), [1, 2, 3, 4]), ((0, 1), [5, 6, 7])], - ), - ], -) -def test_group_gates(gates: list[list[int]], expected_groups: list[tuple[tuple[int, int], list[int]]]) -> None: - assert group_gates(gates) == expected_groups - - @pytest.mark.parametrize( ("circuit", "expected_circuit"), [ @@ -42,8 +85,8 @@ def test_group_gates(gates: list[list[int]], expected_groups: list[tuple[tuple[i (CircuitBuilder(2).CV(0, 1).CV(0, 1).to_circuit(), CircuitBuilder(2).CNOT(0, 1).to_circuit()), (CircuitBuilder(2).H(0).H(1).CNOT(0, 1).H(0).H(1).to_circuit(), CircuitBuilder(2).CNOT(1, 0).to_circuit()), ( - CircuitBuilder(2).CV(0, 1).to_circuit(), CircuitBuilder(2).T(0).H(1).CNOT(1, 0).Tdag(0).T(1).CNOT(1, 0).H(1).to_circuit(), + CircuitBuilder(2).CV(0, 1).to_circuit(), ), ], )