From 7e7db891365e2499553337b7d13e935f5d0037bd Mon Sep 17 00:00:00 2001 From: Melf Date: Thu, 19 Dec 2024 17:01:25 +0000 Subject: [PATCH 1/9] use pytket-qir for qir generation --- .github/workflows/issue-to-project.yml | 2 +- _metadata.py | 2 +- docs/changelog.rst | 6 + pytket/extensions/azure/backends/azure.py | 77 +++++++++-- setup.py | 3 +- tests/backend_test.py | 155 ++++++++++++++++++++-- 6 files changed, 220 insertions(+), 25 deletions(-) diff --git a/.github/workflows/issue-to-project.yml b/.github/workflows/issue-to-project.yml index 872f433..86af809 100644 --- a/.github/workflows/issue-to-project.yml +++ b/.github/workflows/issue-to-project.yml @@ -12,5 +12,5 @@ jobs: steps: - uses: actions/add-to-project@v1.0.2 with: - project-url: https://github.com/orgs/CQCL-DEV/projects/19 + project-url: https://github.com/orgs/quantinuum-dev/projects/19 github-token: ${{ secrets.ADD_TO_PROJECT_PAT }} diff --git a/_metadata.py b/_metadata.py index d5ce1a9..e156add 100644 --- a/_metadata.py +++ b/_metadata.py @@ -1,2 +1,2 @@ -__extension_version__ = "0.3.0" +__extension_version__ = "0.4.0rc0" __extension_name__ = "pytket-azure" diff --git a/docs/changelog.rst b/docs/changelog.rst index 39fc890..35ad170 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -3,6 +3,12 @@ Changelog ~~~~~~~~~ +0.4.0rc0 (December 2024) +------------------------ + +* Update minimum pytket version to 1.37.0. +* Update minimum pytket-qir version to 0.19.0. + 0.3.0 (October 2024) -------------------- diff --git a/pytket/extensions/azure/backends/azure.py b/pytket/extensions/azure/backends/azure.py index 559582d..b9c7a0a 100644 --- a/pytket/extensions/azure/backends/azure.py +++ b/pytket/extensions/azure/backends/azure.py @@ -19,8 +19,6 @@ from functools import cache from typing import Any, Optional, Union, cast -from qiskit_qir import to_qir_module - from azure.quantum import Job, Workspace from pytket.backends import Backend, CircuitStatus, ResultHandle, StatusEnum from pytket.backends.backend import KwargTypes @@ -30,9 +28,9 @@ from pytket.backends.resulthandle import _ResultIdTuple from pytket.circuit import Circuit, OpType from pytket.extensions.azure._metadata import __extension_version__ -from pytket.extensions.qiskit import tk_to_qiskit from pytket.passes import AutoRebase, BasePass from pytket.predicates import GateSetPredicate, Predicate +from pytket.qir import QIRFormat, QIRProfile, pytket_to_qir from pytket.utils import OutcomeArray from .config import AzureConfig @@ -78,6 +76,26 @@ def _get_workspace( } +_ADDITIONAL_GATES = { + OpType.Reset, + OpType.Measure, + OpType.Barrier, + OpType.RangePredicate, + OpType.MultiBit, + OpType.ExplicitPredicate, + OpType.ExplicitModifier, + OpType.SetBits, + OpType.CopyBits, + OpType.ClassicalExpBox, + OpType.ClExpr, + OpType.WASM, +} + + +_ALL_GATES = _ADDITIONAL_GATES.copy() +_ALL_GATES.update(_GATE_SET) + + class AzureBackend(Backend): """Interface to Azure Quantum.""" @@ -129,6 +147,8 @@ def __init__( ) _persistent_handles = False self._jobs: dict[ResultHandle, Job] = {} + self._result_bits: dict[ResultHandle, list] = {} + self._result_c_regs: dict[ResultHandle, list] = {} @property def backend_info(self) -> BackendInfo: @@ -136,7 +156,7 @@ def backend_info(self) -> BackendInfo: @property def required_predicates(self) -> list[Predicate]: - return [GateSetPredicate(_GATE_SET)] + return [GateSetPredicate(_ALL_GATES)] def rebase_pass(self) -> BasePass: return AutoRebase(gateset=_GATE_SET) @@ -177,18 +197,33 @@ def process_circuits( handles = [] for i, (c, n_shots) in enumerate(zip(circuits, n_shots_list)): - qkc = tk_to_qiskit(c) - module, entry_points = to_qir_module(qkc) - assert len(entry_points) == 1 input_params = { - "entryPoint": entry_points[0], + "entryPoint": "main", "arguments": [], "count": n_shots, } + if self._backendinfo.device_name == "ionq.simulator": + module_bitcode = pytket_to_qir( + c, + qir_format=QIRFormat.BINARY, + int_type=64, + cut_pytket_register=False, + profile=QIRProfile.AZUREBASE, + ) + raise ValueError("ionq devices currently not supported") + else: + module_bitcode = pytket_to_qir( + c, + qir_format=QIRFormat.BINARY, + int_type=64, + cut_pytket_register=False, + profile=QIRProfile.AZUREADAPTIVE, + ) + if option_params is not None: input_params.update(option_params) # type: ignore job = self._target.submit( - input_data=module.bitcode, + input_data=module_bitcode, input_data_format="qir.v1", output_data_format="microsoft.quantum-results.v1", name=f"job_{i}", @@ -198,6 +233,8 @@ def process_circuits( handle = ResultHandle(jobid) handles.append(handle) self._jobs[handle] = job + self._result_bits[handle] = c.bits + self._result_c_regs[handle] = c.c_registers for handle in handles: self._cache[handle] = dict() return handles @@ -210,15 +247,27 @@ def _update_cache_result( else: self._cache[handle] = result_dict - def _make_backend_result(self, results: Any, job: Job) -> BackendResult: + def _make_backend_result( + self, results: Any, job: Job, handle: ResultHandle + ) -> BackendResult: n_shots = job.details.input_params["count"] counts: Counter[OutcomeArray] = Counter() for s, p in results.items(): outcome = literal_eval(s) n = int(n_shots * p + 0.5) - oa = OutcomeArray.from_readouts([outcome]) - counts[oa] = n - return BackendResult(counts=counts) + assert len(outcome) == len(self._result_c_regs[handle]) + list_bits: list = [] + for res, creg in zip(outcome, self._result_c_regs[handle]): + long_res = bin(int(res)).replace( + "0b", + "0000000000000000000000000000000000000\ +00000000000000000000000000", # 0 * 63 + ) + list_bits.append(long_res[len(long_res) - creg.size : len(long_res)]) + all_bits = "".join(list_bits) + + counts[OutcomeArray.from_readouts([[int(x) for x in list(all_bits)]])] = n + return BackendResult(counts=counts, c_bits=self._result_bits[handle]) def circuit_status(self, handle) -> CircuitStatus: job = self._jobs[handle] @@ -228,7 +277,7 @@ def circuit_status(self, handle) -> CircuitStatus: results = job.get_results() self._update_cache_result( handle, - {"result": self._make_backend_result(results, job)}, + {"result": self._make_backend_result(results, job, handle)}, ) return CircuitStatus(StatusEnum.COMPLETED) elif status == "Waiting": diff --git a/setup.py b/setup.py index ce0c58a..ce4a707 100644 --- a/setup.py +++ b/setup.py @@ -56,9 +56,10 @@ include_package_data=True, install_requires=[ "azure-quantum >= 2.2.0", - "pytket >= 1.34.0", + "pytket >= 1.37.0", "pytket-qiskit >= 0.58.0", "qiskit-qir >= 0.5.0", + "pytket-qir >= 0.19.0", ], classifiers=[ "Environment :: Console", diff --git a/tests/backend_test.py b/tests/backend_test.py index b66c32c..dfc36a3 100644 --- a/tests/backend_test.py +++ b/tests/backend_test.py @@ -14,11 +14,18 @@ import os from collections import Counter -from warnings import warn import pytest -from pytket.circuit import Circuit +from pytket.circuit import Circuit, Qubit, if_not_bit +from pytket.circuit.logic_exp import ( + reg_eq, + reg_geq, + reg_gt, + reg_leq, + reg_lt, + reg_neq, +) from pytket.extensions.azure import AzureBackend skip_remote_tests: bool = os.getenv("PYTKET_RUN_REMOTE_TESTS") is None @@ -26,6 +33,7 @@ @pytest.mark.skipif(skip_remote_tests, reason=REASON) +@pytest.mark.skip(reason="resulthandling currently not supported for ionq") @pytest.mark.parametrize("azure_backend", ["ionq.simulator"], indirect=True) def test_ionq_simulator(azure_backend: AzureBackend) -> None: c = Circuit(2).H(0).CX(0, 1).measure_all() @@ -37,7 +45,9 @@ def test_ionq_simulator(azure_backend: AzureBackend) -> None: counts = r.get_counts() assert counts == Counter({(0, 0): 5, (1, 1): 5}) else: - warn("ionq.simulator unavailable or queue time >= 60s: not submitting") + raise ValueError( + "quantinuum.sim.h1-1sc unavailable or queue time >= 60s: not submitting" + ) @pytest.mark.skipif(skip_remote_tests, reason=REASON) @@ -52,19 +62,148 @@ def test_quantinuum_sim_h11e(azure_backend: AzureBackend) -> None: counts = r.get_counts() assert sum(counts.values()) == 1000 else: - warn("quantinuum.sim.h1-1sc unavailable or queue time >= 60s: not submitting") + raise ValueError( + "quantinuum.sim.h1-1sc unavailable or queue time >= 60s: not submitting" + ) + + +@pytest.mark.skipif(skip_remote_tests, reason=REASON) +@pytest.mark.parametrize("azure_backend", ["quantinuum.sim.h1-1sc"], indirect=True) +def test_quantinuum_sim_h11e_two_regs(azure_backend: AzureBackend) -> None: + c = Circuit(2, name="test_classical") + a = c.add_c_register("a", 10) + b = c.add_c_register("b", 11) + + c.Measure(Qubit(0), a[0]) + c.Measure(Qubit(1), b[0]) + + b = azure_backend + c1 = b.get_compiled_circuit(c) + if b.is_available() and b.average_queue_time_s() < 60: + h = b.process_circuit(c1, n_shots=1000) + r = b.get_result(h, timeout=120) + counts = r.get_counts() + assert sum(counts.values()) == 1000 + else: + raise ValueError( + "quantinuum.sim.h1-1sc unavailable or queue time >= 60s: not submitting" + ) + + +@pytest.mark.skipif(skip_remote_tests, reason=REASON) +@pytest.mark.parametrize("azure_backend", ["quantinuum.sim.h1-1sc"], indirect=True) +def test_quantinuum_sim_h11e_complex(azure_backend: AzureBackend) -> None: + c = Circuit(1, name="test_classical") + a = c.add_c_register("a", 10) + b = c.add_c_register("b", 11) + d = c.add_c_register("d", 20) + + c.Measure(Qubit(0), a[0]) + + c.add_c_setbits([True, True] + [False] * 9, list(b)) + + c.add_classicalexpbox_register(a + b, d) # type: ignore + b = azure_backend + c1 = b.get_compiled_circuit(c) + if b.is_available() and b.average_queue_time_s() < 60: + h = b.process_circuit(c1, n_shots=1000) + r = b.get_result(h, timeout=120) + counts = r.get_counts() + assert sum(counts.values()) == 1000 + else: + raise ValueError( + "quantinuum.sim.h1-1sc unavailable or queue time >= 60s: not submitting" + ) + + +@pytest.mark.skipif(skip_remote_tests, reason=REASON) +@pytest.mark.parametrize("azure_backend", ["quantinuum.sim.h1-1sc"], indirect=True) +def test_quantinuum_sim_h11e_cond(azure_backend: AzureBackend) -> None: + c = Circuit(1, name="test_classical") + a = c.add_c_register("a", 32) + b = c.add_c_register("b", 32) + d = c.add_c_register("d", 32) + + c.Measure(Qubit(0), a[0]) + + c.add_c_setreg(23, b) + + c.add_classicalexpbox_register(a + b, d) # type: ignore + + c.X(0, condition=a[0]) + c.Measure(Qubit(0), b[4]) + + b = azure_backend + c1 = b.get_compiled_circuit(c) + if b.is_available() and b.average_queue_time_s() < 60: + h = b.process_circuit(c1, n_shots=1000) + r = b.get_result(h, timeout=120) + counts = r.get_counts() + assert sum(counts.values()) == 1000 + else: + raise ValueError( + "quantinuum.sim.h1-1sc unavailable or queue time >= 60s: not submitting" + ) + + +@pytest.mark.skipif(skip_remote_tests, reason=REASON) +@pytest.mark.parametrize("azure_backend", ["quantinuum.sim.h1-1sc"], indirect=True) +def test_quantinuum_sim_h11e_cond_2(azure_backend: AzureBackend) -> None: + c = Circuit(1, name="test_classical") + a = c.add_c_register("a", 32) + b = c.add_c_register("b", 32) + d = c.add_c_register("d", 32) + + c.Measure(Qubit(0), a[0]) + + c.add_c_setreg(23, b) + + c.add_classicalexpbox_register(a + b, d) # type: ignore + c.add_classicalexpbox_register(a - b, d) # type: ignore + c.add_classicalexpbox_register(a << 1, a) # type: ignore + c.add_classicalexpbox_register(a >> 1, b) # type: ignore + + c.X(0, condition=reg_eq(a ^ b, 1)) + c.X(0, condition=(a[0] ^ b[0])) + c.X(0, condition=reg_eq(a & b, 1)) + c.X(0, condition=reg_eq(a | b, 1)) + + c.X(0, condition=a[0]) + c.Measure(Qubit(0), b[4]) + + c.X(0, condition=reg_neq(a, 1)) + c.X(0, condition=if_not_bit(a[0])) + c.X(0, condition=reg_gt(a, 1)) + c.X(0, condition=reg_lt(a, 1)) + c.X(0, condition=reg_geq(a, 1)) + c.X(0, condition=reg_leq(a, 1)) + c.Measure(Qubit(0), b[4]) + b = azure_backend + c1 = b.get_compiled_circuit(c) + if b.is_available() and b.average_queue_time_s() < 60: + h = b.process_circuit(c1, n_shots=1000) + r = b.get_result(h, timeout=120) + counts = r.get_counts() + assert sum(counts.values()) == 1000 + else: + raise ValueError( + "quantinuum.sim.h1-1sc unavailable or queue time >= 60s: not submitting" + ) @pytest.mark.skipif(skip_remote_tests, reason=REASON) @pytest.mark.parametrize("azure_backend", ["quantinuum.sim.h1-1e"], indirect=True) def test_quantinuum_option_params(azure_backend: AzureBackend) -> None: - c = Circuit(2).H(0).CX(0, 1).measure_all() + c = Circuit(2, 2).H(0).CX(0, 1).measure_all() b = azure_backend c1 = b.get_compiled_circuit(c) - if b.is_available() and b.average_queue_time_s() < 600: + if b.is_available() and b.average_queue_time_s() < 60: h = b.process_circuit(c1, n_shots=1000, option_params={"error_model": False}) # type: ignore r = b.get_result(h, timeout=1200) counts = r.get_counts() - assert all(x0 == x1 for x0, x1 in counts) + assert all(x[0] == x[1] for x in counts) + assert any(x[0] == 1 for x in counts) else: - warn("quantinuum.sim.h1-1e unavailable or queue time >= 600s: not submitting") + raise ValueError( + "quantinuum.sim.h1-1e unavailable or queue time >= 60s: not submitting" + ) From 5d4309dcbaf948efa0f35002e858002436ff3483 Mon Sep 17 00:00:00 2001 From: Melf Date: Thu, 19 Dec 2024 17:33:25 +0000 Subject: [PATCH 2/9] allow longer queue --- tests/backend_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/backend_test.py b/tests/backend_test.py index dfc36a3..adfb619 100644 --- a/tests/backend_test.py +++ b/tests/backend_test.py @@ -197,7 +197,7 @@ def test_quantinuum_option_params(azure_backend: AzureBackend) -> None: c = Circuit(2, 2).H(0).CX(0, 1).measure_all() b = azure_backend c1 = b.get_compiled_circuit(c) - if b.is_available() and b.average_queue_time_s() < 60: + if b.is_available() and b.average_queue_time_s() < 600: h = b.process_circuit(c1, n_shots=1000, option_params={"error_model": False}) # type: ignore r = b.get_result(h, timeout=1200) counts = r.get_counts() @@ -205,5 +205,5 @@ def test_quantinuum_option_params(azure_backend: AzureBackend) -> None: assert any(x[0] == 1 for x in counts) else: raise ValueError( - "quantinuum.sim.h1-1e unavailable or queue time >= 60s: not submitting" + "quantinuum.sim.h1-1e unavailable or queue time >= 600s: not submitting" ) From aa00cedee2f37f1f35bc116ccc093a5b7aa337f4 Mon Sep 17 00:00:00 2001 From: Melf Date: Fri, 20 Dec 2024 09:20:58 +0000 Subject: [PATCH 3/9] fix mypy --- tests/backend_test.py | 50 +++++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/tests/backend_test.py b/tests/backend_test.py index adfb619..cd9ea8f 100644 --- a/tests/backend_test.py +++ b/tests/backend_test.py @@ -77,11 +77,11 @@ def test_quantinuum_sim_h11e_two_regs(azure_backend: AzureBackend) -> None: c.Measure(Qubit(0), a[0]) c.Measure(Qubit(1), b[0]) - b = azure_backend - c1 = b.get_compiled_circuit(c) - if b.is_available() and b.average_queue_time_s() < 60: - h = b.process_circuit(c1, n_shots=1000) - r = b.get_result(h, timeout=120) + a_b = azure_backend + c1 = a_b.get_compiled_circuit(c) + if a_b.is_available() and a_b.average_queue_time_s() < 60: + h = a_b.process_circuit(c1, n_shots=1000) + r = a_b.get_result(h, timeout=120) counts = r.get_counts() assert sum(counts.values()) == 1000 else: @@ -103,11 +103,11 @@ def test_quantinuum_sim_h11e_complex(azure_backend: AzureBackend) -> None: c.add_c_setbits([True, True] + [False] * 9, list(b)) c.add_classicalexpbox_register(a + b, d) # type: ignore - b = azure_backend - c1 = b.get_compiled_circuit(c) - if b.is_available() and b.average_queue_time_s() < 60: - h = b.process_circuit(c1, n_shots=1000) - r = b.get_result(h, timeout=120) + a_b = azure_backend + c1 = a_b.get_compiled_circuit(c) + if a_b.is_available() and a_b.average_queue_time_s() < 60: + h = a_b.process_circuit(c1, n_shots=1000) + r = a_b.get_result(h, timeout=120) counts = r.get_counts() assert sum(counts.values()) == 1000 else: @@ -133,11 +133,11 @@ def test_quantinuum_sim_h11e_cond(azure_backend: AzureBackend) -> None: c.X(0, condition=a[0]) c.Measure(Qubit(0), b[4]) - b = azure_backend - c1 = b.get_compiled_circuit(c) - if b.is_available() and b.average_queue_time_s() < 60: - h = b.process_circuit(c1, n_shots=1000) - r = b.get_result(h, timeout=120) + a_b = azure_backend + c1 = a_b.get_compiled_circuit(c) + if a_b.is_available() and a_b.average_queue_time_s() < 60: + h = a_b.process_circuit(c1, n_shots=1000) + r = a_b.get_result(h, timeout=120) counts = r.get_counts() assert sum(counts.values()) == 1000 else: @@ -178,11 +178,11 @@ def test_quantinuum_sim_h11e_cond_2(azure_backend: AzureBackend) -> None: c.X(0, condition=reg_geq(a, 1)) c.X(0, condition=reg_leq(a, 1)) c.Measure(Qubit(0), b[4]) - b = azure_backend - c1 = b.get_compiled_circuit(c) - if b.is_available() and b.average_queue_time_s() < 60: - h = b.process_circuit(c1, n_shots=1000) - r = b.get_result(h, timeout=120) + a_b = azure_backend + c1 = a_b.get_compiled_circuit(c) + if a_b.is_available() and a_b.average_queue_time_s() < 60: + h = a_b.process_circuit(c1, n_shots=1000) + r = a_b.get_result(h, timeout=120) counts = r.get_counts() assert sum(counts.values()) == 1000 else: @@ -195,11 +195,11 @@ def test_quantinuum_sim_h11e_cond_2(azure_backend: AzureBackend) -> None: @pytest.mark.parametrize("azure_backend", ["quantinuum.sim.h1-1e"], indirect=True) def test_quantinuum_option_params(azure_backend: AzureBackend) -> None: c = Circuit(2, 2).H(0).CX(0, 1).measure_all() - b = azure_backend - c1 = b.get_compiled_circuit(c) - if b.is_available() and b.average_queue_time_s() < 600: - h = b.process_circuit(c1, n_shots=1000, option_params={"error_model": False}) # type: ignore - r = b.get_result(h, timeout=1200) + a_b = azure_backend + c1 = a_b.get_compiled_circuit(c) + if a_b.is_available() and a_b.average_queue_time_s() < 600: + h = a_b.process_circuit(c1, n_shots=1000, option_params={"error_model": False}) # type: ignore + r = a_b.get_result(h, timeout=1200) counts = r.get_counts() assert all(x[0] == x[1] for x in counts) assert any(x[0] == 1 for x in counts) From 85913e38cc23c49077eb7f0a1f1396436c7b1fcd Mon Sep 17 00:00:00 2001 From: cqc-melf <70640934+cqc-melf@users.noreply.github.com> Date: Fri, 20 Dec 2024 11:16:47 +0100 Subject: [PATCH 4/9] Update pytket/extensions/azure/backends/azure.py Co-authored-by: Alec Edgington <54802828+cqc-alec@users.noreply.github.com> --- pytket/extensions/azure/backends/azure.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pytket/extensions/azure/backends/azure.py b/pytket/extensions/azure/backends/azure.py index b9c7a0a..de21330 100644 --- a/pytket/extensions/azure/backends/azure.py +++ b/pytket/extensions/azure/backends/azure.py @@ -266,7 +266,7 @@ def _make_backend_result( list_bits.append(long_res[len(long_res) - creg.size : len(long_res)]) all_bits = "".join(list_bits) - counts[OutcomeArray.from_readouts([[int(x) for x in list(all_bits)]])] = n + counts[OutcomeArray.from_readouts([[int(x) for x in all_bits]])] = n return BackendResult(counts=counts, c_bits=self._result_bits[handle]) def circuit_status(self, handle) -> CircuitStatus: From 21e6a1f20f013fcf877ce0504ae8a33a14db590a Mon Sep 17 00:00:00 2001 From: Melf Date: Fri, 20 Dec 2024 11:00:58 +0000 Subject: [PATCH 5/9] requested changes --- pytket/extensions/azure/backends/azure.py | 50 ++++++++++++++--------- tests/backend_test.py | 5 +-- 2 files changed, 33 insertions(+), 22 deletions(-) diff --git a/pytket/extensions/azure/backends/azure.py b/pytket/extensions/azure/backends/azure.py index b9c7a0a..ed7670a 100644 --- a/pytket/extensions/azure/backends/azure.py +++ b/pytket/extensions/azure/backends/azure.py @@ -197,27 +197,29 @@ def process_circuits( handles = [] for i, (c, n_shots) in enumerate(zip(circuits, n_shots_list)): + input_params = { "entryPoint": "main", "arguments": [], "count": n_shots, } - if self._backendinfo.device_name == "ionq.simulator": + + if self._backendinfo.device_name[:11] == "quantinuum.": + module_bitcode = pytket_to_qir( c, qir_format=QIRFormat.BINARY, int_type=64, cut_pytket_register=False, - profile=QIRProfile.AZUREBASE, + profile=QIRProfile.AZUREADAPTIVE, ) - raise ValueError("ionq devices currently not supported") else: module_bitcode = pytket_to_qir( c, qir_format=QIRFormat.BINARY, int_type=64, cut_pytket_register=False, - profile=QIRProfile.AZUREADAPTIVE, + profile=QIRProfile.AZUREBASE, ) if option_params is not None: @@ -252,22 +254,32 @@ def _make_backend_result( ) -> BackendResult: n_shots = job.details.input_params["count"] counts: Counter[OutcomeArray] = Counter() - for s, p in results.items(): - outcome = literal_eval(s) - n = int(n_shots * p + 0.5) - assert len(outcome) == len(self._result_c_regs[handle]) - list_bits: list = [] - for res, creg in zip(outcome, self._result_c_regs[handle]): - long_res = bin(int(res)).replace( - "0b", - "0000000000000000000000000000000000000\ + if self._backendinfo.device_name[:11] == "quantinuum.": + for s, p in results.items(): + outcome = literal_eval(s) + n = int(n_shots * p + 0.5) + assert len(outcome) == len(self._result_c_regs[handle]) + list_bits: list = [] + for res, creg in zip(outcome, self._result_c_regs[handle]): + long_res = bin(int(res)).replace( + "0b", + "0000000000000000000000000000000000000\ 00000000000000000000000000", # 0 * 63 - ) - list_bits.append(long_res[len(long_res) - creg.size : len(long_res)]) - all_bits = "".join(list_bits) - - counts[OutcomeArray.from_readouts([[int(x) for x in list(all_bits)]])] = n - return BackendResult(counts=counts, c_bits=self._result_bits[handle]) + ) + list_bits.append( + long_res[len(long_res) - creg.size : len(long_res)] + ) + all_bits = "".join(list_bits) + + counts[OutcomeArray.from_readouts([[int(x) for x in all_bits]])] = n + return BackendResult(counts=counts, c_bits=self._result_bits[handle]) + else: + for s, p in results.items(): + outcome = literal_eval(s) + n = int(n_shots * p + 0.5) + oa = OutcomeArray.from_readouts([outcome]) + counts[oa] = n + return BackendResult(counts=counts) def circuit_status(self, handle) -> CircuitStatus: job = self._jobs[handle] diff --git a/tests/backend_test.py b/tests/backend_test.py index cd9ea8f..f2c7d56 100644 --- a/tests/backend_test.py +++ b/tests/backend_test.py @@ -33,7 +33,6 @@ @pytest.mark.skipif(skip_remote_tests, reason=REASON) -@pytest.mark.skip(reason="resulthandling currently not supported for ionq") @pytest.mark.parametrize("azure_backend", ["ionq.simulator"], indirect=True) def test_ionq_simulator(azure_backend: AzureBackend) -> None: c = Circuit(2).H(0).CX(0, 1).measure_all() @@ -197,12 +196,12 @@ def test_quantinuum_option_params(azure_backend: AzureBackend) -> None: c = Circuit(2, 2).H(0).CX(0, 1).measure_all() a_b = azure_backend c1 = a_b.get_compiled_circuit(c) - if a_b.is_available() and a_b.average_queue_time_s() < 600: + if a_b.is_available() and a_b.average_queue_time_s() < 6000: h = a_b.process_circuit(c1, n_shots=1000, option_params={"error_model": False}) # type: ignore r = a_b.get_result(h, timeout=1200) counts = r.get_counts() assert all(x[0] == x[1] for x in counts) - assert any(x[0] == 1 for x in counts) + assert any(x[0] == 1 for x in counts) # might fail in very rare cases else: raise ValueError( "quantinuum.sim.h1-1e unavailable or queue time >= 600s: not submitting" From 3a89c6e13a7f9ca2e549eb606d6188735a8a3a5c Mon Sep 17 00:00:00 2001 From: Melf Date: Fri, 20 Dec 2024 11:05:04 +0000 Subject: [PATCH 6/9] remove qiskit, update changelog and version --- _metadata.py | 2 +- docs/changelog.rst | 7 ++++--- setup.py | 2 -- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/_metadata.py b/_metadata.py index e156add..e9589dd 100644 --- a/_metadata.py +++ b/_metadata.py @@ -1,2 +1,2 @@ -__extension_version__ = "0.4.0rc0" +__extension_version__ = "0.4.0" __extension_name__ = "pytket-azure" diff --git a/docs/changelog.rst b/docs/changelog.rst index 35ad170..97e3715 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -3,11 +3,13 @@ Changelog ~~~~~~~~~ -0.4.0rc0 (December 2024) ------------------------- +0.4.0 (unreleased) +------------------ * Update minimum pytket version to 1.37.0. * Update minimum pytket-qir version to 0.19.0. +* Use pytket-qir for the QIR generation +* remove qiskit-qir and pytket-qiskit as dependencies 0.3.0 (October 2024) -------------------- @@ -17,7 +19,6 @@ Changelog * Update minimum pytket version to 1.34.0. * Update minimum pytket-qiskit version to 0.58.0. - 0.2.0 (October 2024) --------------------- diff --git a/setup.py b/setup.py index ce4a707..a174de7 100644 --- a/setup.py +++ b/setup.py @@ -57,8 +57,6 @@ install_requires=[ "azure-quantum >= 2.2.0", "pytket >= 1.37.0", - "pytket-qiskit >= 0.58.0", - "qiskit-qir >= 0.5.0", "pytket-qir >= 0.19.0", ], classifiers=[ From 22c085626b1c5895c6dde04741fc55393ed0ce6c Mon Sep 17 00:00:00 2001 From: Melf Date: Fri, 20 Dec 2024 11:15:50 +0000 Subject: [PATCH 7/9] clean up --- pytket/extensions/azure/backends/azure.py | 10 ++++++++-- tests/backend_test.py | 4 ++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/pytket/extensions/azure/backends/azure.py b/pytket/extensions/azure/backends/azure.py index ed7670a..40a05ef 100644 --- a/pytket/extensions/azure/backends/azure.py +++ b/pytket/extensions/azure/backends/azure.py @@ -204,7 +204,10 @@ def process_circuits( "count": n_shots, } - if self._backendinfo.device_name[:11] == "quantinuum.": + if ( + self._backendinfo.device_name + and self._backendinfo.device_name[:11] == "quantinuum." + ): module_bitcode = pytket_to_qir( c, @@ -254,7 +257,10 @@ def _make_backend_result( ) -> BackendResult: n_shots = job.details.input_params["count"] counts: Counter[OutcomeArray] = Counter() - if self._backendinfo.device_name[:11] == "quantinuum.": + if ( + self._backendinfo.device_name + and self._backendinfo.device_name[:11] == "quantinuum." + ): for s, p in results.items(): outcome = literal_eval(s) n = int(n_shots * p + 0.5) diff --git a/tests/backend_test.py b/tests/backend_test.py index f2c7d56..a5bd741 100644 --- a/tests/backend_test.py +++ b/tests/backend_test.py @@ -45,7 +45,7 @@ def test_ionq_simulator(azure_backend: AzureBackend) -> None: assert counts == Counter({(0, 0): 5, (1, 1): 5}) else: raise ValueError( - "quantinuum.sim.h1-1sc unavailable or queue time >= 60s: not submitting" + "ionq.simulator unavailable or queue time >= 60s: not submitting" ) @@ -196,7 +196,7 @@ def test_quantinuum_option_params(azure_backend: AzureBackend) -> None: c = Circuit(2, 2).H(0).CX(0, 1).measure_all() a_b = azure_backend c1 = a_b.get_compiled_circuit(c) - if a_b.is_available() and a_b.average_queue_time_s() < 6000: + if a_b.is_available() and a_b.average_queue_time_s() < 600: h = a_b.process_circuit(c1, n_shots=1000, option_params={"error_model": False}) # type: ignore r = a_b.get_result(h, timeout=1200) counts = r.get_counts() From 01553168611942835f7c9179d2eac9ce27ee555c Mon Sep 17 00:00:00 2001 From: cqc-melf <70640934+cqc-melf@users.noreply.github.com> Date: Fri, 20 Dec 2024 11:46:42 +0000 Subject: [PATCH 8/9] Update pytket/extensions/azure/backends/azure.py Co-authored-by: Alec Edgington <54802828+cqc-alec@users.noreply.github.com> --- pytket/extensions/azure/backends/azure.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pytket/extensions/azure/backends/azure.py b/pytket/extensions/azure/backends/azure.py index 40a05ef..7731c0e 100644 --- a/pytket/extensions/azure/backends/azure.py +++ b/pytket/extensions/azure/backends/azure.py @@ -273,7 +273,7 @@ def _make_backend_result( 00000000000000000000000000", # 0 * 63 ) list_bits.append( - long_res[len(long_res) - creg.size : len(long_res)] + long_res[-creg.size:] ) all_bits = "".join(list_bits) From f9afca653eda1cf642c25a8bee5cfca9d9f13180 Mon Sep 17 00:00:00 2001 From: Melf Date: Fri, 20 Dec 2024 11:49:28 +0000 Subject: [PATCH 9/9] format --- pytket/extensions/azure/backends/azure.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pytket/extensions/azure/backends/azure.py b/pytket/extensions/azure/backends/azure.py index 7731c0e..5b7fc44 100644 --- a/pytket/extensions/azure/backends/azure.py +++ b/pytket/extensions/azure/backends/azure.py @@ -272,9 +272,7 @@ def _make_backend_result( "0000000000000000000000000000000000000\ 00000000000000000000000000", # 0 * 63 ) - list_bits.append( - long_res[-creg.size:] - ) + list_bits.append(long_res[-creg.size :]) all_bits = "".join(list_bits) counts[OutcomeArray.from_readouts([[int(x) for x in all_bits]])] = n