Skip to content
This repository has been archived by the owner on Jun 17, 2024. It is now read-only.

Commit

Permalink
fix: moved job input handling to backends
Browse files Browse the repository at this point in the history
  • Loading branch information
Sebastian Wagner committed Apr 16, 2024
1 parent 685e87e commit 52fcd0b
Show file tree
Hide file tree
Showing 9 changed files with 139 additions and 134 deletions.
28 changes: 19 additions & 9 deletions planqk/qiskit/backend.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import datetime
from abc import ABC, abstractmethod
from copy import copy
from typing import Optional
from typing import Optional, Tuple

from qiskit import QuantumCircuit
from qiskit.circuit import Instruction as QiskitInstruction, Delay, Parameter
from qiskit.circuit import Measure
from qiskit.providers import BackendV2, Provider
from qiskit.providers.models import QasmBackendConfiguration, GateConfig
from qiskit.transpiler import Target

from .client.backend_dtos import ConfigurationDto, TYPE, BackendDto, PROVIDER
from .client.job_dtos import JobDto
from .client.job_dtos import JobDto, INPUT_FORMAT
from .job import PlanqkJob
from .options import OptionsV2

Expand Down Expand Up @@ -195,6 +196,17 @@ def max_shots(self):
def _default_options(cls):
return OptionsV2()

@abstractmethod
def convert_to_job_input(self, circuit: QuantumCircuit, options=None) -> Tuple[INPUT_FORMAT, dict]:
pass

def convert_to_job_params(self, circuit: QuantumCircuit = None, options=None) -> dict:
return {}

@abstractmethod
def get_job_input_format(self) -> INPUT_FORMAT:
pass

