Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

qiskit qir by default #630

Merged
merged 30 commits into from
Aug 30, 2024
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 33 additions & 1 deletion azure-quantum/azure/quantum/qiskit/backends/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
To install run: pip install azure-quantum[qiskit]"
)


class AzureBackendBase(Backend, SessionHost):

# Name of the provider's input parameter which specifies number of shots for a submitted job.
Expand Down Expand Up @@ -280,6 +279,8 @@ def _azure_config(self) -> Dict[str, str]:
"content_type": "qir.v1",
"input_data_format": "qir.v1",
"output_data_format": "microsoft.quantum-results.v2",
"is_default": True,
"is_passthrough": False
}

def run(
Expand Down Expand Up @@ -429,6 +430,37 @@ def _translate_input(

return module.bitcode

def _estimate_cost_qir(self, circuits, shots, options={}):
"""Estimate the cost for the given circuit."""
config = self.configuration()
input_params = self._get_input_params(options, shots=shots)

if not (isinstance(circuits, list)):
circuits = [circuits]

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"),
)

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
)


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

workspace = self.provider().get_workspace()
target = workspace.get_targets(self.name())
return target.estimate_cost(module, shots=shots)


class AzureBackend(AzureBackendBase):
"""Base class for interfacing with a backend in Azure Quantum"""
Expand Down
96 changes: 62 additions & 34 deletions azure-quantum/azure/quantum/qiskit/backends/ionq.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
from qiskit.providers import Options, Provider

from qiskit_ionq.helpers import (
ionq_basis_gates,
GATESET_MAP,
qiskit_circ_to_ionq_circ,
)
Expand All @@ -46,6 +45,24 @@
"IonQForteNativeBackend",
]

QIR_BASIS_GATES = [
LawsonGraham marked this conversation as resolved.
Show resolved Hide resolved
"x",
"y",
"z",
"rx",
"ry",
"rz",
"h",
"swap",
"cx",
"cz",
"s",
"sdg",
"t",
"tdg",
"measure",
]

_IONQ_SHOTS_INPUT_PARAM_NAME = "shots"
_DEFAULT_SHOTS_COUNT = 500

Expand All @@ -68,6 +85,9 @@ def _default_options(cls) -> Options:
},
targetCapability="BasicExecution",
)

def gateset(self):
LawsonGraham marked this conversation as resolved.
Show resolved Hide resolved
return self.configuration().gateset

def _azure_config(self) -> Dict[str, str]:
config = super()._azure_config()
Expand All @@ -77,6 +97,10 @@ def _azure_config(self) -> Dict[str, str]:
}
)
return config

def estimate_cost(self, circuits, shots, options={}):
"""Estimate the cost for the given circuit."""
return self._estimate_cost_qir(circuits, shots, options)

