azure-quantum 2.1.1__py3-none-any.whl → 2.2.0.dev1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- azure/quantum/_client/_version.py +1 -1
- azure/quantum/qiskit/backends/backend.py +54 -0
- azure/quantum/qiskit/backends/ionq.py +8 -35
- azure/quantum/qiskit/backends/qci.py +2 -24
- azure/quantum/qiskit/backends/quantinuum.py +29 -4
- azure/quantum/qiskit/backends/rigetti.py +2 -22
- azure/quantum/qiskit/job.py +29 -6
- azure/quantum/qiskit/provider.py +4 -4
- azure/quantum/target/ionq.py +24 -19
- azure/quantum/target/quantinuum.py +31 -25
- azure/quantum/target/target.py +61 -1
- azure/quantum/version.py +1 -1
- {azure_quantum-2.1.1.dist-info → azure_quantum-2.2.0.dev1.dist-info}/METADATA +1 -1
- {azure_quantum-2.1.1.dist-info → azure_quantum-2.2.0.dev1.dist-info}/RECORD +16 -16
- {azure_quantum-2.1.1.dist-info → azure_quantum-2.2.0.dev1.dist-info}/WHEEL +0 -0
- {azure_quantum-2.1.1.dist-info → azure_quantum-2.2.0.dev1.dist-info}/top_level.txt +0 -0
|
@@ -36,6 +36,25 @@ except ImportError:
|
|
|
36
36
|
To install run: pip install azure-quantum[qiskit]"
|
|
37
37
|
)
|
|
38
38
|
|
|
39
|
+
QIR_BASIS_GATES = [
|
|
40
|
+
"x",
|
|
41
|
+
"y",
|
|
42
|
+
"z",
|
|
43
|
+
"rx",
|
|
44
|
+
"ry",
|
|
45
|
+
"rz",
|
|
46
|
+
"h",
|
|
47
|
+
"swap",
|
|
48
|
+
"cx",
|
|
49
|
+
"cz",
|
|
50
|
+
"reset",
|
|
51
|
+
"s",
|
|
52
|
+
"sdg",
|
|
53
|
+
"t",
|
|
54
|
+
"tdg",
|
|
55
|
+
"measure",
|
|
56
|
+
]
|
|
57
|
+
|
|
39
58
|
|
|
40
59
|
class AzureBackendBase(Backend, SessionHost):
|
|
41
60
|
|
|
@@ -280,7 +299,11 @@ class AzureQirBackend(AzureBackendBase):
|
|
|
280
299
|
"content_type": "qir.v1",
|
|
281
300
|
"input_data_format": "qir.v1",
|
|
282
301
|
"output_data_format": "microsoft.quantum-results.v2",
|
|
302
|
+
"is_default": True,
|
|
283
303
|
}
|
|
304
|
+
|
|
305
|
+
def _basis_gates(self) -> List[str]:
|
|
306
|
+
return QIR_BASIS_GATES
|
|
284
307
|
|
|
285
308
|
def run(
|
|
286
309
|
self,
|
|
@@ -429,6 +452,37 @@ class AzureQirBackend(AzureBackendBase):
|
|
|
429
452
|
|
|
430
453
|
return module.bitcode
|
|
431
454
|
|
|
455
|
+
def _estimate_cost_qir(self, circuits, shots, options={}):
|
|
456
|
+
"""Estimate the cost for the given circuit."""
|
|
457
|
+
config = self.configuration()
|
|
458
|
+
input_params = self._get_input_params(options, shots=shots)
|
|
459
|
+
|
|
460
|
+
if not (isinstance(circuits, list)):
|
|
461
|
+
circuits = [circuits]
|
|
462
|
+
|
|
463
|
+
to_qir_kwargs = input_params.pop(
|
|
464
|
+
"to_qir_kwargs", config.azure.get("to_qir_kwargs", {"record_output": True})
|
|
465
|
+
)
|
|
466
|
+
targetCapability = input_params.pop(
|
|
467
|
+
"targetCapability",
|
|
468
|
+
self.options.get("targetCapability", "AdaptiveExecution"),
|
|
469
|
+
)
|
|
470
|
+
|
|
471
|
+
if not input_params.pop("skipTranspile", False):
|
|
472
|
+
# Set of gates supported by QIR targets.
|
|
473
|
+
circuits = transpile(
|
|
474
|
+
circuits, basis_gates=config.basis_gates, optimization_level=0
|
|
475
|
+
)
|
|
476
|
+
|
|
477
|
+
|
|
478
|
+
(module, _) = self._generate_qir(
|
|
479
|
+
circuits, targetCapability, **to_qir_kwargs
|
|
480
|
+
)
|
|
481
|
+
|
|
482
|
+
workspace = self.provider().get_workspace()
|
|
483
|
+
target = workspace.get_targets(self.name())
|
|
484
|
+
return target.estimate_cost(module, shots=shots)
|
|
485
|
+
|
|
432
486
|
|
|
433
487
|
class AzureBackend(AzureBackendBase):
|
|
434
488
|
"""Base class for interfacing with a backend in Azure Quantum"""
|
|
@@ -20,7 +20,6 @@ from qiskit.providers.models import BackendConfiguration
|
|
|
20
20
|
from qiskit.providers import Options, Provider
|
|
21
21
|
|
|
22
22
|
from qiskit_ionq.helpers import (
|
|
23
|
-
ionq_basis_gates,
|
|
24
23
|
GATESET_MAP,
|
|
25
24
|
qiskit_circ_to_ionq_circ,
|
|
26
25
|
)
|
|
@@ -77,6 +76,10 @@ class IonQQirBackendBase(AzureQirBackend):
|
|
|
77
76
|
}
|
|
78
77
|
)
|
|
79
78
|
return config
|
|
79
|
+
|
|
80
|
+
def estimate_cost(self, circuits, shots, options={}):
|
|
81
|
+
"""Estimate the cost for the given circuit."""
|
|
82
|
+
return self._estimate_cost_qir(circuits, shots, options)
|
|
80
83
|
|
|
81
84
|
def run(
|
|
82
85
|
self,
|
|
@@ -103,7 +106,6 @@ class IonQSimulatorQirBackend(IonQQirBackendBase):
|
|
|
103
106
|
|
|
104
107
|
def __init__(self, name: str, provider: "AzureQuantumProvider", **kwargs):
|
|
105
108
|
"""Base class for interfacing with an IonQ QIR Simulator backend"""
|
|
106
|
-
|
|
107
109
|
default_config = BackendConfiguration.from_dict(
|
|
108
110
|
{
|
|
109
111
|
"backend_name": name,
|
|
@@ -112,7 +114,7 @@ class IonQSimulatorQirBackend(IonQQirBackendBase):
|
|
|
112
114
|
"local": False,
|
|
113
115
|
"coupling_map": None,
|
|
114
116
|
"description": "IonQ simulator on Azure Quantum",
|
|
115
|
-
"basis_gates":
|
|
117
|
+
"basis_gates": self._basis_gates(),
|
|
116
118
|
"memory": False,
|
|
117
119
|
"n_qubits": 29,
|
|
118
120
|
"conditional": False,
|
|
@@ -135,7 +137,6 @@ class IonQAriaQirBackend(IonQQirBackendBase):
|
|
|
135
137
|
|
|
136
138
|
def __init__(self, name: str, provider: "AzureQuantumProvider", **kwargs):
|
|
137
139
|
"""Base class for interfacing with an IonQ Aria QPU backend"""
|
|
138
|
-
|
|
139
140
|
default_config = BackendConfiguration.from_dict(
|
|
140
141
|
{
|
|
141
142
|
"backend_name": name,
|
|
@@ -144,7 +145,7 @@ class IonQAriaQirBackend(IonQQirBackendBase):
|
|
|
144
145
|
"local": False,
|
|
145
146
|
"coupling_map": None,
|
|
146
147
|
"description": "IonQ Aria QPU on Azure Quantum",
|
|
147
|
-
"basis_gates":
|
|
148
|
+
"basis_gates": self._basis_gates(),
|
|
148
149
|
"memory": False,
|
|
149
150
|
"n_qubits": 23,
|
|
150
151
|
"conditional": False,
|
|
@@ -167,7 +168,6 @@ class IonQForteQirBackend(IonQQirBackendBase):
|
|
|
167
168
|
|
|
168
169
|
def __init__(self, name: str, provider: "AzureQuantumProvider", **kwargs):
|
|
169
170
|
"""Base class for interfacing with an IonQ Forte QPU backend"""
|
|
170
|
-
|
|
171
171
|
default_config = BackendConfiguration.from_dict(
|
|
172
172
|
{
|
|
173
173
|
"backend_name": name,
|
|
@@ -176,7 +176,7 @@ class IonQForteQirBackend(IonQQirBackendBase):
|
|
|
176
176
|
"local": False,
|
|
177
177
|
"coupling_map": None,
|
|
178
178
|
"description": "IonQ Forte QPU on Azure Quantum",
|
|
179
|
-
"basis_gates":
|
|
179
|
+
"basis_gates": self._basis_gates(),
|
|
180
180
|
"memory": False,
|
|
181
181
|
"n_qubits": 35,
|
|
182
182
|
"conditional": False,
|
|
@@ -241,7 +241,7 @@ class IonQBackend(AzureBackend):
|
|
|
241
241
|
"provider_id": "ionq",
|
|
242
242
|
"input_data_format": "ionq.circuit.v1",
|
|
243
243
|
"output_data_format": "ionq.quantum-results.v1",
|
|
244
|
-
"is_default":
|
|
244
|
+
"is_default": False,
|
|
245
245
|
}
|
|
246
246
|
|
|
247
247
|
def _prepare_job_metadata(self, circuit, **kwargs):
|
|
@@ -316,15 +316,6 @@ class IonQSimulatorNativeBackend(IonQSimulatorBackend):
|
|
|
316
316
|
kwargs["gateset"] = "native"
|
|
317
317
|
super().__init__(name, provider, **kwargs)
|
|
318
318
|
|
|
319
|
-
def _azure_config(self) -> Dict[str, str]:
|
|
320
|
-
config = super()._azure_config()
|
|
321
|
-
config.update(
|
|
322
|
-
{
|
|
323
|
-
"is_default": False,
|
|
324
|
-
}
|
|
325
|
-
)
|
|
326
|
-
return config
|
|
327
|
-
|
|
328
319
|
|
|
329
320
|
class IonQAriaBackend(IonQBackend):
|
|
330
321
|
backend_names = ("ionq.qpu.aria-1", "ionq.qpu.aria-2")
|
|
@@ -398,27 +389,9 @@ class IonQAriaNativeBackend(IonQAriaBackend):
|
|
|
398
389
|
kwargs["gateset"] = "native"
|
|
399
390
|
super().__init__(name, provider, **kwargs)
|
|
400
391
|
|
|
401
|
-
def _azure_config(self) -> Dict[str, str]:
|
|
402
|
-
config = super()._azure_config()
|
|
403
|
-
config.update(
|
|
404
|
-
{
|
|
405
|
-
"is_default": False,
|
|
406
|
-
}
|
|
407
|
-
)
|
|
408
|
-
return config
|
|
409
|
-
|
|
410
392
|
|
|
411
393
|
class IonQForteNativeBackend(IonQForteBackend):
|
|
412
394
|
def __init__(self, name: str, provider: "AzureQuantumProvider", **kwargs):
|
|
413
395
|
if "gateset" not in kwargs:
|
|
414
396
|
kwargs["gateset"] = "native"
|
|
415
397
|
super().__init__(name, provider, **kwargs)
|
|
416
|
-
|
|
417
|
-
def _azure_config(self) -> Dict[str, str]:
|
|
418
|
-
config = super()._azure_config()
|
|
419
|
-
config.update(
|
|
420
|
-
{
|
|
421
|
-
"is_default": False,
|
|
422
|
-
}
|
|
423
|
-
)
|
|
424
|
-
return config
|
|
@@ -15,28 +15,6 @@ from .backend import (
|
|
|
15
15
|
from qiskit.providers.models import BackendConfiguration
|
|
16
16
|
from qiskit.providers import Options, Provider
|
|
17
17
|
|
|
18
|
-
QIR_BASIS_GATES = [
|
|
19
|
-
"measure",
|
|
20
|
-
"m",
|
|
21
|
-
"barrier",
|
|
22
|
-
"cx",
|
|
23
|
-
"cz",
|
|
24
|
-
"h",
|
|
25
|
-
"reset",
|
|
26
|
-
"rx",
|
|
27
|
-
"ry",
|
|
28
|
-
"rz",
|
|
29
|
-
"s",
|
|
30
|
-
"sdg",
|
|
31
|
-
"swap",
|
|
32
|
-
"t",
|
|
33
|
-
"tdg",
|
|
34
|
-
"x",
|
|
35
|
-
"y",
|
|
36
|
-
"z",
|
|
37
|
-
"id",
|
|
38
|
-
]
|
|
39
|
-
|
|
40
18
|
if TYPE_CHECKING:
|
|
41
19
|
from azure.quantum.qiskit import AzureQuantumProvider
|
|
42
20
|
|
|
@@ -110,7 +88,7 @@ class QCISimulatorBackend(QCIBackend):
|
|
|
110
88
|
"local": False,
|
|
111
89
|
"coupling_map": None,
|
|
112
90
|
"description": "QCI simulator on Azure Quantum",
|
|
113
|
-
"basis_gates":
|
|
91
|
+
"basis_gates": self._basis_gates(),
|
|
114
92
|
"memory": False,
|
|
115
93
|
"n_qubits": 29,
|
|
116
94
|
"conditional": True,
|
|
@@ -142,7 +120,7 @@ class QCIQPUBackend(QCIBackend):
|
|
|
142
120
|
"local": False,
|
|
143
121
|
"coupling_map": None,
|
|
144
122
|
"description": "QCI QPU on Azure Quantum",
|
|
145
|
-
"basis_gates":
|
|
123
|
+
"basis_gates": self._basis_gates(),
|
|
146
124
|
"memory": False,
|
|
147
125
|
"n_qubits": 11,
|
|
148
126
|
"conditional": True,
|
|
@@ -50,6 +50,24 @@ QUANTINUUM_BASIS_GATES = [
|
|
|
50
50
|
"reset",
|
|
51
51
|
]
|
|
52
52
|
|
|
53
|
+
QUANTINUUM_QIR_BASIS_GATES = [
|
|
54
|
+
"x",
|
|
55
|
+
"y",
|
|
56
|
+
"z",
|
|
57
|
+
"rx",
|
|
58
|
+
"ry",
|
|
59
|
+
"rz",
|
|
60
|
+
"h",
|
|
61
|
+
"cx",
|
|
62
|
+
"cz",
|
|
63
|
+
"reset",
|
|
64
|
+
"s",
|
|
65
|
+
"sdg",
|
|
66
|
+
"t",
|
|
67
|
+
"tdg",
|
|
68
|
+
"measure",
|
|
69
|
+
]
|
|
70
|
+
|
|
53
71
|
QUANTINUUM_PROVIDER_ID = "quantinuum"
|
|
54
72
|
QUANTINUUM_PROVIDER_NAME = "Quantinuum"
|
|
55
73
|
|
|
@@ -94,9 +112,16 @@ class QuantinuumQirBackendBase(AzureQirBackend):
|
|
|
94
112
|
}
|
|
95
113
|
)
|
|
96
114
|
return config
|
|
115
|
+
|
|
116
|
+
def _basis_gates(self) -> List[str]:
|
|
117
|
+
return QUANTINUUM_QIR_BASIS_GATES
|
|
97
118
|
|
|
98
119
|
def _get_n_qubits(self, name):
|
|
99
120
|
return _get_n_qubits(name)
|
|
121
|
+
|
|
122
|
+
def estimate_cost(self, circuits, shots, options={}):
|
|
123
|
+
"""Estimate the cost for the given circuit."""
|
|
124
|
+
return self._estimate_cost_qir(circuits, shots, options)
|
|
100
125
|
|
|
101
126
|
|
|
102
127
|
class QuantinuumSyntaxCheckerQirBackend(QuantinuumQirBackendBase):
|
|
@@ -118,7 +143,7 @@ class QuantinuumSyntaxCheckerQirBackend(QuantinuumQirBackendBase):
|
|
|
118
143
|
"local": False,
|
|
119
144
|
"coupling_map": None,
|
|
120
145
|
"description": f"Quantinuum Syntax Checker on Azure Quantum",
|
|
121
|
-
"basis_gates":
|
|
146
|
+
"basis_gates": self._basis_gates(),
|
|
122
147
|
"memory": True,
|
|
123
148
|
"n_qubits": self._get_n_qubits(name),
|
|
124
149
|
"conditional": False,
|
|
@@ -155,7 +180,7 @@ class QuantinuumEmulatorQirBackend(QuantinuumQirBackendBase):
|
|
|
155
180
|
"local": False,
|
|
156
181
|
"coupling_map": None,
|
|
157
182
|
"description": f"Quantinuum emulator on Azure Quantum",
|
|
158
|
-
"basis_gates":
|
|
183
|
+
"basis_gates": self._basis_gates(),
|
|
159
184
|
"memory": True,
|
|
160
185
|
"n_qubits": self._get_n_qubits(name),
|
|
161
186
|
"conditional": False,
|
|
@@ -192,7 +217,7 @@ class QuantinuumQPUQirBackend(QuantinuumQirBackendBase):
|
|
|
192
217
|
"local": False,
|
|
193
218
|
"coupling_map": None,
|
|
194
219
|
"description": f"Quantinuum QPU on Azure Quantum",
|
|
195
|
-
"basis_gates":
|
|
220
|
+
"basis_gates": self._basis_gates(),
|
|
196
221
|
"memory": True,
|
|
197
222
|
"n_qubits": self._get_n_qubits(name),
|
|
198
223
|
"conditional": False,
|
|
@@ -236,7 +261,7 @@ class QuantinuumBackend(AzureBackend):
|
|
|
236
261
|
"provider_id": self._provider_id,
|
|
237
262
|
"input_data_format": "honeywell.openqasm.v1",
|
|
238
263
|
"output_data_format": "honeywell.quantum-results.v1",
|
|
239
|
-
"is_default":
|
|
264
|
+
"is_default": False,
|
|
240
265
|
}
|
|
241
266
|
|
|
242
267
|
def _translate_input(self, circuit):
|
|
@@ -12,26 +12,6 @@ from .backend import AzureQirBackend
|
|
|
12
12
|
from qiskit.providers.models import BackendConfiguration
|
|
13
13
|
from qiskit.providers import Options, Provider
|
|
14
14
|
|
|
15
|
-
QIR_BASIS_GATES = [
|
|
16
|
-
"measure",
|
|
17
|
-
"m",
|
|
18
|
-
"cx",
|
|
19
|
-
"cz",
|
|
20
|
-
"h",
|
|
21
|
-
"reset",
|
|
22
|
-
"rx",
|
|
23
|
-
"ry",
|
|
24
|
-
"rz",
|
|
25
|
-
"s",
|
|
26
|
-
"sdg,"
|
|
27
|
-
"t",
|
|
28
|
-
"tdg",
|
|
29
|
-
"x",
|
|
30
|
-
"y",
|
|
31
|
-
"z",
|
|
32
|
-
"id",
|
|
33
|
-
]
|
|
34
|
-
|
|
35
15
|
if TYPE_CHECKING:
|
|
36
16
|
from azure.quantum.qiskit import AzureQuantumProvider
|
|
37
17
|
|
|
@@ -85,7 +65,7 @@ class RigettiSimulatorBackend(RigettiBackend):
|
|
|
85
65
|
"local": False,
|
|
86
66
|
"coupling_map": None,
|
|
87
67
|
"description": "Rigetti simulator on Azure Quantum",
|
|
88
|
-
"basis_gates":
|
|
68
|
+
"basis_gates": self._basis_gates(),
|
|
89
69
|
"memory": True,
|
|
90
70
|
"n_qubits": RigettiTarget.num_qubits(name),
|
|
91
71
|
"conditional": False,
|
|
@@ -117,7 +97,7 @@ class RigettiQPUBackend(RigettiBackend):
|
|
|
117
97
|
"local": False,
|
|
118
98
|
"coupling_map": None,
|
|
119
99
|
"description": "Rigetti QPU on Azure Quantum",
|
|
120
|
-
"basis_gates":
|
|
100
|
+
"basis_gates": self._basis_gates(),
|
|
121
101
|
"memory": True,
|
|
122
102
|
"n_qubits": RigettiTarget.num_qubits(name),
|
|
123
103
|
"conditional": False,
|
azure/quantum/qiskit/job.py
CHANGED
|
@@ -312,6 +312,26 @@ class AzureQuantumJob(JobV1):
|
|
|
312
312
|
entry_point_names.append(entry_point["entryPoint"])
|
|
313
313
|
return entry_point_names if len(entry_point_names) > 0 else ["main"]
|
|
314
314
|
|
|
315
|
+
def _get_headers(self):
|
|
316
|
+
headers = self._azure_job.details.metadata
|
|
317
|
+
if (not isinstance(headers, list)):
|
|
318
|
+
headers = [headers]
|
|
319
|
+
|
|
320
|
+
# 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
|
|
321
|
+
def tryParseJSON(header):
|
|
322
|
+
try:
|
|
323
|
+
json_object = json.loads(header)
|
|
324
|
+
except ValueError as e:
|
|
325
|
+
return header
|
|
326
|
+
return json_object
|
|
327
|
+
|
|
328
|
+
for header in headers:
|
|
329
|
+
del header['qiskit'] # we throw out the qiskit header as it is implied
|
|
330
|
+
for key in header.keys():
|
|
331
|
+
header[key] = tryParseJSON(header[key])
|
|
332
|
+
return headers
|
|
333
|
+
|
|
334
|
+
|
|
315
335
|
def _format_microsoft_v2_results(self) -> List[Dict[str, Any]]:
|
|
316
336
|
success = self._azure_job.details.status == "Succeeded"
|
|
317
337
|
|
|
@@ -326,10 +346,15 @@ class AzureQuantumJob(JobV1):
|
|
|
326
346
|
entry_point_names = self._get_entry_point_names()
|
|
327
347
|
|
|
328
348
|
results = self._translate_microsoft_v2_results()
|
|
329
|
-
|
|
349
|
+
|
|
330
350
|
if len(results) != len(entry_point_names):
|
|
331
|
-
raise ValueError("The number of experiment results does not match the number of
|
|
351
|
+
raise ValueError("The number of experiment results does not match the number of entry point names")
|
|
352
|
+
|
|
353
|
+
headers = self._get_headers()
|
|
332
354
|
|
|
355
|
+
if len(results) != len(headers):
|
|
356
|
+
raise ValueError("The number of experiment results does not match the number of headers")
|
|
357
|
+
|
|
333
358
|
status = self.status()
|
|
334
359
|
|
|
335
360
|
return [{
|
|
@@ -338,7 +363,5 @@ class AzureQuantumJob(JobV1):
|
|
|
338
363
|
"shots": total_count,
|
|
339
364
|
"name": name,
|
|
340
365
|
"status": status,
|
|
341
|
-
"header":
|
|
342
|
-
|
|
343
|
-
}
|
|
344
|
-
} for name, (total_count, result) in zip(entry_point_names, results)]
|
|
366
|
+
"header": header
|
|
367
|
+
} for name, (total_count, result), header in zip(entry_point_names, results, headers)]
|
azure/quantum/qiskit/provider.py
CHANGED
|
@@ -129,9 +129,9 @@ see https://aka.ms/AQ/Docs/AddProvider"
|
|
|
129
129
|
filtered_backends,
|
|
130
130
|
)
|
|
131
131
|
)
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
if len(default_backends) > 0:
|
|
132
|
+
# If default backends were found - return them, otherwise return the filtered_backends collection.
|
|
133
|
+
# The latter case could happen where there's no default backend defined for the specified target.
|
|
134
|
+
if len(default_backends) > 0:
|
|
135
135
|
return default_backends
|
|
136
136
|
|
|
137
137
|
return filtered_backends
|
|
@@ -277,7 +277,7 @@ see https://aka.ms/AQ/Docs/AddProvider"
|
|
|
277
277
|
warnings.warn(
|
|
278
278
|
f"Specified filters {unknown_filters} are not supported by the available backends."
|
|
279
279
|
)
|
|
280
|
-
|
|
280
|
+
|
|
281
281
|
backends = list(filter(filters, backends))
|
|
282
282
|
|
|
283
283
|
return backends
|
azure/quantum/target/ionq.py
CHANGED
|
@@ -12,6 +12,7 @@ from azure.quantum.target.target import (
|
|
|
12
12
|
from azure.quantum.job.job import Job
|
|
13
13
|
from azure.quantum.workspace import Workspace
|
|
14
14
|
from azure.quantum._client.models import CostEstimate, UsageEvent
|
|
15
|
+
from typing import Union
|
|
15
16
|
|
|
16
17
|
COST_1QUBIT_GATE_MAP = {
|
|
17
18
|
"ionq.simulator" : 0.0,
|
|
@@ -123,7 +124,7 @@ class IonQ(Target):
|
|
|
123
124
|
|
|
124
125
|
def estimate_cost(
|
|
125
126
|
self,
|
|
126
|
-
circuit: Dict[str, Any],
|
|
127
|
+
circuit: Union[Dict[str, Any], Any],
|
|
127
128
|
num_shots: int = None,
|
|
128
129
|
price_1q: float = None,
|
|
129
130
|
price_2q: float = None,
|
|
@@ -174,20 +175,6 @@ class IonQ(Target):
|
|
|
174
175
|
)
|
|
175
176
|
shots = num_shots
|
|
176
177
|
|
|
177
|
-
def is_1q_gate(gate: Dict[str, Any]):
|
|
178
|
-
return "controls" not in gate and "control" not in gate
|
|
179
|
-
|
|
180
|
-
def is_multi_q_gate(gate):
|
|
181
|
-
return "controls" in gate or "control" in gate
|
|
182
|
-
|
|
183
|
-
def num_2q_gates(gate):
|
|
184
|
-
controls = gate.get("controls")
|
|
185
|
-
if controls is None or len(controls) == 1:
|
|
186
|
-
# Only one control qubit
|
|
187
|
-
return 1
|
|
188
|
-
# Multiple control qubits
|
|
189
|
-
return 6 * (len(controls) - 2)
|
|
190
|
-
|
|
191
178
|
# Get the costs for the gates depending on the provider if not specified
|
|
192
179
|
if price_1q is None:
|
|
193
180
|
price_1q = COST_1QUBIT_GATE_MAP[self.name]
|
|
@@ -198,10 +185,28 @@ class IonQ(Target):
|
|
|
198
185
|
if min_price is None:
|
|
199
186
|
min_price = MIN_PRICE_MAP[self.name]
|
|
200
187
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
188
|
+
if (isinstance(circuit, Dict)):
|
|
189
|
+
def is_1q_gate(gate: Dict[str, Any]):
|
|
190
|
+
return "controls" not in gate and "control" not in gate
|
|
191
|
+
|
|
192
|
+
def is_multi_q_gate(gate):
|
|
193
|
+
return "controls" in gate or "control" in gate
|
|
194
|
+
|
|
195
|
+
def num_2q_gates(gate):
|
|
196
|
+
controls = gate.get("controls")
|
|
197
|
+
if controls is None or len(controls) == 1:
|
|
198
|
+
# Only one control qubit
|
|
199
|
+
return 1
|
|
200
|
+
# Multiple control qubits
|
|
201
|
+
return 6 * (len(controls) - 2)
|
|
202
|
+
|
|
203
|
+
gates = circuit.get("circuit", [])
|
|
204
|
+
N_1q = sum(map(is_1q_gate, gates))
|
|
205
|
+
N_2q = sum(map(num_2q_gates, filter(is_multi_q_gate, gates)))
|
|
206
|
+
|
|
207
|
+
else:
|
|
208
|
+
N_1q, N_2q, _ = Target._calculate_qir_module_gate_stats(circuit)
|
|
209
|
+
|
|
205
210
|
price = (price_1q * N_1q + price_2q * N_2q) * shots
|
|
206
211
|
price = max(price, min_price)
|
|
207
212
|
|
|
@@ -12,6 +12,7 @@ from azure.quantum.target.target import (
|
|
|
12
12
|
from azure.quantum.job.job import Job
|
|
13
13
|
from azure.quantum.workspace import Workspace
|
|
14
14
|
from azure.quantum._client.models import CostEstimate, UsageEvent
|
|
15
|
+
from typing import Union
|
|
15
16
|
|
|
16
17
|
|
|
17
18
|
class Quantinuum(Target):
|
|
@@ -98,7 +99,7 @@ class Quantinuum(Target):
|
|
|
98
99
|
|
|
99
100
|
def estimate_cost(
|
|
100
101
|
self,
|
|
101
|
-
circuit: str = None,
|
|
102
|
+
circuit: Union[str, Any] = None,
|
|
102
103
|
num_shots: int = None,
|
|
103
104
|
N_1q: int = None,
|
|
104
105
|
N_2q: int = None,
|
|
@@ -144,30 +145,35 @@ class Quantinuum(Target):
|
|
|
144
145
|
)
|
|
145
146
|
shots = num_shots
|
|
146
147
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
"
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
148
|
+
# If we use passthrough, else assume QIR
|
|
149
|
+
if (isinstance(circuit, str)):
|
|
150
|
+
if circuit is not None and (N_1q is None or N_2q is None or N_m is None):
|
|
151
|
+
try:
|
|
152
|
+
from qiskit.qasm2 import loads
|
|
153
|
+
from qiskit.converters.circuit_to_dag import circuit_to_dag
|
|
154
|
+
|
|
155
|
+
except ImportError:
|
|
156
|
+
raise ImportError(
|
|
157
|
+
"Missing dependency qiskit. Please run `pip install azure-quantum[qiskit]` " \
|
|
158
|
+
"to estimate the circuit cost. Alternatively, specify the number of one-qubit and two-qubit " \
|
|
159
|
+
"gates in the method input arguments.")
|
|
160
|
+
|
|
161
|
+
else:
|
|
162
|
+
from qiskit.dagcircuit.dagnode import DAGOpNode
|
|
163
|
+
circuit_obj = loads(string=circuit)
|
|
164
|
+
dag = circuit_to_dag(circuit=circuit_obj)
|
|
165
|
+
N_1q, N_2q, N_m = 0, 0, 0
|
|
166
|
+
for node in dag._multi_graph.nodes():
|
|
167
|
+
if isinstance(node, DAGOpNode):
|
|
168
|
+
if node.op.name in ["measure", "reset"]:
|
|
169
|
+
N_m += 1
|
|
170
|
+
elif node.op.num_qubits == 1:
|
|
171
|
+
N_1q += 1
|
|
172
|
+
else:
|
|
173
|
+
N_2q += 1
|
|
174
|
+
else:
|
|
175
|
+
N_1q, N_2q, N_m = Target._calculate_qir_module_gate_stats(circuit)
|
|
176
|
+
|
|
171
177
|
|
|
172
178
|
import re
|
|
173
179
|
is_emulator_regex = re.compile("^.*(-sim|-[0-9]*e)$")
|
azure/quantum/target/target.py
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
# Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
3
|
# Licensed under the MIT License.
|
|
4
4
|
##
|
|
5
|
-
from typing import TYPE_CHECKING, Any, Dict, Optional, Union, Type, Protocol, runtime_checkable
|
|
5
|
+
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union, Type, Protocol, runtime_checkable
|
|
6
|
+
from dataclasses import dataclass
|
|
6
7
|
import io
|
|
7
8
|
import json
|
|
8
9
|
import abc
|
|
@@ -26,6 +27,14 @@ class QirRepresentable(Protocol):
|
|
|
26
27
|
def _repr_qir_(self, **kwargs: Any) -> bytes:
|
|
27
28
|
raise NotImplementedError
|
|
28
29
|
|
|
30
|
+
@dataclass
|
|
31
|
+
class GateStats:
|
|
32
|
+
one_qubit_gates: int
|
|
33
|
+
multi_qubit_gates: int
|
|
34
|
+
measurement_gates: int
|
|
35
|
+
|
|
36
|
+
def __iter__(self):
|
|
37
|
+
return iter((self.one_qubit_gates, self.multi_qubit_gates, self.measurement_gates))
|
|
29
38
|
|
|
30
39
|
class Target(abc.ABC, SessionHost):
|
|
31
40
|
|
|
@@ -327,6 +336,57 @@ target '{self.name}' of provider '{self.provider_id}' not found."
|
|
|
327
336
|
def _get_azure_provider_id(self) -> str:
|
|
328
337
|
return self.provider_id
|
|
329
338
|
|
|
339
|
+
@classmethod
|
|
340
|
+
def _calculate_qir_module_gate_stats(self, qir_module) -> GateStats:
|
|
341
|
+
try:
|
|
342
|
+
from pyqir import Module, is_qubit_type, is_result_type, entry_point, is_entry_point, Function
|
|
343
|
+
|
|
344
|
+
except ImportError:
|
|
345
|
+
raise ImportError(
|
|
346
|
+
"Missing optional 'qiskit' dependencies. \
|
|
347
|
+
To install run: pip install azure-quantum[qiskit]"
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
module: Module = qir_module
|
|
351
|
+
|
|
352
|
+
one_qubit_gates = 0
|
|
353
|
+
multi_qubit_gates = 0
|
|
354
|
+
measurement_gates = 0
|
|
355
|
+
|
|
356
|
+
function_entry_points: list[Function] = filter(is_entry_point, module.functions)
|
|
357
|
+
|
|
358
|
+
# Iterate over the blocks and their instructions
|
|
359
|
+
for function in function_entry_points:
|
|
360
|
+
for block in function.basic_blocks:
|
|
361
|
+
for instruction in block.instructions:
|
|
362
|
+
qubit_count = 0
|
|
363
|
+
result_count = 0
|
|
364
|
+
|
|
365
|
+
# If the instruction is of type quantum rt, do not include this is the price calculation
|
|
366
|
+
if len(instruction.operands) > 0 and "__quantum__rt" not in instruction.operands[-1].name:
|
|
367
|
+
# Check each operand in the instruction
|
|
368
|
+
for operand in instruction.operands:
|
|
369
|
+
value_type = operand.type
|
|
370
|
+
|
|
371
|
+
if is_qubit_type(value_type):
|
|
372
|
+
qubit_count += 1
|
|
373
|
+
elif is_result_type(value_type):
|
|
374
|
+
result_count += 1
|
|
375
|
+
|
|
376
|
+
# Determine the type of gate based on the counts
|
|
377
|
+
if qubit_count == 1 and result_count == 0:
|
|
378
|
+
one_qubit_gates += 1
|
|
379
|
+
if qubit_count >= 2 and result_count == 0:
|
|
380
|
+
multi_qubit_gates += 1
|
|
381
|
+
if result_count > 0:
|
|
382
|
+
measurement_gates += 1
|
|
383
|
+
|
|
384
|
+
return GateStats (
|
|
385
|
+
one_qubit_gates,
|
|
386
|
+
multi_qubit_gates,
|
|
387
|
+
measurement_gates
|
|
388
|
+
)
|
|
389
|
+
|
|
330
390
|
|
|
331
391
|
def _determine_shots_or_deprecated_num_shots(
|
|
332
392
|
shots: int = None,
|
azure/quantum/version.py
CHANGED
|
@@ -2,7 +2,7 @@ azure/quantum/__init__.py,sha256=Za8xZY4lzFkW8m4ero-bqrfN437D2NRukM77ukb4GPM,508
|
|
|
2
2
|
azure/quantum/_constants.py,sha256=nDL_QrGdI_Zz_cvTB9nVgfE7J6A_Boo1ollMYqsiEBs,3499
|
|
3
3
|
azure/quantum/_workspace_connection_params.py,sha256=KoT90U89Dj6pVwAKp_ENJL1hyTF0oQe7w0QioOGvjXg,21685
|
|
4
4
|
azure/quantum/storage.py,sha256=_4bMniDk9LrB_K5CQwuCivJFZXdmhRvU2b6Z3xxXw9I,12556
|
|
5
|
-
azure/quantum/version.py,sha256=
|
|
5
|
+
azure/quantum/version.py,sha256=gvTDAq8hnfvdel4jCNKOp7qTOApD18CJ8O1YB4iuuaE,240
|
|
6
6
|
azure/quantum/workspace.py,sha256=1__iZTe59ozAsAGJ4nECxmk_211Dm8ALJi-MFIdrg4o,23438
|
|
7
7
|
azure/quantum/_authentication/__init__.py,sha256=bniNZlS0hMIjO_y7DevGBAS6MixyA5pbPHcdGipUWM4,236
|
|
8
8
|
azure/quantum/_authentication/_chained.py,sha256=0rdohB_fVGFHUhlly9sGxqQTBTZGpGxtlBqNHDFbAqE,4848
|
|
@@ -14,7 +14,7 @@ azure/quantum/_client/_configuration.py,sha256=5uktKtZxoVVAoSyeL0VNGS9AfPERp-9rU
|
|
|
14
14
|
azure/quantum/_client/_patch.py,sha256=YTV6yZ9bRfBBaw2z7v4MdzR-zeHkdtKkGb4SU8C25mE,694
|
|
15
15
|
azure/quantum/_client/_serialization.py,sha256=KJSS6KWgnKcz-cENQCmWZ9Ziv303lnBbLwFIpYZeKFU,81097
|
|
16
16
|
azure/quantum/_client/_vendor.py,sha256=h8ByiyZ4cCQyFxqnuhTQdv1Rms3dVjKsrgZDzwMcSJ0,996
|
|
17
|
-
azure/quantum/_client/_version.py,sha256=
|
|
17
|
+
azure/quantum/_client/_version.py,sha256=3O6TxldW9qCVAZrcwc9lIYAg9p9_625appTdZwUDvmI,500
|
|
18
18
|
azure/quantum/_client/models/__init__.py,sha256=c1PRpzNsQTcDk4GkrFMMIlwNQQa2c0p5N0Lzd-23YBA,2100
|
|
19
19
|
azure/quantum/_client/models/_enums.py,sha256=omj_B8_E8ONzTHg5hLgDlFYibRRbdr9sEN298im_otA,2977
|
|
20
20
|
azure/quantum/_client/models/_models.py,sha256=wktCM5oBVfwQetNoHobL1wNsC3knXV-HmqBq_Q77Kw4,41810
|
|
@@ -41,22 +41,22 @@ azure/quantum/job/session.py,sha256=EEJVKEEB5g0yyH963aaR0GY0Cd0axrX-49gwDWxBcfE,
|
|
|
41
41
|
azure/quantum/job/workspace_item.py,sha256=lyBIJCtUfIZMGJYJkX7Se8IDnXhXe4JU0RnqzSuhhI4,1380
|
|
42
42
|
azure/quantum/job/workspace_item_factory.py,sha256=QRWyrtgcKZqUucJOFi9V_SYMV3lj6S74tGRrPtk3NE0,1200
|
|
43
43
|
azure/quantum/qiskit/__init__.py,sha256=gjKsmRwtVNcbbsuOvy2wT0ASELh5NXGmuwaEwjZcVQo,314
|
|
44
|
-
azure/quantum/qiskit/job.py,sha256=
|
|
45
|
-
azure/quantum/qiskit/provider.py,sha256=
|
|
44
|
+
azure/quantum/qiskit/job.py,sha256=zrm1HHW_9A9DG8XFO-y7xDZtsbuS64C8etFaF8XWotE,14888
|
|
45
|
+
azure/quantum/qiskit/provider.py,sha256=kbZJgDX-hI-AD5rQfik4JHNnNd74oyfrKYauosqBRxw,10966
|
|
46
46
|
azure/quantum/qiskit/backends/__init__.py,sha256=DBC6GgYa5Y0p5xu5ADqlOXs7zp8_RDTJ86qKhuzXfG0,1118
|
|
47
|
-
azure/quantum/qiskit/backends/backend.py,sha256=
|
|
48
|
-
azure/quantum/qiskit/backends/ionq.py,sha256=
|
|
47
|
+
azure/quantum/qiskit/backends/backend.py,sha256=nj4gYDPxp91uA_JfStnpJFU7hSw15Nsz8--9wmHFMXc,22024
|
|
48
|
+
azure/quantum/qiskit/backends/ionq.py,sha256=PqFdNlzIeEABrMNf34H0S-iNW6YEbi2kFqie1eKxUg8,14406
|
|
49
49
|
azure/quantum/qiskit/backends/microsoft.py,sha256=7Nw-z8KoIBOBXbP8aqhmhAeW9TENrwwkZk6LD31M9F0,3478
|
|
50
|
-
azure/quantum/qiskit/backends/qci.py,sha256=
|
|
51
|
-
azure/quantum/qiskit/backends/quantinuum.py,sha256=
|
|
52
|
-
azure/quantum/qiskit/backends/rigetti.py,sha256=
|
|
50
|
+
azure/quantum/qiskit/backends/qci.py,sha256=_t6IVUGe4LXzXSLLADwpvic2JTNxOSCsRl0s4zx3K7c,4656
|
|
51
|
+
azure/quantum/qiskit/backends/quantinuum.py,sha256=bwmhcZEnj71LMWT31six-WnZJ_b-YqP0VxzCZwnsa9g,13910
|
|
52
|
+
azure/quantum/qiskit/backends/rigetti.py,sha256=3XYr5C37MhGH6fm7LHX3UQ6BGYlSN9-q61Xd8WHEX5U,4112
|
|
53
53
|
azure/quantum/qiskit/results/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
54
54
|
azure/quantum/qiskit/results/resource_estimator.py,sha256=pAP5ZpVxdzNekQ_qXfSdSKYWWuqL_Vq7WK6pWU4wbEQ,814
|
|
55
55
|
azure/quantum/target/__init__.py,sha256=F0nIEJdlbczmxAdtvPDWf8J1Y33_XjPzloliHeWpJos,689
|
|
56
|
-
azure/quantum/target/ionq.py,sha256=
|
|
56
|
+
azure/quantum/target/ionq.py,sha256=cJ7jZHN8HSLvlxCSY6hNHf1dFog40PmVLfJ_PT5eVo4,8068
|
|
57
57
|
azure/quantum/target/params.py,sha256=oI-35HUEMCskNjpxCJU3tjL664K-TxqAg5LA5xU0nso,9130
|
|
58
|
-
azure/quantum/target/quantinuum.py,sha256=
|
|
59
|
-
azure/quantum/target/target.py,sha256=
|
|
58
|
+
azure/quantum/target/quantinuum.py,sha256=Fe84Mf927Bd4UrebOa0qx4qOH7SqOAlFE6zR42ioYec,7937
|
|
59
|
+
azure/quantum/target/target.py,sha256=GChB4kzTlETgLHlH8zIMPJX5W5m1GoOkrNqiCHw3R70,16281
|
|
60
60
|
azure/quantum/target/target_factory.py,sha256=jjZ9zxWaNDkcvslKV02GSP8rtXwStJ7EmVXQSOFh_j8,5266
|
|
61
61
|
azure/quantum/target/microsoft/__init__.py,sha256=36kM2YlWv69AzAfUA5wMdWyYRSaCMwX2Ajhffpzx67g,570
|
|
62
62
|
azure/quantum/target/microsoft/job.py,sha256=GM4OA-rxFUqQzsH8V59pVc4BmBaPYvd99E26pyPwxto,1249
|
|
@@ -72,7 +72,7 @@ azure/quantum/target/pasqal/target.py,sha256=DMt2uYeCeaUFh7vlnf687VOoRStXCWL-6Mk
|
|
|
72
72
|
azure/quantum/target/rigetti/__init__.py,sha256=I1vyzZBYGI540pauTqJd0RSSyTShGqkEL7Yjo25_RNY,378
|
|
73
73
|
azure/quantum/target/rigetti/result.py,sha256=65mtAZxfdNrTWNjWPqgfwt2BZN6Nllo4g_bls7-Nm68,2334
|
|
74
74
|
azure/quantum/target/rigetti/target.py,sha256=wL2RkOHKUT7YL6dHBvlO1UiKm1fhnehS3SSGCcarwf8,7048
|
|
75
|
-
azure_quantum-2.
|
|
76
|
-
azure_quantum-2.
|
|
77
|
-
azure_quantum-2.
|
|
78
|
-
azure_quantum-2.
|
|
75
|
+
azure_quantum-2.2.0.dev1.dist-info/METADATA,sha256=QTBJKwgR1zu0oGSf_SCztSS9OPAA9TlobfsWEmkHl5w,7344
|
|
76
|
+
azure_quantum-2.2.0.dev1.dist-info/WHEEL,sha256=eOLhNAGa2EW3wWl_TU484h7q1UNgy0JXjjoqKoxAAQc,92
|
|
77
|
+
azure_quantum-2.2.0.dev1.dist-info/top_level.txt,sha256=S7DhWV9m80TBzAhOFjxDUiNbKszzoThbnrSz5MpbHSQ,6
|
|
78
|
+
azure_quantum-2.2.0.dev1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|