def run(self, circuit, **kwargs) -> PlanqkJob:
"""Run a circuit on the backend as job.
Expand All @@ -204,7 +216,6 @@ 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

if isinstance(circuit, (list, tuple)):
if len(circuit) > 1:
Expand All @@ -222,15 +233,14 @@ def run(self, circuit, **kwargs) -> PlanqkJob:
if field in options.data:
options[field] = kwargs[field]

supported_input_formats = self.backend_info.configuration.supported_input_formats

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_input_format = self.get_job_input_format()
job_input = self.convert_to_job_input(circuit, options)
input_params = self.convert_to_job_params(circuit, options)

job_request = JobDto(backend_id=self.backend_info.id,
provider=self.backend_info.provider.name,
input_format=backend_input[0],
input=backend_input[1],
input_format=job_input_format,
input=job_input,
shots=shots,
input_params=input_params)

Expand Down
62 changes: 60 additions & 2 deletions planqk/qiskit/providers/aws/aws_backend.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
from typing import Optional
from typing import Optional, Dict

from braket.circuits import Circuit, Instruction
from braket.circuits.circuit_helpers import validate_circuit_and_shots
from braket.circuits.compiler_directives import StartVerbatimBox
from braket.circuits.gates import PulseGate
from braket.circuits.serialization import QubitReferenceType, OpenQASMSerializationProperties, IRType
from braket.ir.openqasm import Program as OpenQASMProgram
from qiskit import QuantumCircuit
from qiskit.circuit import Gate
from qiskit_braket_provider.providers.adapter import _GATE_NAME_TO_QISKIT_GATE
from qiskit.providers import Options
from qiskit_braket_provider.providers.adapter import _GATE_NAME_TO_QISKIT_GATE, to_braket

from planqk.qiskit import PlanqkBackend
from planqk.qiskit.client.job_dtos import INPUT_FORMAT
from planqk.qiskit.options import OptionsV2


Expand Down Expand Up @@ -39,3 +48,52 @@ def get_multi_qubit_gate_properties(self) -> dict:
return {(int(qubit), int(connected_qubit)): None
for qubit, connections in connectivity.graph.items()
for connected_qubit in connections}

def convert_to_job_input(self, circuit: QuantumCircuit, options: Options = None):
shots = options.get("shots", 1)
inputs = options.get("inputs", {})
verbatim = options.get("verbatim", False)

basis_gates = self.operation_names if not verbatim else None
braket_circuit = to_braket(circuit, basis_gates, verbatim=verbatim)

validate_circuit_and_shots(braket_circuit, shots)

return self._transform_braket_to_qasm_3_program(braket_circuit, False, inputs)

def get_job_input_format(self) -> INPUT_FORMAT:
return INPUT_FORMAT.BRAKET_OPEN_QASM_V3

def convert_to_job_params(self, circuit, options=None) -> dict:
return {'disable_qubit_rewiring': False}

def _transform_braket_to_qasm_3_program(self, braket_circuit: Circuit,
disable_qubit_rewiring: bool,
inputs: Dict[str, float]) -> str:
"""Transforms a Braket input to a QASM 3 program."""

qubit_reference_type = QubitReferenceType.VIRTUAL

if (
disable_qubit_rewiring
or Instruction(StartVerbatimBox()) in braket_circuit.instructions
or any(isinstance(instruction.operator, PulseGate) for instruction in braket_circuit.instructions)
):
qubit_reference_type = QubitReferenceType.PHYSICAL

serialization_properties = OpenQASMSerializationProperties(
qubit_reference_type=qubit_reference_type
)

openqasm_program = braket_circuit.to_ir(
ir_type=IRType.OPENQASM, serialization_properties=serialization_properties
)
if inputs:
inputs_copy = openqasm_program.inputs.copy() if openqasm_program.inputs is not None else {}
inputs_copy.update(inputs)
openqasm_program = OpenQASMProgram(
source=openqasm_program.source,
inputs=inputs_copy,
)

return openqasm_program.source
14 changes: 14 additions & 0 deletions planqk/qiskit/providers/azure/azure_backend.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
from typing import Optional

from qiskit.circuit import Gate
from qiskit_ionq.helpers import qiskit_circ_to_ionq_circ

from planqk.qiskit import PlanqkBackend
from planqk.qiskit.client.job_dtos import INPUT_FORMAT


class PlanqkAzureBackend(PlanqkBackend):
Expand All @@ -19,3 +21,15 @@ def get_single_qubit_gate_properties(self) -> dict:

def get_multi_qubit_gate_properties(self) -> dict:
return {None: None}

def convert_to_job_input(self, circuit, options):
gateset = options.get("gateset", "qis")
ionq_circ, _, _ = qiskit_circ_to_ionq_circ(circuit, gateset=gateset)
return {
"gateset": gateset,
"qubits": circuit.num_qubits,
"circuit": ionq_circ,
}

def get_job_input_format(self) -> INPUT_FORMAT:
return INPUT_FORMAT.IONQ_CIRCUIT_V1
14 changes: 13 additions & 1 deletion planqk/qiskit/providers/ibm/ibm_backend.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
from typing import Optional
import json
from typing import Optional, Tuple

from qiskit.circuit import Gate
from qiskit.circuit import IfElseOp, WhileLoopOp, ForLoopOp, SwitchCaseOp, Instruction
from qiskit.circuit import Parameter, Reset
from qiskit.circuit.library import IGate, SXGate, XGate, CXGate, RZGate, ECRGate, CZGate
from qiskit.qobj.utils import MeasLevel, MeasReturnType
from qiskit_ibm_runtime import RuntimeEncoder

from planqk.qiskit import PlanqkBackend
from planqk.qiskit.client.job_dtos import INPUT_FORMAT
from planqk.qiskit.options import OptionsV2

ibm_name_mapping = {
Expand Down Expand Up @@ -71,3 +74,12 @@ def to_non_gate_instruction(self, name: str) -> Optional[Instruction]:
return instr

return super().to_non_gate_instruction(name)

def convert_to_job_input(self, circuit, options=None) -> Tuple[INPUT_FORMAT, dict]:
# Transforms circuit to base64 encoded byte stream
input_json_str = json.dumps(circuit, cls=RuntimeEncoder)
# Transform back to json but with the circuit property base64 encoded
return json.loads(input_json_str)

def get_job_input_format(self) -> INPUT_FORMAT:
return INPUT_FORMAT.QISKIT
2 changes: 1 addition & 1 deletion planqk/qiskit/providers/ibm/ibm_provider_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def _submit_job(
job_tags: Optional[List[str]] = None,
image: Optional[str] = None,
) -> IBMCircuitJob:
encoded_input = _encode_circuit_base64(circuit=inputs, backend=self, options=None)
encoded_input = _encode_circuit_base64(circuit=inputs)
hgp_name = 'ibm-q/open/main'

runtime_job_params = RuntimeJobParamsDto(
Expand Down
96 changes: 0 additions & 96 deletions planqk/qiskit/providers/job_input_converter.py

This file was deleted.

27 changes: 26 additions & 1 deletion planqk/qiskit/providers/qryd/qryd_backend.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
from typing import Optional
from typing import Optional, Tuple

from qiskit import QuantumCircuit
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

from planqk.qiskit import PlanqkBackend
from planqk.qiskit.client.job_dtos import INPUT_FORMAT
from planqk.qiskit.options import OptionsV2
from planqk.qiskit.providers.qryd.pcp_gate import PCPGate
from planqk.qiskit.providers.qryd.pcz_gate import PCZGate
from planqk.qiskit.providers.qryd.qryd_converter_utils import convert_to_wire_format

qryd_gate_name_mapping = {
"p": PhaseGate(Parameter("lambda")),
Expand Down Expand Up @@ -66,3 +69,25 @@ def get_multi_qubit_gate_properties(self) -> dict:
qubits = self.backend_info.configuration.qubits
return {(int(qubit1.id), int(qubit2.id)): None for qubit1 in qubits for qubit2 in qubits
if qubit1.id != qubit2.id}

def convert_to_job_input(self, circuit, options=None) -> Tuple[INPUT_FORMAT, dict]:
return convert_to_wire_format(circuit=circuit, options=options)

def convert_to_job_params(self, circuit: QuantumCircuit = None, options=None) -> dict:
return {
"format": "qoqo",
"backend": "", # Backend is set in the middleware
"fusion_max_qubits": options.fusion_max_qubits,
"seed_simulator": options.seed_simulator,
"seed_compiler": options.seed_compiler,
"allow_compilation": options.allow_compilation,
"pcz_theta": float(PCZGate().get_theta()),
"use_extended_set": options.use_extended_set,
"use_reverse_traversal": options.use_reverse_traversal,
"extended_set_size": options.extended_set_size,
"extended_set_weight": options.extended_set_weight,
"reverse_traversal_iterations": options.reverse_traversal_iterations,
}

def get_job_input_format(self) -> INPUT_FORMAT:
return INPUT_FORMAT.QOQO
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from math import pi
from typing import Any
from typing import Dict

from math import pi
from qiskit import QuantumCircuit
from qiskit.providers import Options

Expand Down Expand Up @@ -372,20 +372,3 @@ def convert_to_wire_format(circuit: QuantumCircuit, options: Options) -> dict:
]

return circuit_dict


def create_qoqu_input_params(circuit, options: Options):
return {
"format": "qoqo",
"backend": "", # Backend is set in the middleware
"fusion_max_qubits": options.fusion_max_qubits,
"seed_simulator": options.seed_simulator,
"seed_compiler": options.seed_compiler,
"allow_compilation": options.allow_compilation,
"pcz_theta": float(PCZGate().get_theta()),
"use_extended_set": options.use_extended_set,
"use_reverse_traversal": options.use_reverse_traversal,
"extended_set_size": options.extended_set_size,
"extended_set_weight": options.extended_set_weight,
"reverse_traversal_iterations": options.reverse_traversal_iterations,
}
Loading

0 comments on commit 52fcd0b

Please sign in to comment.