diff --git a/src/iqm/cirq_iqm/__init__.py b/src/iqm/cirq_iqm/__init__.py index 5ac7ec21..981603bb 100644 --- a/src/iqm/cirq_iqm/__init__.py +++ b/src/iqm/cirq_iqm/__init__.py @@ -25,6 +25,6 @@ __version__ = 'unknown' finally: del version, PackageNotFoundError - +# pylint: disable=wrong-import-position from .iqm_gates import * from .transpiler import transpile_insert_moves_into_circuit diff --git a/src/iqm/cirq_iqm/devices/iqm_device.py b/src/iqm/cirq_iqm/devices/iqm_device.py index 69e95ac2..f3d75c26 100644 --- a/src/iqm/cirq_iqm/devices/iqm_device.py +++ b/src/iqm/cirq_iqm/devices/iqm_device.py @@ -23,12 +23,12 @@ import collections.abc as ca from itertools import zip_longest from math import pi as PI -from typing import Optional, cast, Sequence +from typing import Optional, Sequence, cast import uuid import cirq from cirq import InsertStrategy, MeasurementGate, devices, ops, protocols -import networkx as nx +from cirq.contrib.routing.router import nx from iqm.cirq_iqm.iqm_gates import IQMMoveGate from iqm.cirq_iqm.transpiler import transpile_insert_moves_into_circuit @@ -357,7 +357,7 @@ def validate_moves(self, circuit: cirq.AbstractCircuit) -> None: Returns: None if the IQMMoveGates are applied correctly. """ - moves: dict[cirq.Qid, list[tuple[cirq.Qid, cirq.Qid]]] = {r: [] for r in self.resonators} + moves: dict[cirq.Qid, list[cirq.Qid]] = {r: [] for r in self.resonators} for moment in circuit: for operation in moment.operations: if isinstance(operation.gate, IQMMoveGate): diff --git a/src/iqm/cirq_iqm/devices/iqm_device_metadata.py b/src/iqm/cirq_iqm/devices/iqm_device_metadata.py index b306baee..8e12fec9 100644 --- a/src/iqm/cirq_iqm/devices/iqm_device_metadata.py +++ b/src/iqm/cirq_iqm/devices/iqm_device_metadata.py @@ -18,7 +18,7 @@ from typing import FrozenSet, Optional import cirq -from cirq import NamedQid, NamedQubit, Qid, devices, ops +from cirq import Gate, NamedQid, devices, ops from cirq.contrib.routing.router import nx from iqm.cirq_iqm.iqm_operation_mapping import _IQM_CIRQ_OP_MAP @@ -43,11 +43,11 @@ class IQMDeviceMetadata(devices.DeviceMetadata): def __init__( self, - qubits: Iterable[NamedQubit], - connectivity: Iterable[Iterable[Qid]], - operations: Optional[dict[type[cirq.Gate], list[tuple[cirq.Qid, ...]]]] = None, + qubits: Iterable[NamedQid], + connectivity: Iterable[Iterable[NamedQid]], + operations: Optional[dict[type[cirq.Gate], list[tuple[cirq.NamedQid, ...]]]] = None, gateset: Optional[cirq.Gateset] = None, - resonators: Iterable[Qid] = (), + resonators: Iterable[NamedQid] = (), ): """Construct an IQMDeviceMetadata object.""" nx_graph = nx.Graph() @@ -55,7 +55,8 @@ def __init__( edge_qubits = list(edge) nx_graph.add_edge(edge_qubits[0], edge_qubits[1]) super().__init__(qubits, nx_graph) - self._resonator_set: FrozenSet[Qid] = frozenset(resonators) + self._qubit_set: FrozenSet[NamedQid] = frozenset(qubits) + self._resonator_set: FrozenSet[NamedQid] = frozenset(resonators) if gateset is None: if operations is None: @@ -63,22 +64,20 @@ def __init__( gateset = cirq.Gateset( ops.PhasedXPowGate, ops.XPowGate, ops.YPowGate, ops.MeasurementGate, ops.CZPowGate ) - qb_list: list[tuple[cirq.Qid, ...]] = [(qb,) for qb in qubits] - operations = { - ops.PhasedXPowGate: qb_list, - ops.XPowGate: qb_list, - ops.YPowGate: qb_list, - ops.MeasurementGate: qb_list, - ops.CZPowGate: list(tuple(edge) for edge in connectivity), - } + sqg_list: list[type[Gate]] = [ops.PhasedXPowGate, ops.XPowGate, ops.YPowGate, ops.MeasurementGate] + operations = {} + operations[ops.CZPowGate] = list(tuple(edge) for edge in connectivity) + operations.update({gate: [(qb,) for qb in qubits] for gate in sqg_list}) else: gateset = cirq.Gateset(*operations.keys()) self._gateset = gateset + if operations is None: + raise ValueError('Operations must be provided if a gateset is provided, it cannot be reconstructed.') self.operations = operations @property - def resonator_set(self) -> FrozenSet[Qid]: + def resonator_set(self) -> FrozenSet[NamedQid]: """Returns the set of resonators on the device. Returns: @@ -89,7 +88,7 @@ def resonator_set(self) -> FrozenSet[Qid]: @classmethod def from_architecture(cls, architecture: QuantumArchitectureSpecification) -> IQMDeviceMetadata: """Returns device metadata object created based on architecture specification""" - qubits = tuple(NamedQubit(qb) for qb in architecture.qubits if qb.startswith(cls.QUBIT_NAME_PREFIX)) + qubits = tuple(NamedQid(qb, dimension=2) for qb in architecture.qubits if qb.startswith(cls.QUBIT_NAME_PREFIX)) resonators = tuple( NamedQid(qb, dimension=cls.RESONATOR_DIMENSION) for qb in architecture.qubits @@ -98,7 +97,7 @@ def from_architecture(cls, architecture: QuantumArchitectureSpecification) -> IQ connectivity = tuple( tuple( ( - NamedQubit(qb) + NamedQid(qb, dimension=2) if qb.startswith(cls.QUBIT_NAME_PREFIX) else NamedQid(qb, dimension=cls.RESONATOR_DIMENSION) ) @@ -106,11 +105,11 @@ def from_architecture(cls, architecture: QuantumArchitectureSpecification) -> IQ ) for edge in architecture.qubit_connectivity ) - operations: dict[type[cirq.Gate], list[tuple[cirq.Qid, ...]]] = { + operations: dict[type[cirq.Gate], list[tuple[NamedQid, ...]]] = { cirq_op: [ tuple( ( - NamedQubit(qb) + NamedQid(qb, dimension=2) if qb.startswith(cls.QUBIT_NAME_PREFIX) else NamedQid(qb, dimension=cls.RESONATOR_DIMENSION) ) @@ -125,7 +124,7 @@ def from_architecture(cls, architecture: QuantumArchitectureSpecification) -> IQ def to_architecture(self) -> QuantumArchitectureSpecification: """Returns the architecture specification object created based on device metadata.""" - qubits = tuple(qb.name for qb in self.qubit_set) + qubits = tuple(qb.name for qb in self._qubit_set) resonators = tuple(qb.name for qb in self.resonator_set) connectivity = tuple(tuple(qb.name for qb in edge) for edge in self.nx_graph.edges()) operations: dict[str, list[tuple[str, ...]]] = { @@ -140,26 +139,28 @@ def to_architecture(self) -> QuantumArchitectureSpecification: @classmethod def from_qubit_indices( - cls, qubit_count: int, connectivity_indices: tuple[set[int], ...], gateset: Optional[tuple[cirq.Gate]] = None + cls, + qubit_count: int, + connectivity_indices: tuple[set[int], ...], + gateset: Optional[tuple[type[cirq.Gate]]] = None, ) -> IQMDeviceMetadata: """Returns device metadata object created based on connectivity specified using qubit indices only.""" - qubits = tuple(NamedQubit.range(1, qubit_count + 1, prefix=cls.QUBIT_NAME_PREFIX)) + qubits = tuple(NamedQid.range(1, qubit_count + 1, prefix=cls.QUBIT_NAME_PREFIX, dimension=2)) connectivity = tuple( - tuple(NamedQubit(f'{cls.QUBIT_NAME_PREFIX}{qb}') for qb in edge) for edge in connectivity_indices + tuple(NamedQid(f'{cls.QUBIT_NAME_PREFIX}{qb}', dimension=2) for qb in edge) for edge in connectivity_indices ) if gateset: - qb_list: list[tuple[cirq.Qid, ...]] = [(qb,) for qb in qubits] - operations = { - gate: ( - qb_list - if gate in (ops.PhasedXPowGate, ops.XPowGate, ops.YPowGate, ops.MeasurementGate) - else list(tuple(edge) for edge in connectivity) - ) - for gate in gateset - } - else: - operations = None - return cls(qubits, connectivity, operations=operations, gateset=cirq.Gateset(*gateset) if gateset else None) + sqg_list: list[type[Gate]] = [ + g for g in gateset if g in [ops.PhasedXPowGate, ops.XPowGate, ops.YPowGate, ops.MeasurementGate] + ] + operations: dict[type[cirq.Gate], list[tuple[cirq.NamedQid, ...]]] = {} + if ops.CZPowGate in gateset: + operations[ops.CZPowGate] = list(tuple(edge) for edge in connectivity) + if ops.ISwapPowGate in gateset: + operations[ops.ISwapPowGate] = list(tuple(edge) for edge in connectivity) + operations.update({gate: [(qb,) for qb in qubits] for gate in sqg_list}) + return cls(qubits, connectivity, operations=operations, gateset=cirq.Gateset(*gateset)) + return cls(qubits, connectivity) @property def gateset(self) -> cirq.Gateset: diff --git a/src/iqm/cirq_iqm/devices/valkmusa.py b/src/iqm/cirq_iqm/devices/valkmusa.py index fba491c2..5a32f006 100644 --- a/src/iqm/cirq_iqm/devices/valkmusa.py +++ b/src/iqm/cirq_iqm/devices/valkmusa.py @@ -17,10 +17,9 @@ from math import pi as PI from typing import Optional -import cirq from cirq import ops -from .iqm_device import IQMDevice, IQMDeviceMetadata +from iqm.cirq_iqm.devices import IQMDevice, IQMDeviceMetadata PI_2 = PI / 2 diff --git a/src/iqm/cirq_iqm/iqm_sampler.py b/src/iqm/cirq_iqm/iqm_sampler.py index a88e1ec9..a5755eb3 100644 --- a/src/iqm/cirq_iqm/iqm_sampler.py +++ b/src/iqm/cirq_iqm/iqm_sampler.py @@ -29,7 +29,7 @@ from iqm.cirq_iqm.devices.iqm_device import IQMDevice, IQMDeviceMetadata from iqm.cirq_iqm.serialize import serialize_circuit -from iqm.iqm_client import IQMClient, JobAbortionError, RunRequest, CircuitCompilationOptions +from iqm.iqm_client import CircuitCompilationOptions, IQMClient, JobAbortionError, RunRequest class IQMSampler(cirq.work.Sampler): diff --git a/src/iqm/cirq_iqm/serialize.py b/src/iqm/cirq_iqm/serialize.py index be03fb73..72b6c180 100644 --- a/src/iqm/cirq_iqm/serialize.py +++ b/src/iqm/cirq_iqm/serialize.py @@ -45,7 +45,7 @@ def deserialize_circuit(circuit: iqm_client.Circuit) -> Circuit: """ return Circuit( map( - lambda instr: instruction_to_operation(instr), + instruction_to_operation, circuit.instructions, ) ) diff --git a/src/iqm/cirq_iqm/transpiler.py b/src/iqm/cirq_iqm/transpiler.py index 317dcf43..25df6dfb 100644 --- a/src/iqm/cirq_iqm/transpiler.py +++ b/src/iqm/cirq_iqm/transpiler.py @@ -14,17 +14,20 @@ """Helper functions for IQM specific transpilation needs.""" from __future__ import annotations -from typing import Optional +from typing import TYPE_CHECKING, Optional from cirq import Circuit from iqm.cirq_iqm.serialize import deserialize_circuit, serialize_circuit from iqm.iqm_client import ExistingMoveHandlingOptions, transpile_insert_moves +if TYPE_CHECKING: + from iqm.cirq_iqm.devices import IQMDevice + def transpile_insert_moves_into_circuit( cirq_circuit: Circuit, - device: "IQMDevice", + device: IQMDevice, existing_moves: Optional[ExistingMoveHandlingOptions] = None, qubit_mapping: Optional[dict[str, str]] = None, ) -> Circuit: diff --git a/tests/test_iqm_device.py b/tests/test_iqm_device.py index 60ee8199..f1bf145d 100644 --- a/tests/test_iqm_device.py +++ b/tests/test_iqm_device.py @@ -108,7 +108,7 @@ def test_qubit_connectivity(device: IQMDevice, request): edge in device.supported_operations[ops.CZPowGate] or tuple(reversed(edge)) in device.supported_operations[ops.CZPowGate] ): - assert device.check_qubit_connectivity(gate) is None + device.check_qubit_connectivity(gate) # This should not raise an error else: with pytest.raises(ValueError): device.check_qubit_connectivity(gate) diff --git a/tests/test_iqm_operation_mapping.py b/tests/test_iqm_operation_mapping.py index d2abadd5..1e8c935f 100644 --- a/tests/test_iqm_operation_mapping.py +++ b/tests/test_iqm_operation_mapping.py @@ -13,12 +13,12 @@ # limitations under the License. import cirq from cirq import CZPowGate, GateOperation, MeasurementGate, PhasedXPowGate, XPowGate, YPowGate, ZPowGate -import pytest from mockito import mock +import pytest -from iqm.cirq_iqm.iqm_operation_mapping import OperationNotSupportedError, map_operation, instruction_to_operation -from iqm.iqm_client import Instruction from iqm.cirq_iqm.iqm_gates import IQMMoveGate +from iqm.cirq_iqm.iqm_operation_mapping import OperationNotSupportedError, instruction_to_operation, map_operation +from iqm.iqm_client import Instruction @pytest.fixture() diff --git a/tests/test_iqm_sampler.py b/tests/test_iqm_sampler.py index a2b4e558..ce264f6f 100644 --- a/tests/test_iqm_sampler.py +++ b/tests/test_iqm_sampler.py @@ -27,10 +27,10 @@ from iqm.cirq_iqm.iqm_sampler import IQMResult, IQMSampler, ResultMetadata from iqm.iqm_client import ( Circuit, + CircuitCompilationOptions, HeraldingMode, Instruction, IQMClient, - CircuitCompilationOptions, JobAbortionError, Metadata, RunRequest, @@ -195,7 +195,7 @@ def test_run_sweep_has_heralding_mode_none_by_default( sampler = IQMSampler(base_url, Adonis()) run_result = RunResult(status=Status.READY, measurements=[{'some stuff': [[0], [1]]}], metadata=iqm_metadata) kwargs = submit_circuits_default_kwargs - assert sampler._compiler_options.heralding_mode is None + assert sampler._compiler_options.heralding_mode is HeraldingMode.NONE when(client).submit_circuits(ANY, options=ANY, **kwargs).thenReturn(job_id) when(client).wait_for_results(job_id).thenReturn(run_result)