From b57d42b203d009ec2679008dca66be9448c3011f Mon Sep 17 00:00:00 2001 From: Sebastian Wagner Date: Thu, 21 Mar 2024 15:25:47 +0100 Subject: [PATCH] fix: AWS simulator transpilation not using correct gateset --- environment.yml | 5 ++-- planqk/qiskit/backend.py | 9 +++---- planqk/qiskit/providers/adapter.py | 2 +- planqk/qiskit/providers/aws/aws_backend.py | 14 ++++++++--- planqk/qiskit/providers/aws_adapter.py | 16 ++++++++----- planqk/qiskit/providers/azure_adapter.py | 2 +- planqk/qiskit/providers/ibm/ibm_adapter.py | 2 +- .../qiskit/providers/job_input_converter.py | 24 +++++++++++-------- planqk/qiskit/providers/qryd/qryd_adapter.py | 2 +- requirements.txt | 4 ++-- 10 files changed, 49 insertions(+), 31 deletions(-) diff --git a/environment.yml b/environment.yml index 1960b15..44f51eb 100644 --- a/environment.yml +++ b/environment.yml @@ -22,9 +22,10 @@ dependencies: - requests # aws braket # 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 + #- git+https://github.com/qiskit-community/qiskit-braket-provider.git@main + - qiskit-braket-provider==0.2.0 # azure quantum - azure-quantum[qiskit]==0.28.* - - qiskit-ionq==0.4.4 + - qiskit-ionq==0.5.0 # dwave - dwave-ocean-sdk==6.4.1 diff --git a/planqk/qiskit/backend.py b/planqk/qiskit/backend.py index 6288d6f..82bd3a9 100644 --- a/planqk/qiskit/backend.py +++ b/planqk/qiskit/backend.py @@ -8,7 +8,6 @@ from qiskit.providers.models import QasmBackendConfiguration, GateConfig from qiskit.transpiler import Target -from planqk.qiskit.providers.job_input_converter import convert_to_backend_input, convert_to_backend_params from .client.backend_dtos import ConfigurationDto, TYPE, BackendDto, ConnectivityDto, PROVIDER, HARDWARE_PROVIDER from .client.job_dtos import JobDto from .job import PlanqkJob @@ -80,14 +79,14 @@ def _planqk_backend_to_target(self) -> Target: 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) + gate = adapter.op_to_instruction(name, is_simulator) if gate is None: continue if gate.num_qubits == 1: target.add_instruction(gate, single_qubit_props) - elif gate.num_qubits == 2: + elif gate.num_qubits > 1: target.add_instruction(gate, multi_qubit_props) elif gate.num_qubits == 0 and single_qubit_props == {None: None}: # For gates without qubit number qargs can not be determined @@ -168,6 +167,8 @@ def run(self, circuit, **kwargs) -> PlanqkJob: Returns: PlanqkJob: The job instance for the circuit that was run. """ + from planqk.qiskit.providers.job_input_converter import convert_to_backend_input, convert_to_backend_params + self._validate_provider_for_backend() if isinstance(circuit, (list, tuple)): @@ -187,7 +188,7 @@ def run(self, circuit, **kwargs) -> PlanqkJob: options[field] = kwargs[field] supported_input_formats = self._backend_info.configuration.supported_input_formats - backend_input = convert_to_backend_input(supported_input_formats, circuit, options) + backend_input = convert_to_backend_input(supported_input_formats, circuit, self, options) input_params = convert_to_backend_params(self._backend_info.provider, circuit, options) job_request = JobDto(backend_id=self._backend_info.id, diff --git a/planqk/qiskit/providers/adapter.py b/planqk/qiskit/providers/adapter.py index ca229ef..012ca5d 100644 --- a/planqk/qiskit/providers/adapter.py +++ b/planqk/qiskit/providers/adapter.py @@ -6,7 +6,7 @@ class ProviderAdapter: - def op_to_instruction(self, operation: str) -> Optional[QiskitInstruction]: + def op_to_instruction(self, operation: str, is_simulator: bool = False) -> Optional[QiskitInstruction]: pass def single_qubit_gate_props(self, qubits: List[QubitDto], is_simulator: bool = False): diff --git a/planqk/qiskit/providers/aws/aws_backend.py b/planqk/qiskit/providers/aws/aws_backend.py index 82b284d..4da1b1b 100644 --- a/planqk/qiskit/providers/aws/aws_backend.py +++ b/planqk/qiskit/providers/aws/aws_backend.py @@ -9,6 +9,14 @@ def __init__(self, **kwargs): @classmethod def _default_options(cls): - return OptionsV2( - verbatim=False - ) + return OptionsV2() + + +class PlanqkAwsSv1Backend(PlanqkAwsBackend): + + def __init__(self, **kwargs): + super().__init__(**kwargs) + + @classmethod + def _default_options(cls): + return OptionsV2() diff --git a/planqk/qiskit/providers/aws_adapter.py b/planqk/qiskit/providers/aws_adapter.py index 68b0a7f..14767c2 100644 --- a/planqk/qiskit/providers/aws_adapter.py +++ b/planqk/qiskit/providers/aws_adapter.py @@ -1,25 +1,29 @@ from typing import Optional, List -from qiskit.circuit import Instruction as QiskitInstruction, Gate +from qiskit.circuit import Instruction as QiskitInstruction +from qiskit_braket_provider.providers.adapter import _GATE_NAME_TO_QISKIT_GATE import planqk.qiskit.providers.adapter as adapter from planqk.qiskit.client.backend_dtos import QubitDto, ConnectivityDto -from planqk.qiskit.providers.aws_converters import qiskit_gate_name_to_braket_gate_mapping class AwsAdapter(adapter.ProviderAdapter): """Adapter for AWS Braket backend.""" - def op_to_instruction(self, operation: str) -> Optional[QiskitInstruction]: + def op_to_instruction(self, operation: str, is_simulator: bool = False) -> Optional[QiskitInstruction]: operation = operation.lower() - gate = qiskit_gate_name_to_braket_gate_mapping.get(operation, None) or Gate(operation, 0, []) - # Braket only supports 1 and 2 qubit gates - return gate if gate.num_qubits < 3 else None + gate = _GATE_NAME_TO_QISKIT_GATE.get(operation, 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 def single_qubit_gate_props(self, qubits: List[QubitDto], is_simulator: bool = False): + if is_simulator: + return {None: None} return {(int(qubit.id),): None for qubit in qubits} def multi_qubit_gate_props(self, qubits: List[QubitDto], connectivity: ConnectivityDto, is_simulator: bool = False): + if is_simulator: + return {None: None} if connectivity.fully_connected: return {(int(qubit1.id), int(qubit2.id)): None for qubit1 in qubits for qubit2 in qubits if qubit1.id != qubit2.id} diff --git a/planqk/qiskit/providers/azure_adapter.py b/planqk/qiskit/providers/azure_adapter.py index 260a1b7..61e5341 100644 --- a/planqk/qiskit/providers/azure_adapter.py +++ b/planqk/qiskit/providers/azure_adapter.py @@ -7,7 +7,7 @@ class AzureAdapter(ProviderAdapter): - def op_to_instruction(self, operation: str) -> Optional[QiskitInstruction]: + def op_to_instruction(self, operation: str, is_simulator: bool = False) -> Optional[QiskitInstruction]: operation = operation.lower() return Gate(operation, 0, []) diff --git a/planqk/qiskit/providers/ibm/ibm_adapter.py b/planqk/qiskit/providers/ibm/ibm_adapter.py index fac8fda..98da279 100644 --- a/planqk/qiskit/providers/ibm/ibm_adapter.py +++ b/planqk/qiskit/providers/ibm/ibm_adapter.py @@ -20,7 +20,7 @@ class IbmAdapter(ProviderAdapter): - def op_to_instruction(self, operation: str) -> Optional[QiskitInstruction]: + 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, []) diff --git a/planqk/qiskit/providers/job_input_converter.py b/planqk/qiskit/providers/job_input_converter.py index e7f4515..250b6aa 100644 --- a/planqk/qiskit/providers/job_input_converter.py +++ b/planqk/qiskit/providers/job_input_converter.py @@ -3,23 +3,27 @@ from braket.circuits import Circuit from braket.circuits.circuit_helpers import validate_circuit_and_shots -from planqk.qiskit.client.backend_dtos import PROVIDER -from planqk.qiskit.client.job_dtos import INPUT_FORMAT -from planqk.qiskit.providers.aws_converters import transform_to_qasm_3_program -from planqk.qiskit.providers.qryd.qryd_converters import convert_to_wire_format, create_qoqu_input_params from qiskit import QuantumCircuit from qiskit.providers import Options from qiskit_braket_provider.providers.adapter import to_braket from qiskit_ibm_runtime import RuntimeEncoder from qiskit_ionq.helpers import qiskit_circ_to_ionq_circ +from planqk.qiskit.backend import PlanqkBackend +from planqk.qiskit.client.backend_dtos import PROVIDER +from planqk.qiskit.client.job_dtos import INPUT_FORMAT +from planqk.qiskit.providers.aws_converters import transform_to_qasm_3_program +from planqk.qiskit.providers.qryd.qryd_converters import convert_to_wire_format, create_qoqu_input_params + -def _convert_to_open_qasm_3(circuit: QuantumCircuit, options: Options): +def _convert_to_open_qasm_3(circuit: QuantumCircuit, backend: PlanqkBackend, options: Options): shots = options.get("shots", 1) inputs = options.get("inputs", {}) verbatim = options.get("verbatim", False) - braket_circuit = to_braket(circuit, verbatim=verbatim) + basis_gates = backend.operation_names if not verbatim else None + braket_circuit = to_braket(circuit, basis_gates, verbatim=verbatim) + validate_circuit_and_shots(braket_circuit, shots) return transform_to_qasm_3_program(braket_circuit, False, inputs) @@ -35,14 +39,14 @@ def _convert_to_ionq(circuit: QuantumCircuit, options: Options): } -def _convert_to_qiskit_primitive(circuit: QuantumCircuit, options: Options): +def _convert_to_qiskit_primitive(circuit: QuantumCircuit, backend: PlanqkBackend, options: Options): # Transforms circuit to base64 encoded byte stream input_json_str = json.dumps(circuit, cls=RuntimeEncoder) # Transform back to json but with the base64 encoded byte stream return json.loads(input_json_str) -def _convert_to_qoqo_circuit(circuit: QuantumCircuit, options: Options): +def _convert_to_qoqo_circuit(circuit: QuantumCircuit, backend: PlanqkBackend, options: Options): return convert_to_wire_format(circuit=circuit, options=options) @@ -75,12 +79,12 @@ class UnsupportedFormatException(Exception): pass -def convert_to_backend_input(supported_input_formats: List[INPUT_FORMAT], circuit, options=None) \ +def convert_to_backend_input(supported_input_formats: List[INPUT_FORMAT], circuit, backend, options=None) \ -> Tuple[INPUT_FORMAT, dict]: for input_format in supported_input_formats: convert_circuit = input_format_converter_factory.get(input_format) if convert_circuit: - return input_format, convert_circuit(circuit=circuit, options=options) + return input_format, convert_circuit(circuit=circuit, backend=backend, options=options) raise UnsupportedFormatException("Could not convert input to any of the supported inputs formats of the actual") diff --git a/planqk/qiskit/providers/qryd/qryd_adapter.py b/planqk/qiskit/providers/qryd/qryd_adapter.py index 663ab41..c049288 100644 --- a/planqk/qiskit/providers/qryd/qryd_adapter.py +++ b/planqk/qiskit/providers/qryd/qryd_adapter.py @@ -34,7 +34,7 @@ class QrydAdapter(adapter.ProviderAdapter): - def op_to_instruction(self, operation: str) -> Optional[QiskitInstruction]: + 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, []) diff --git a/requirements.txt b/requirements.txt index 8bb3458..96b8930 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,10 +1,10 @@ requests>=2.19 pydantic==1.9.2 qiskit-ibm-runtime==0.17.0 -qiskit-ionq==0.4.7 +qiskit-ionq==0.5.0 qiskit-ibm-provider==0.7.2 dwave-ocean-sdk==6.4.1 -qiskit-braket-provider==0.1.1 +qiskit-braket-provider==0.2.0 boto3==1.33.13 botocore==1.33.13 qiskit==0.44.1