From 7b7e3288d425f328951a9a64fc87b486b4879fd6 Mon Sep 17 00:00:00 2001 From: Sebastian Wagner Date: Thu, 28 Mar 2024 17:45:00 +0100 Subject: [PATCH] feat: Migration of providers AWS, Azure, T-Systems and IBM to Qiskit 1.0 --- environment.yml | 18 ++++++------ planqk/qiskit/backend.py | 28 +++++++++++-------- planqk/qiskit/job.py | 5 ++-- planqk/qiskit/providers/adapter.py | 15 +++++++++- planqk/qiskit/providers/aws_adapter.py | 8 +++--- planqk/qiskit/providers/azure_adapter.py | 9 +++--- planqk/qiskit/providers/ibm/ibm_adapter.py | 25 ++++++++++++++--- planqk/qiskit/providers/ibm/ibm_backend.py | 3 +- .../qiskit/providers/job_input_converter.py | 2 +- planqk/qiskit/providers/qryd/qryd_adapter.py | 13 ++++----- requirements.txt | 12 ++++---- setup.py | 2 +- 12 files changed, 86 insertions(+), 54 deletions(-) diff --git a/environment.yml b/environment.yml index 44f51eb..b4af968 100644 --- a/environment.yml +++ b/environment.yml @@ -5,12 +5,12 @@ channels: dependencies: - python=3.9 - pip>=21.1.3 - - pydantic=1.9.0 + - pydantic==2.5.0 - pip: ## dev dependencies - - qiskit==0.44.1 - - qiskit-ibm-runtime==0.17.0 - - qiskit-ibm-provider==0.7.2 + - qiskit==1.0.2 + - qiskit-ibm-runtime==0.21.2 + - qiskit-ibm-provider==0.10.0 - setuptools - pytest - pytest-cov @@ -21,11 +21,13 @@ dependencies: ## prod dependencies - requests # aws braket + - amazon-braket-default-simulator==1.21.0 + - amazon-braket-schemas==1.21.0 # we need the latest dev version as this fixes the issue with the Rigetti's qubit ids - #- git+https://github.com/qiskit-community/qiskit-braket-provider.git@main - - qiskit-braket-provider==0.2.0 + - git+https://github.com/qiskit-community/qiskit-braket-provider.git@main + #- qiskit-braket-provider==0.2.0 # azure quantum - - azure-quantum[qiskit]==0.28.* + - azure-quantum[qiskit]==2.0.0.dev1 - qiskit-ionq==0.5.0 # dwave - - dwave-ocean-sdk==6.4.1 + - dwave-ocean-sdk==6.9.0 diff --git a/planqk/qiskit/backend.py b/planqk/qiskit/backend.py index 82bd3a9..22a5211 100644 --- a/planqk/qiskit/backend.py +++ b/planqk/qiskit/backend.py @@ -2,7 +2,6 @@ from abc import ABC from copy import copy -from qiskit.circuit import Delay, Parameter from qiskit.circuit import Measure from qiskit.providers import BackendV2, Provider from qiskit.providers.models import QasmBackendConfiguration, GateConfig @@ -57,6 +56,7 @@ def __init__( # pylint: disable=too-many-arguments self._backend_info = backend_info self._target = self._planqk_backend_to_target() self._configuration = self._planqk_backend_dto_to_configuration() + self._instance = None def _planqk_backend_to_target(self) -> Target: """Converts properties of a PlanQK actual into Qiskit Target object. @@ -77,26 +77,31 @@ def _planqk_backend_to_target(self) -> Target: single_qubit_props = adapter.single_qubit_gate_props(qubits, is_simulator) multi_qubit_props = adapter.multi_qubit_gate_props(qubits, connectivity, is_simulator) - for gate in configuration.gates: - name = gate.name - gate = adapter.op_to_instruction(name, is_simulator) + gates_names = {gate.name.lower() for gate in configuration.gates} + for gate in gates_names: + gate = adapter.to_gate(gate, is_simulator) if gate is None: continue if gate.num_qubits == 1: - target.add_instruction(gate, single_qubit_props) + target.add_instruction(instruction=gate, properties=single_qubit_props) elif gate.num_qubits > 1: - target.add_instruction(gate, multi_qubit_props) + target.add_instruction(instruction=gate, properties=multi_qubit_props) elif gate.num_qubits == 0 and single_qubit_props == {None: None}: # For gates without qubit number qargs can not be determined - target.add_instruction(gate, {None: None}) + target.add_instruction(instruction=gate, properties={None: None}) target.add_instruction(Measure(), single_qubit_props) - if "delay" in configuration.instructions and "delay" not in target: - # gate_props = {None: None} if is_simulator else single_qubit_gate_props() - target.add_instruction(Delay(Parameter("t")), single_qubit_props) + non_gate_instructions = set(configuration.instructions).difference(gates_names).difference({'measure'}) + for non_gate_instruction_name in non_gate_instructions: + instruction = adapter.to_non_gate_instruction(non_gate_instruction_name, is_simulator) + if instruction is not None: + if instruction.has_single_gate_props: + target.add_instruction(instruction, single_qubit_props) + else: + target.add_instruction(instruction=instruction, name=non_gate_instruction_name) return target @@ -129,7 +134,8 @@ def _planqk_backend_dto_to_configuration(self) -> QasmBackendConfiguration: def _get_gate_config_from_target(self, name) -> GateConfig: operations = [operation for operation in self._target.operations - if operation.name.casefold() == name.casefold()] + if isinstance(operation.name, str) # Filters out the IBM conditional instructions having no name + and operation.name.casefold() == name.casefold()] if len(operations) == 1: operation = operations[0] return GateConfig( diff --git a/planqk/qiskit/job.py b/planqk/qiskit/job.py index ff56efd..b0ec6ed 100644 --- a/planqk/qiskit/job.py +++ b/planqk/qiskit/job.py @@ -1,13 +1,12 @@ from typing import Optional +from planqk.qiskit.client.client import _PlanqkClient +from planqk.qiskit.client.job_dtos import JobDto from qiskit.providers import JobV1, JobStatus, Backend from qiskit.qobj import QobjExperimentHeader from qiskit.result import Result from qiskit.result.models import ExperimentResult, ExperimentResultData -from planqk.qiskit.client.client import _PlanqkClient -from planqk.qiskit.client.job_dtos import JobDto - JobStatusMap = { "CREATED": JobStatus.INITIALIZING, "PENDING": JobStatus.QUEUED, diff --git a/planqk/qiskit/providers/adapter.py b/planqk/qiskit/providers/adapter.py index 012ca5d..c880a09 100644 --- a/planqk/qiskit/providers/adapter.py +++ b/planqk/qiskit/providers/adapter.py @@ -1,14 +1,27 @@ from typing import Optional, List +from qiskit.circuit import Gate, Delay, Parameter, Measure from qiskit.circuit import Instruction as QiskitInstruction from planqk.qiskit.client.backend_dtos import PROVIDER, QubitDto, ConnectivityDto class ProviderAdapter: - def op_to_instruction(self, operation: str, is_simulator: bool = False) -> Optional[QiskitInstruction]: + non_gate_instr_mapping = { + "delay": Delay(Parameter("t")), + "measure": Measure(), + } + + def to_gate(self, name: str, is_simulator: bool = False) -> Optional[Gate]: pass + def to_non_gate_instruction(self, name: str, is_simulator: bool = False) -> Optional[QiskitInstruction]: + instr = self.non_gate_instr_mapping.get(name, None) + if instr is not None: + instr.has_single_gate_props = True + return instr + return None + def single_qubit_gate_props(self, qubits: List[QubitDto], is_simulator: bool = False): pass diff --git a/planqk/qiskit/providers/aws_adapter.py b/planqk/qiskit/providers/aws_adapter.py index 14767c2..745af8f 100644 --- a/planqk/qiskit/providers/aws_adapter.py +++ b/planqk/qiskit/providers/aws_adapter.py @@ -1,6 +1,6 @@ from typing import Optional, List -from qiskit.circuit import Instruction as QiskitInstruction +from qiskit.circuit import Gate from qiskit_braket_provider.providers.adapter import _GATE_NAME_TO_QISKIT_GATE import planqk.qiskit.providers.adapter as adapter @@ -10,9 +10,9 @@ class AwsAdapter(adapter.ProviderAdapter): """Adapter for AWS Braket backend.""" - def op_to_instruction(self, operation: str, is_simulator: bool = False) -> Optional[QiskitInstruction]: - operation = operation.lower() - gate = _GATE_NAME_TO_QISKIT_GATE.get(operation, None) + def to_gate(self, name: str, is_simulator: bool = False) -> Optional[Gate]: + name = name.lower() + gate = _GATE_NAME_TO_QISKIT_GATE.get(name, None) # Braket quantum backends only support 1 and 2 qubit gates return gate if (gate and gate.num_qubits < 3) or is_simulator else None diff --git a/planqk/qiskit/providers/azure_adapter.py b/planqk/qiskit/providers/azure_adapter.py index 61e5341..bc06e70 100644 --- a/planqk/qiskit/providers/azure_adapter.py +++ b/planqk/qiskit/providers/azure_adapter.py @@ -1,15 +1,14 @@ from typing import Optional, List -from qiskit.circuit import Instruction as QiskitInstruction, Gate - from planqk.qiskit.client.backend_dtos import QubitDto, ConnectivityDto from planqk.qiskit.providers.adapter import ProviderAdapter +from qiskit.circuit import Gate class AzureAdapter(ProviderAdapter): - def op_to_instruction(self, operation: str, is_simulator: bool = False) -> Optional[QiskitInstruction]: - operation = operation.lower() - return Gate(operation, 0, []) + def to_gate(self, name: str, is_simulator: bool = False) -> Optional[Gate]: + name = name.lower() + return Gate(name, 0, []) def single_qubit_gate_props(self, qubits: List[QubitDto], is_simulator: bool = False): return {None: None} diff --git a/planqk/qiskit/providers/ibm/ibm_adapter.py b/planqk/qiskit/providers/ibm/ibm_adapter.py index 98da279..9c4f874 100644 --- a/planqk/qiskit/providers/ibm/ibm_adapter.py +++ b/planqk/qiskit/providers/ibm/ibm_adapter.py @@ -1,6 +1,6 @@ from typing import Optional, List -from qiskit.circuit import Instruction as QiskitInstruction, Gate +from qiskit.circuit import Gate, IfElseOp, WhileLoopOp, ForLoopOp, SwitchCaseOp, Instruction from qiskit.circuit import Parameter, Reset from qiskit.circuit.library import IGate, SXGate, XGate, CXGate, RZGate, ECRGate, CZGate @@ -18,11 +18,28 @@ "cz": CZGate(), } +qiskit_control_flow_mapping = { + "if_else": IfElseOp, + "while_loop": WhileLoopOp, + "for_loop": ForLoopOp, + "switch_case": SwitchCaseOp, +} + class IbmAdapter(ProviderAdapter): - def op_to_instruction(self, operation: str, is_simulator: bool = False) -> Optional[QiskitInstruction]: - operation = operation.lower() - return ibm_name_mapping.get(operation, None) or Gate(operation, 0, []) + + def to_gate(self, name: str, is_simulator: bool = False) -> Optional[Gate]: + name = name.lower() + + return ibm_name_mapping.get(name, None) or Gate(name, 0, []) + + def to_non_gate_instruction(self, name: str, is_simulator: bool = False) -> Optional[Instruction]: + if name in qiskit_control_flow_mapping: + instr = qiskit_control_flow_mapping[name] + instr.has_single_gate_props = False + return instr + + return super().to_non_gate_instruction(name, is_simulator) def single_qubit_gate_props(self, qubits: List[QubitDto], is_simulator: bool = False): return {None: None} if is_simulator else {(int(qubit.id),): None for qubit in qubits} diff --git a/planqk/qiskit/providers/ibm/ibm_backend.py b/planqk/qiskit/providers/ibm/ibm_backend.py index d4ae964..7874bfb 100644 --- a/planqk/qiskit/providers/ibm/ibm_backend.py +++ b/planqk/qiskit/providers/ibm/ibm_backend.py @@ -1,7 +1,6 @@ -from qiskit.qobj.utils import MeasLevel, MeasReturnType - from planqk.qiskit import PlanqkBackend from planqk.qiskit.options import OptionsV2 +from qiskit.qobj.utils import MeasLevel, MeasReturnType class PlanqkIbmBackend(PlanqkBackend): diff --git a/planqk/qiskit/providers/job_input_converter.py b/planqk/qiskit/providers/job_input_converter.py index 1e509bf..5bba95b 100644 --- a/planqk/qiskit/providers/job_input_converter.py +++ b/planqk/qiskit/providers/job_input_converter.py @@ -78,7 +78,7 @@ class UnsupportedFormatException(Exception): pass -def convert_to_backend_input(supported_input_formats: List[INPUT_FORMAT], circuit, backend, options=None) \ +def convert_to_backend_input(supported_input_formats: List[INPUT_FORMAT], circuit, backend=None, options=None) \ -> Tuple[INPUT_FORMAT, dict]: for input_format in supported_input_formats: convert_circuit = input_format_converter_factory.get(input_format) diff --git a/planqk/qiskit/providers/qryd/qryd_adapter.py b/planqk/qiskit/providers/qryd/qryd_adapter.py index c049288..cb4500e 100644 --- a/planqk/qiskit/providers/qryd/qryd_adapter.py +++ b/planqk/qiskit/providers/qryd/qryd_adapter.py @@ -1,13 +1,12 @@ from typing import Optional, List -from qiskit.circuit import Instruction as QiskitInstruction, Parameter, Gate -from qiskit.circuit.library import PhaseGate, RGate, RXGate, RYGate, HGate, RZGate, UGate, XGate, YGate, ZGate, SXGate, \ - SXdgGate, CXGate, CYGate, CZGate, CPhaseGate, SwapGate, iSwapGate - import planqk.qiskit.providers.adapter as adapter from planqk.qiskit.client.backend_dtos import QubitDto, ConnectivityDto from planqk.qiskit.providers.qryd.pcp_gate import PCPGate from planqk.qiskit.providers.qryd.pcz_gate import PCZGate +from qiskit.circuit import Parameter, Gate +from qiskit.circuit.library import PhaseGate, RGate, RXGate, RYGate, HGate, RZGate, UGate, XGate, YGate, ZGate, SXGate, \ + SXdgGate, CXGate, CYGate, CZGate, CPhaseGate, SwapGate, iSwapGate qryd_gate_name_mapping = { "p": PhaseGate(Parameter("lambda")), @@ -34,9 +33,9 @@ class QrydAdapter(adapter.ProviderAdapter): - def op_to_instruction(self, operation: str, is_simulator: bool = False) -> Optional[QiskitInstruction]: - operation = operation.lower() - return qryd_gate_name_mapping.get(operation, None) or Gate(operation, 0, []) + def to_gate(self, name: str, is_simulator: bool = False) -> Optional[Gate]: + name = name.lower() + return qryd_gate_name_mapping.get(name, None) or Gate(name, 0, []) def single_qubit_gate_props(self, qubits: List[QubitDto], is_simulator: bool = False): return {(int(qubit.id),): None for qubit in qubits} diff --git a/requirements.txt b/requirements.txt index 96b8930..1b3cc6d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,10 +1,8 @@ requests>=2.19 -pydantic==1.9.2 -qiskit-ibm-runtime==0.17.0 +pydantic==2.5.0 +qiskit-ibm-runtime==0.21.2 qiskit-ionq==0.5.0 -qiskit-ibm-provider==0.7.2 -dwave-ocean-sdk==6.4.1 +qiskit-ibm-provider==0.10.0 +dwave-ocean-sdk==6.9.0 qiskit-braket-provider==0.2.0 -boto3==1.33.13 -botocore==1.33.13 -qiskit==0.44.1 +qiskit==1.0.2 diff --git a/setup.py b/setup.py index 0a1ce95..bf4f76e 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ setup( name='planqk-quantum', - version="1.34.1", + version="2.0.0rc1", author='Anaqor AG', author_email='info@anaqor.io', url='https://github.com/planqk/planqk-quantum',