Skip to content

Commit

Permalink
Resolved merge conflict
Browse files Browse the repository at this point in the history
  • Loading branch information
warren-jones committed Oct 22, 2024
2 parents 55aeb87 + 46c47f8 commit d388893
Show file tree
Hide file tree
Showing 30 changed files with 1,119 additions and 997 deletions.
138 changes: 93 additions & 45 deletions azure-quantum/azure/quantum/qiskit/backends/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,38 +21,53 @@
from azure.quantum.job.session import SessionHost

try:
from qiskit import QuantumCircuit, transpile
from qiskit import QuantumCircuit
from qiskit.providers import BackendV1 as Backend
from qiskit.providers import Options
from qiskit.providers import Provider
from qiskit.providers.models import BackendConfiguration
from qiskit.qobj import QasmQobj, PulseQobj
from pyqir import Module
from qiskit_qir import to_qir_module
import pyqir as pyqir
from qsharp.interop.qiskit import QSharpBackend
from qsharp import TargetProfile

except ImportError:
raise ImportError(
"Missing optional 'qiskit' dependencies. \
To install run: pip install azure-quantum[qiskit]"
)

# barrier is handled by an extra flag which will transpile
# them away if the backend doesn't support them. This has
# to be done as a special pass as the transpiler will not
# remove barriers by default.
QIR_BASIS_GATES = [
"x",
"y",
"z",
"measure",
"reset",
"ccx",
"cx",
"cy",
"cz",
"rx",
"rxx",
"crx",
"ry",
"ryy",
"cry",
"rz",
"rzz",
"crz",
"h",
"swap",
"cx",
"cz",
"reset",
"s",
"sdg",
"swap",
"t",
"tdg",
"measure",
"x",
"y",
"z",
"id",
"ch",
]


Expand Down Expand Up @@ -391,66 +406,99 @@ def _prepare_job_metadata(self, circuits: List[QuantumCircuit]) -> Dict[str, str
return {}

def _generate_qir(
self, circuits, targetCapability, **to_qir_kwargs
) -> Tuple[Module, List[str]]:
self, circuits: List[QuantumCircuit], target_profile: TargetProfile, **kwargs
) -> pyqir.Module:

if len(circuits) == 0:
raise ValueError("No QuantumCircuits provided")

