Skip to content

Commit

Permalink
Updating the artifact download path to fix the publish pipeline (micr…
Browse files Browse the repository at this point in the history
  • Loading branch information
masenol authored and Lawson Graham committed Aug 6, 2024
1 parent 085939b commit e1cb3a1
Show file tree
Hide file tree
Showing 53 changed files with 9,765 additions and 6,057 deletions.
2 changes: 1 addition & 1 deletion .ado/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ extends:
inputs:
- input: pipelineArtifact
displayName: 'Download azure-quantum artifacts'
targetPath: $(System.DefaultWorkingDirectory)/drop
targetPath: $(Pipeline.Workspace)/azure-quantum-wheels
artifactName: azure-quantum-wheels
steps:
- task: UsePythonVersion@0
Expand Down
81 changes: 53 additions & 28 deletions azure-quantum/azure/quantum/qiskit/backends/ionq.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from azure.quantum.target.ionq import IonQ
from abc import abstractmethod

from qiskit import QuantumCircuit
from qiskit import QuantumCircuit, transpile

from .backend import (
AzureBackend,
Expand All @@ -21,6 +21,7 @@

from qiskit_ionq.helpers import (
GATESET_MAP,
GATESET_MAP_QIR,
qiskit_circ_to_ionq_circ,
)

Expand Down Expand Up @@ -51,25 +52,6 @@
_IONQ_SHOTS_INPUT_PARAM_NAME = "shots"
_DEFAULT_SHOTS_COUNT = 500

IONQ_BASIS_GATES = [
"measure",
"m",
"cx",
"cz",
"h",
"reset",
"rx",
"ry",
"rz",
"s",
"swap",
"t",
"x",
"y",
"z",
"id",
]

class IonQQirBackendBase(AzureQirBackend):
"""Base class for interfacing with an IonQ QIR backend"""

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

def gateset(self):
return self.configuration().gateset

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

# TODO: decide if we want to allow for options passing differently
def estimate_cost(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]

# TODO: evaluate proper means of use / fetching these values for transpile (could ignore, could use)
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
)

qir = self._get_qir_str(circuits, targetCapability, **to_qir_kwargs)
print (qir)


(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)

def run(
self,
Expand All @@ -124,7 +145,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 @@ -133,7 +154,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": GATESET_MAP_QIR[gateset],
"memory": False,
"n_qubits": 29,
"conditional": False,
Expand All @@ -142,6 +163,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 @@ -156,7 +178,7 @@ class IonQQPUQirBackend(IonQQirBackendBase):

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

gateset = kwargs.pop("gateset", "qis")
default_config = BackendConfiguration.from_dict(
{
"backend_name": name,
Expand All @@ -165,7 +187,7 @@ def __init__(self, name: str, provider: "AzureQuantumProvider", **kwargs):
"local": False,
"coupling_map": None,
"description": "IonQ QPU on Azure Quantum",
"basis_gates": IONQ_BASIS_GATES,
"basis_gates": GATESET_MAP_QIR[gateset],
"memory": False,
"n_qubits": 11,
"conditional": False,
Expand All @@ -174,6 +196,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 IonQQPUQirBackend")
Expand All @@ -188,7 +211,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 @@ -197,7 +220,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": GATESET_MAP_QIR[gateset],
"memory": False,
"n_qubits": 23,
"conditional": False,
Expand All @@ -206,6 +229,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 @@ -220,7 +244,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 @@ -229,7 +253,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": GATESET_MAP_QIR[gateset],
"memory": False,
"n_qubits": 35,
"conditional": False,
Expand All @@ -238,6 +262,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
65 changes: 61 additions & 4 deletions azure-quantum/azure/quantum/qiskit/backends/quantinuum.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

from .backend import AzureBackend, AzureQirBackend
from abc import abstractmethod
from qiskit import QuantumCircuit
from qiskit import QuantumCircuit, transpile
from qiskit.providers.models import BackendConfiguration
from qiskit.providers import Options
from qiskit.providers import Provider
Expand Down Expand Up @@ -48,6 +48,27 @@
"reset",
]

QUANTINUUM_BASIS_GATES_QIR = [
"x",
"y",
"z",
"rx",
"ry",
"rz",
"h",
"cx",
"cz",
"s",
"sdg",
"t",
"tdg",
"v",
"vdg",
"zz",
"measure",
"reset",
]

QUANTINUUM_PROVIDER_ID = "quantinuum"
QUANTINUUM_PROVIDER_NAME = "Quantinuum"

Expand Down Expand Up @@ -95,6 +116,42 @@ def _azure_config(self) -> Dict[str, str]:

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

# TODO: decide if we want to allow for options passing differently
def estimate_cost(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]

# TODO: evaluate proper means of use / fetching these values for transpile (could ignore, could use)
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
)

qir = self._get_qir_str(circuits, targetCapability, **to_qir_kwargs)
print (qir)


(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 QuantinuumSyntaxCheckerQirBackend(QuantinuumQirBackendBase):
Expand All @@ -116,7 +173,7 @@ def __init__(self, name: str, provider: "AzureQuantumProvider", **kwargs):
"local": False,
"coupling_map": None,
"description": f"Quantinuum Syntax Checker on Azure Quantum",
"basis_gates": QUANTINUUM_BASIS_GATES,
"basis_gates": QUANTINUUM_BASIS_GATES_QIR,
"memory": True,
"n_qubits": self._get_n_qubits(name),
"conditional": False,
Expand Down Expand Up @@ -153,7 +210,7 @@ def __init__(self, name: str, provider: "AzureQuantumProvider", **kwargs):
"local": False,
"coupling_map": None,
"description": f"Quantinuum emulator on Azure Quantum",
"basis_gates": QUANTINUUM_BASIS_GATES,
"basis_gates": QUANTINUUM_BASIS_GATES_QIR,
"memory": True,
"n_qubits": self._get_n_qubits(name),
"conditional": False,
Expand Down Expand Up @@ -190,7 +247,7 @@ def __init__(self, name: str, provider: "AzureQuantumProvider", **kwargs):
"local": False,
"coupling_map": None,
"description": f"Quantinuum QPU on Azure Quantum",
"basis_gates": QUANTINUUM_BASIS_GATES,
"basis_gates": QUANTINUUM_BASIS_GATES_QIR,
"memory": True,
"n_qubits": self._get_n_qubits(name),
"conditional": False,
Expand Down
26 changes: 21 additions & 5 deletions azure-quantum/azure/quantum/qiskit/job.py
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,20 @@ 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
# TODO: when multi circuit jobs are possible, confirm metadata field will be a list of metadatas
if (not isinstance(headers, list)):
headers = [headers]

for header in headers:
del header['qiskit'] # we throw out the qiskit header as it is implied
for key in header.keys():
if isinstance(header[key], str) and header[key].startswith('{') and header[key].endswith('}'):
header[key] = json.loads(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 +340,14 @@ def _format_microsoft_v2_results(self) -> List[Dict[str, Any]]:
entry_point_names = self._get_entry_point_names()

results = self._translate_microsoft_v2_results()


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

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

status = self.status()

return [{
Expand All @@ -338,7 +356,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

0 comments on commit e1cb3a1

Please sign in to comment.