def run(
self,
Expand All @@ -103,7 +127,7 @@ class IonQSimulatorQirBackend(IonQQirBackendBase):

def __init__(self, name: str, provider: "AzureQuantumProvider", **kwargs):
"""Base class for interfacing with an IonQ QIR Simulator backend"""

gateset = kwargs.pop("gateset", "qis")
default_config = BackendConfiguration.from_dict(
{
"backend_name": name,
Expand All @@ -112,7 +136,7 @@ def __init__(self, name: str, provider: "AzureQuantumProvider", **kwargs):
"local": False,
"coupling_map": None,
"description": "IonQ simulator on Azure Quantum",
"basis_gates": ionq_basis_gates,
"basis_gates": QIR_BASIS_GATES,
"memory": False,
"n_qubits": 29,
"conditional": False,
Expand All @@ -121,6 +145,7 @@ def __init__(self, name: str, provider: "AzureQuantumProvider", **kwargs):
"open_pulse": False,
"gates": [{"name": "TODO", "parameters": [], "qasm_def": "TODO"}],
"azure": self._azure_config(),
"gateset": gateset
}
)
logger.info("Initializing IonQSimulatorQirBackend")
Expand All @@ -135,7 +160,7 @@ class IonQAriaQirBackend(IonQQirBackendBase):

def __init__(self, name: str, provider: "AzureQuantumProvider", **kwargs):
"""Base class for interfacing with an IonQ Aria QPU backend"""

gateset = kwargs.pop("gateset", "qis")
default_config = BackendConfiguration.from_dict(
{
"backend_name": name,
Expand All @@ -144,7 +169,7 @@ def __init__(self, name: str, provider: "AzureQuantumProvider", **kwargs):
"local": False,
"coupling_map": None,
"description": "IonQ Aria QPU on Azure Quantum",
"basis_gates": ionq_basis_gates,
"basis_gates": QIR_BASIS_GATES,
"memory": False,
"n_qubits": 23,
"conditional": False,
Expand All @@ -153,6 +178,7 @@ def __init__(self, name: str, provider: "AzureQuantumProvider", **kwargs):
"open_pulse": False,
"gates": [{"name": "TODO", "parameters": [], "qasm_def": "TODO"}],
"azure": self._azure_config(),
"gateset": gateset
}
)
logger.info("Initializing IonQAriaQirBackend")
Expand All @@ -167,7 +193,7 @@ class IonQForteQirBackend(IonQQirBackendBase):

def __init__(self, name: str, provider: "AzureQuantumProvider", **kwargs):
"""Base class for interfacing with an IonQ Forte QPU backend"""

gateset = kwargs.pop("gateset", "qis")
default_config = BackendConfiguration.from_dict(
{
"backend_name": name,
Expand All @@ -176,7 +202,7 @@ def __init__(self, name: str, provider: "AzureQuantumProvider", **kwargs):
"local": False,
"coupling_map": None,
"description": "IonQ Forte QPU on Azure Quantum",
"basis_gates": ionq_basis_gates,
"basis_gates": QIR_BASIS_GATES,
"memory": False,
"n_qubits": 35,
"conditional": False,
Expand All @@ -185,6 +211,7 @@ def __init__(self, name: str, provider: "AzureQuantumProvider", **kwargs):
"open_pulse": False,
"gates": [{"name": "TODO", "parameters": [], "qasm_def": "TODO"}],
"azure": self._azure_config(),
"gateset": gateset
}
)
logger.info("Initializing IonQForteQirBackend")
Expand Down Expand Up @@ -241,7 +268,8 @@ def _azure_config(self) -> Dict[str, str]:
"provider_id": "ionq",
"input_data_format": "ionq.circuit.v1",
"output_data_format": "ionq.quantum-results.v1",
"is_default": True,
"is_default": False,
"is_passthrough": True
}

def _prepare_job_metadata(self, circuit, **kwargs):
Expand Down Expand Up @@ -309,23 +337,23 @@ def __init__(self, name: str, provider: "AzureQuantumProvider", **kwargs):
)
super().__init__(configuration=configuration, provider=provider, **kwargs)


class IonQSimulatorNativeBackend(IonQSimulatorBackend):
def __init__(self, name: str, provider: "AzureQuantumProvider", **kwargs):
if "gateset" not in kwargs:
kwargs["gateset"] = "native"
super().__init__(name, provider, **kwargs)

def _azure_config(self) -> Dict[str, str]:
config = super()._azure_config()
config.update(
{
"is_default": False,
"is_default": True,
LawsonGraham marked this conversation as resolved.
Show resolved Hide resolved
}
)
return config


class IonQSimulatorNativeBackend(IonQSimulatorBackend):
def __init__(self, name: str, provider: "AzureQuantumProvider", **kwargs):
if "gateset" not in kwargs:
kwargs["gateset"] = "native"
super().__init__(name, provider, **kwargs)


class IonQAriaBackend(IonQBackend):
backend_names = ("ionq.qpu.aria-1", "ionq.qpu.aria-2")

Expand Down Expand Up @@ -357,6 +385,15 @@ def __init__(self, name: str, provider: "AzureQuantumProvider", **kwargs):
"configuration", default_config
)
super().__init__(configuration=configuration, provider=provider, **kwargs)

def _azure_config(self) -> Dict[str, str]:
config = super()._azure_config()
config.update(
{
"is_default": True,
}
)
return config