config = self.configuration()
# Barriers aren't removed by transpilation and must be explicitly removed in the Qiskit to QIR translation.
emit_barrier_calls = "barrier" in config.basis_gates
return to_qir_module(
circuits,
targetCapability,
emit_barrier_calls=emit_barrier_calls,
**to_qir_kwargs,
supports_barrier = "barrier" in config.basis_gates
skip_transpilation = kwargs.pop("skip_transpilation", False)

backend = QSharpBackend(
qiskit_pass_options={"supports_barrier": supports_barrier},
target_profile=target_profile,
skip_transpilation=skip_transpilation,
**kwargs,
)

def _get_qir_str(self, circuits, targetCapability, **to_qir_kwargs) -> str:
module, _ = self._generate_qir(circuits, targetCapability, **to_qir_kwargs)
name = "batch"
if len(circuits) == 1:
name = circuits[0].name

if isinstance(circuits, list):
for value in circuits:
if not isinstance(value, QuantumCircuit):
raise ValueError("Input must be List[QuantumCircuit]")
else:
raise ValueError("Input must be List[QuantumCircuit]")

context = pyqir.Context()
llvm_module = pyqir.qir_module(context, name)
for circuit in circuits:
qir_str = backend.qir(circuit)
module = pyqir.Module.from_ir(context, qir_str)
entry_point = next(filter(pyqir.is_entry_point, module.functions))
entry_point.name = circuit.name
llvm_module.link(module)
err = llvm_module.verify()
if err is not None:
raise Exception(err)

return llvm_module

def _get_qir_str(
self,
circuits: List[QuantumCircuit],
target_profile: TargetProfile,
**to_qir_kwargs,
) -> str:
module = self._generate_qir(circuits, target_profile, **to_qir_kwargs)
return str(module)

def _translate_input(
self, circuits: List[QuantumCircuit], input_params: Dict[str, Any]
self, circuits: Union[QuantumCircuit, List[QuantumCircuit]], input_params: Dict[str, Any]
) -> bytes:
"""Translates the input values to the QIR expected by the Backend."""
logger.info(f"Using QIR as the job's payload format.")
config = self.configuration()
if not (isinstance(circuits, list)):
circuits = [circuits]

# Override QIR translation parameters
# We will record the output by default, but allow the backend to override this, and allow the user to override the backend.
to_qir_kwargs = input_params.pop(
"to_qir_kwargs", config.azure.get("to_qir_kwargs", {"record_output": True})
)
targetCapability = input_params.pop(
"targetCapability",
self.options.get("targetCapability", "AdaptiveExecution"),
)
target_profile = self._get_target_profile(input_params)

if logger.isEnabledFor(logging.DEBUG):
qir = self._get_qir_str(circuits, targetCapability, **to_qir_kwargs)
qir = self._get_qir_str(circuits, target_profile, skip_transpilation=True)
logger.debug(f"QIR:\n{qir}")

# We'll transpile automatically to the supported gates in QIR unless explicitly skipped.
if not input_params.pop("skipTranspile", False):
# Set of gates supported by QIR targets.
circuits = transpile(
circuits, basis_gates=config.basis_gates, optimization_level=0
)
skip_transpilation = input_params.pop("skipTranspile", False)

module = self._generate_qir(
circuits, target_profile, skip_transpilation=skip_transpilation
)

def get_func_name(func: pyqir.Function) -> str:
return func.name

entry_points = list(
map(get_func_name, filter(pyqir.is_entry_point, module.functions))
)

if not skip_transpilation:
# We'll only log the QIR again if we performed a transpilation.
if logger.isEnabledFor(logging.DEBUG):
qir = self._get_qir_str(circuits, targetCapability, **to_qir_kwargs)
qir = str(module)
logger.debug(f"QIR (Post-transpilation):\n{qir}")

(module, entry_points) = self._generate_qir(
circuits, targetCapability, **to_qir_kwargs
)

if not "items" in input_params:
if "items" not in input_params:
arguments = input_params.pop("arguments", [])
input_params["items"] = [
{"entryPoint": name, "arguments": arguments} for name in entry_points
]

return module.bitcode
return str(module).encode("utf-8")

class AzureBackend(AzureBackendBase):
"""Base class for interfacing with a backend in Azure Quantum"""
Expand Down
4 changes: 2 additions & 2 deletions azure-quantum/azure/quantum/qiskit/backends/ionq.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from azure.quantum.qiskit.job import AzureQuantumJob
from azure.quantum.target.ionq import IonQ
from abc import abstractmethod

from qsharp import TargetProfile
from qiskit import QuantumCircuit

from .backend import (
Expand Down Expand Up @@ -65,7 +65,7 @@ def _default_options(cls) -> Options:
**{
cls._SHOTS_PARAM_NAME: _DEFAULT_SHOTS_COUNT,
},
targetCapability="BasicExecution",
target_profile=TargetProfile.Base,
)

def _azure_config(self) -> Dict[str, str]:
Expand Down
89 changes: 62 additions & 27 deletions azure-quantum/azure/quantum/qiskit/backends/microsoft.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,17 @@
# Licensed under the MIT License.
##

from typing import TYPE_CHECKING, Any, Dict, List
from typing import TYPE_CHECKING, Any, Dict, List, Union
from azure.quantum.version import __version__
from qiskit import QuantumCircuit
from abc import abstractmethod
from .backend import AzureQirBackend
from .backend import AzureQirBackend, QIR_BASIS_GATES

from qiskit.providers.models import BackendConfiguration
from qiskit.providers import Options, Provider

QIR_BASIS_GATES = [
"measure",
"m",
"ccx",
"cx",
"cz",
"h",
"reset",
"rx",
"ry",
"rz",
"s",
"sdg",
"swap",
"t",
"tdg",
"x",
"y",
"z",
"id",
]
from qsharp import TargetProfile
from qsharp.interop.qiskit import ResourceEstimatorBackend
import pyqir as pyqir

if TYPE_CHECKING:
from azure.quantum.qiskit import AzureQuantumProvider
Expand All @@ -55,19 +36,73 @@ def __init__(

@classmethod
def _default_options(cls):
return Options(targetCapability="AdaptiveExecution")
return Options(target_profile=TargetProfile.Adaptive_RI)

def _azure_config(self) -> Dict[str, str]:
config = super()._azure_config()
config.update(
{
"provider_id": "microsoft-qc",
"output_data_format": "microsoft.resource-estimates.v1",
"to_qir_kwargs": {"record_output": False},
}
)
return config

def _translate_input(
self,
circuits: Union[QuantumCircuit, List[QuantumCircuit]],
input_params: Dict[str, Any],
) -> bytes:
"""Translates the input values to the QIR expected by the Backend."""
# All the logic is in the base class, but we need to override
# this method to ensure that the bitcode QIR format is used for RE.

# normal translation is to QIR text format in utf-8 encoded bytes
ir_byte_str = super()._translate_input(circuits, input_params)
# decode the utf-8 encoded bytes to a string
ir_str = ir_byte_str.decode('utf-8')
# convert the QIR text format to QIR bitcode format
module = pyqir.Module.from_ir(pyqir.Context(), ir_str)
return module.bitcode

def _generate_qir(
self, circuits: List[QuantumCircuit], target_profile: TargetProfile, **kwargs
) -> pyqir.Module:
if len(circuits) == 0:
raise ValueError("No QuantumCircuits provided")

name = "circuits"
if isinstance(circuits, QuantumCircuit):
name = circuits.name
circuits = [circuits]
elif isinstance(circuits, list):
for value in circuits:
if not isinstance(value, QuantumCircuit):
raise ValueError(
"Input must be Union[QuantumCircuit, List[QuantumCircuit]]"
)
else:
raise ValueError(
"Input must be Union[QuantumCircuit, List[QuantumCircuit]]"
)

skip_transpilation = kwargs.pop("skip_transpilation", False)
backend = ResourceEstimatorBackend(
skip_transpilation=skip_transpilation, **kwargs
)
context = pyqir.Context()
llvm_module = pyqir.qir_module(context, name)
for circuit in circuits:
qir_str = backend.qir(circuit, target_profile=target_profile)
module = pyqir.Module.from_ir(context, qir_str)
llvm_module.link(module)

err = llvm_module.verify()
if err is not None:
raise Exception(err)

return llvm_module


class MicrosoftResourceEstimationBackend(MicrosoftBackend):
"""Backend class for interfacing with the resource estimator target"""
Expand All @@ -77,7 +112,7 @@ class MicrosoftResourceEstimationBackend(MicrosoftBackend):
@classmethod
def _default_options(cls):
return Options(
targetCapability="AdaptiveExecution",
target_profile=TargetProfile.Adaptive_RI,
errorBudget=1e-3,
qubitParams={"name": "qubit_gate_ns_e3"},
qecScheme={"name": "surface_code"}
Expand Down
13 changes: 9 additions & 4 deletions azure-quantum/azure/quantum/qiskit/backends/qci.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# Licensed under the MIT License.
##

from typing import TYPE_CHECKING, Dict
from typing import TYPE_CHECKING, Dict, List
from azure.quantum.version import __version__
from azure.quantum.qiskit.job import AzureQuantumJob
from abc import abstractmethod
Expand All @@ -14,6 +14,8 @@

from qiskit.providers.models import BackendConfiguration
from qiskit.providers import Options, Provider
from qsharp import TargetProfile


if TYPE_CHECKING:
from azure.quantum.qiskit import AzureQuantumProvider
Expand Down Expand Up @@ -43,7 +45,7 @@ def _default_options(cls) -> Options:
**{
cls._SHOTS_PARAM_NAME: _DEFAULT_SHOTS_COUNT,
},
targetCapability="AdaptiveExecution",
target_profile=TargetProfile.Adaptive_RI,
)

def _azure_config(self) -> Dict[str, str]:
Expand All @@ -54,9 +56,12 @@ def _azure_config(self) -> Dict[str, str]:
}
)
return config


def _basis_gates(self) -> List[str]:
return super()._basis_gates() + ["barrier"]

def run(
self,
self,
run_input=None,
shots: int = None,
**options,
Expand Down
Loading

0 comments on commit d388893

Please sign in to comment.