class IonQForteBackend(IonQBackend):
Expand Down Expand Up @@ -390,35 +427,26 @@ def __init__(self, name: str, provider: "AzureQuantumProvider", **kwargs):
"configuration", default_config
)
super().__init__(configuration=configuration, provider=provider, **kwargs)


class IonQAriaNativeBackend(IonQAriaBackend):
def __init__(self, name: str, provider: "AzureQuantumProvider", **kwargs):
if "gateset" not in kwargs:
kwargs["gateset"] = "native"
super().__init__(name, provider, **kwargs)


def _azure_config(self) -> Dict[str, str]:
config = super()._azure_config()
config.update(
{
"is_default": False,
"is_default": True,
}
)
return config


class IonQForteNativeBackend(IonQForteBackend):
class IonQAriaNativeBackend(IonQAriaBackend):
def __init__(self, name: str, provider: "AzureQuantumProvider", **kwargs):
if "gateset" not in kwargs:
kwargs["gateset"] = "native"
super().__init__(name, provider, **kwargs)

def _azure_config(self) -> Dict[str, str]:
config = super()._azure_config()
config.update(
{
"is_default": False,
}
)
return config

class IonQForteNativeBackend(IonQForteBackend):
def __init__(self, name: str, provider: "AzureQuantumProvider", **kwargs):
if "gateset" not in kwargs:
kwargs["gateset"] = "native"
super().__init__(name, provider, **kwargs)
7 changes: 6 additions & 1 deletion azure-quantum/azure/quantum/qiskit/backends/quantinuum.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ def _azure_config(self) -> Dict[str, str]:

def _get_n_qubits(self, name):
return _get_n_qubits(name)

def estimate_cost(self, circuits, shots, options={}):
"""Estimate the cost for the given circuit."""
return self._estimate_cost_qir(circuits, shots, options)


class QuantinuumSyntaxCheckerQirBackend(QuantinuumQirBackendBase):
Expand Down Expand Up @@ -236,7 +240,8 @@ def _azure_config(self) -> Dict[str, str]:
"provider_id": self._provider_id,
"input_data_format": "honeywell.openqasm.v1",
"output_data_format": "honeywell.quantum-results.v1",
"is_default": True,
"is_default": False,
"is_passthrough": True
}

def _translate_input(self, circuit):
Expand Down
35 changes: 29 additions & 6 deletions azure-quantum/azure/quantum/qiskit/job.py
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,26 @@ def _get_entry_point_names(self):
entry_point_names.append(entry_point["entryPoint"])
return entry_point_names if len(entry_point_names) > 0 else ["main"]

def _get_headers(self):
headers = self._azure_job.details.metadata
if (not isinstance(headers, list)):
headers = [headers]

# This function will attempt to parse the header into a JSON object, and if the header is not a JSON object, we return the header itself
def tryParseJSON(header):
try:
json_object = json.loads(header)
except ValueError as e:
return header
return json_object

for header in headers:
del header['qiskit'] # we throw out the qiskit header as it is implied
for key in header.keys():
header[key] = tryParseJSON(header[key])
return headers


def _format_microsoft_v2_results(self) -> List[Dict[str, Any]]:
success = self._azure_job.details.status == "Succeeded"

Expand All @@ -326,10 +346,15 @@ def _format_microsoft_v2_results(self) -> List[Dict[str, Any]]:
entry_point_names = self._get_entry_point_names()

results = self._translate_microsoft_v2_results()

if len(results) != len(entry_point_names):
raise ValueError("The number of experiment results does not match the number of experiment names")
raise ValueError("The number of experiment results does not match the number of entry point names")

headers = self._get_headers()

if len(results) != len(headers):
raise ValueError("The number of experiment results does not match the number of headers")

status = self.status()

return [{
Expand All @@ -338,7 +363,5 @@ def _format_microsoft_v2_results(self) -> List[Dict[str, Any]]:
"shots": total_count,
"name": name,
"status": status,
"header": {
"name": name
}
} for name, (total_count, result) in zip(entry_point_names, results)]
"header": header
} for name, (total_count, result), header in zip(entry_point_names, results, headers)]
Loading
Loading