azure-quantum 2.1.1__py3-none-any.whl → 2.2.0__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 +149 -37
- azure/quantum/qiskit/backends/ionq.py +11 -38
- azure/quantum/qiskit/backends/microsoft.py +62 -27
- azure/quantum/qiskit/backends/qci.py +10 -27
- azure/quantum/qiskit/backends/quantinuum.py +12 -7
- azure/quantum/qiskit/backends/rigetti.py +4 -23
- azure/quantum/qiskit/job.py +37 -10
- azure/quantum/qiskit/provider.py +4 -4
- azure/quantum/target/ionq.py +32 -22
- azure/quantum/target/microsoft/target.py +15 -7
- azure/quantum/target/pasqal/target.py +7 -1
- azure/quantum/target/quantinuum.py +37 -27
- azure/quantum/target/rigetti/target.py +13 -6
- azure/quantum/target/target.py +77 -4
- azure/quantum/version.py +1 -1
- {azure_quantum-2.1.1.dist-info → azure_quantum-2.2.0.dist-info}/METADATA +5 -5
- {azure_quantum-2.1.1.dist-info → azure_quantum-2.2.0.dist-info}/RECORD +20 -20
- {azure_quantum-2.1.1.dist-info → azure_quantum-2.2.0.dist-info}/WHEEL +0 -0
- {azure_quantum-2.1.1.dist-info → azure_quantum-2.2.0.dist-info}/top_level.txt +0 -0
|
@@ -11,26 +11,7 @@ from .backend import AzureQirBackend
|
|
|
11
11
|
|
|
12
12
|
from qiskit.providers.models import BackendConfiguration
|
|
13
13
|
from qiskit.providers import Options, Provider
|
|
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
|
-
]
|
|
14
|
+
from qsharp import TargetProfile
|
|
34
15
|
|
|
35
16
|
if TYPE_CHECKING:
|
|
36
17
|
from azure.quantum.qiskit import AzureQuantumProvider
|
|
@@ -60,7 +41,7 @@ class RigettiBackend(AzureQirBackend):
|
|
|
60
41
|
other_options = {
|
|
61
42
|
cls._SHOTS_PARAM_NAME: _DEFAULT_SHOTS_COUNT,
|
|
62
43
|
}
|
|
63
|
-
return Options(
|
|
44
|
+
return Options(target_profile=TargetProfile.Base, **other_options)
|
|
64
45
|
|
|
65
46
|
def _azure_config(self) -> Dict[str, str]:
|
|
66
47
|
config = super()._azure_config()
|
|
@@ -85,7 +66,7 @@ class RigettiSimulatorBackend(RigettiBackend):
|
|
|
85
66
|
"local": False,
|
|
86
67
|
"coupling_map": None,
|
|
87
68
|
"description": "Rigetti simulator on Azure Quantum",
|
|
88
|
-
"basis_gates":
|
|
69
|
+
"basis_gates": self._basis_gates(),
|
|
89
70
|
"memory": True,
|
|
90
71
|
"n_qubits": RigettiTarget.num_qubits(name),
|
|
91
72
|
"conditional": False,
|
|
@@ -117,7 +98,7 @@ class RigettiQPUBackend(RigettiBackend):
|
|
|
117
98
|
"local": False,
|
|
118
99
|
"coupling_map": None,
|
|
119
100
|
"description": "Rigetti QPU on Azure Quantum",
|
|
120
|
-
"basis_gates":
|
|
101
|
+
"basis_gates": self._basis_gates(),
|
|
121
102
|
"memory": True,
|
|
122
103
|
"n_qubits": RigettiTarget.num_qubits(name),
|
|
123
104
|
"conditional": False,
|
azure/quantum/qiskit/job.py
CHANGED
|
@@ -212,15 +212,19 @@ class AzureQuantumJob(JobV1):
|
|
|
212
212
|
|
|
213
213
|
@staticmethod
|
|
214
214
|
def _qir_to_qiskit_bitstring(obj):
|
|
215
|
-
"""Convert the data structure from Azure into the "schema" used by Qiskit
|
|
215
|
+
"""Convert the data structure from Azure into the "schema" used by Qiskit"""
|
|
216
216
|
if isinstance(obj, str) and not re.match(r"[\d\s]+$", obj):
|
|
217
217
|
obj = ast.literal_eval(obj)
|
|
218
218
|
|
|
219
219
|
if isinstance(obj, tuple):
|
|
220
220
|
# the outermost implied container is a tuple, and each item is
|
|
221
|
-
# associated with a classical register.
|
|
222
|
-
|
|
223
|
-
|
|
221
|
+
# associated with a classical register.
|
|
222
|
+
return " ".join(
|
|
223
|
+
[
|
|
224
|
+
AzureQuantumJob._qir_to_qiskit_bitstring(term)
|
|
225
|
+
for term in obj
|
|
226
|
+
]
|
|
227
|
+
)
|
|
224
228
|
elif isinstance(obj, list):
|
|
225
229
|
# a list is for an individual classical register
|
|
226
230
|
return "".join([str(bit) for bit in obj])
|
|
@@ -312,6 +316,26 @@ class AzureQuantumJob(JobV1):
|
|
|
312
316
|
entry_point_names.append(entry_point["entryPoint"])
|
|
313
317
|
return entry_point_names if len(entry_point_names) > 0 else ["main"]
|
|
314
318
|
|
|
319
|
+
def _get_headers(self):
|
|
320
|
+
headers = self._azure_job.details.metadata
|
|
321
|
+
if (not isinstance(headers, list)):
|
|
322
|
+
headers = [headers]
|
|
323
|
+
|
|
324
|
+
# 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
|
|
325
|
+
def tryParseJSON(header):
|
|
326
|
+
try:
|
|
327
|
+
json_object = json.loads(header)
|
|
328
|
+
except ValueError as e:
|
|
329
|
+
return header
|
|
330
|
+
return json_object
|
|
331
|
+
|
|
332
|
+
for header in headers:
|
|
333
|
+
del header['qiskit'] # we throw out the qiskit header as it is implied
|
|
334
|
+
for key in header.keys():
|
|
335
|
+
header[key] = tryParseJSON(header[key])
|
|
336
|
+
return headers
|
|
337
|
+
|
|
338
|
+
|
|
315
339
|
def _format_microsoft_v2_results(self) -> List[Dict[str, Any]]:
|
|
316
340
|
success = self._azure_job.details.status == "Succeeded"
|
|
317
341
|
|
|
@@ -326,10 +350,15 @@ class AzureQuantumJob(JobV1):
|
|
|
326
350
|
entry_point_names = self._get_entry_point_names()
|
|
327
351
|
|
|
328
352
|
results = self._translate_microsoft_v2_results()
|
|
329
|
-
|
|
353
|
+
|
|
330
354
|
if len(results) != len(entry_point_names):
|
|
331
|
-
raise ValueError("The number of experiment results does not match the number of
|
|
355
|
+
raise ValueError("The number of experiment results does not match the number of entry point names")
|
|
356
|
+
|
|
357
|
+
headers = self._get_headers()
|
|
332
358
|
|
|
359
|
+
if len(results) != len(headers):
|
|
360
|
+
raise ValueError("The number of experiment results does not match the number of headers")
|
|
361
|
+
|
|
333
362
|
status = self.status()
|
|
334
363
|
|
|
335
364
|
return [{
|
|
@@ -338,7 +367,5 @@ class AzureQuantumJob(JobV1):
|
|
|
338
367
|
"shots": total_count,
|
|
339
368
|
"name": name,
|
|
340
369
|
"status": status,
|
|
341
|
-
"header":
|
|
342
|
-
|
|
343
|
-
}
|
|
344
|
-
} for name, (total_count, result) in zip(entry_point_names, results)]
|
|
370
|
+
"header": header
|
|
371
|
+
} 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,
|
|
@@ -58,12 +59,16 @@ class IonQ(Target):
|
|
|
58
59
|
name: str = "ionq.simulator",
|
|
59
60
|
input_data_format: str = "ionq.circuit.v1",
|
|
60
61
|
output_data_format: str = "ionq.quantum-results.v1",
|
|
61
|
-
capability: str = "
|
|
62
|
+
capability: str = "",
|
|
62
63
|
provider_id: str = "IonQ",
|
|
63
64
|
content_type: str = "application/json",
|
|
64
65
|
encoding: str = "",
|
|
65
|
-
|
|
66
|
+
target_profile: Union[str, "TargetProfile"] = "Base",
|
|
67
|
+
**kwargs,
|
|
66
68
|
):
|
|
69
|
+
if capability:
|
|
70
|
+
msg = "The 'capability' parameter is not used for the Quantinuum target."
|
|
71
|
+
warn(msg, DeprecationWarning)
|
|
67
72
|
super().__init__(
|
|
68
73
|
workspace=workspace,
|
|
69
74
|
name=name,
|
|
@@ -73,7 +78,8 @@ class IonQ(Target):
|
|
|
73
78
|
provider_id=provider_id,
|
|
74
79
|
content_type=content_type,
|
|
75
80
|
encoding=encoding,
|
|
76
|
-
|
|
81
|
+
target_profile=target_profile,
|
|
82
|
+
**kwargs,
|
|
77
83
|
)
|
|
78
84
|
|
|
79
85
|
def submit(
|
|
@@ -123,7 +129,7 @@ class IonQ(Target):
|
|
|
123
129
|
|
|
124
130
|
def estimate_cost(
|
|
125
131
|
self,
|
|
126
|
-
circuit: Dict[str, Any],
|
|
132
|
+
circuit: Union[Dict[str, Any], Any],
|
|
127
133
|
num_shots: int = None,
|
|
128
134
|
price_1q: float = None,
|
|
129
135
|
price_2q: float = None,
|
|
@@ -174,20 +180,6 @@ class IonQ(Target):
|
|
|
174
180
|
)
|
|
175
181
|
shots = num_shots
|
|
176
182
|
|
|
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
183
|
# Get the costs for the gates depending on the provider if not specified
|
|
192
184
|
if price_1q is None:
|
|
193
185
|
price_1q = COST_1QUBIT_GATE_MAP[self.name]
|
|
@@ -198,10 +190,28 @@ class IonQ(Target):
|
|
|
198
190
|
if min_price is None:
|
|
199
191
|
min_price = MIN_PRICE_MAP[self.name]
|
|
200
192
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
193
|
+
if (isinstance(circuit, Dict)):
|
|
194
|
+
def is_1q_gate(gate: Dict[str, Any]):
|
|
195
|
+
return "controls" not in gate and "control" not in gate
|
|
196
|
+
|
|
197
|
+
def is_multi_q_gate(gate):
|
|
198
|
+
return "controls" in gate or "control" in gate
|
|
199
|
+
|
|
200
|
+
def num_2q_gates(gate):
|
|
201
|
+
controls = gate.get("controls")
|
|
202
|
+
if controls is None or len(controls) == 1:
|
|
203
|
+
# Only one control qubit
|
|
204
|
+
return 1
|
|
205
|
+
# Multiple control qubits
|
|
206
|
+
return 6 * (len(controls) - 2)
|
|
207
|
+
|
|
208
|
+
gates = circuit.get("circuit", [])
|
|
209
|
+
N_1q = sum(map(is_1q_gate, gates))
|
|
210
|
+
N_2q = sum(map(num_2q_gates, filter(is_multi_q_gate, gates)))
|
|
211
|
+
|
|
212
|
+
else:
|
|
213
|
+
N_1q, N_2q, _ = Target._calculate_qir_module_gate_stats(circuit)
|
|
214
|
+
|
|
205
215
|
price = (price_1q * N_1q + price_2q * N_2q) * shots
|
|
206
216
|
price = max(price, min_price)
|
|
207
217
|
|
|
@@ -392,6 +392,7 @@ class MicrosoftEstimator(Target):
|
|
|
392
392
|
output_data_format="microsoft.resource-estimates.v1",
|
|
393
393
|
provider_id="microsoft-qc",
|
|
394
394
|
content_type=ContentType.json,
|
|
395
|
+
target_profile="Adaptive_RI",
|
|
395
396
|
**kwargs
|
|
396
397
|
)
|
|
397
398
|
|
|
@@ -422,14 +423,21 @@ class MicrosoftEstimator(Target):
|
|
|
422
423
|
warnings.warn("The 'shots' parameter is ignored in resource estimation job.")
|
|
423
424
|
|
|
424
425
|
try:
|
|
425
|
-
from qiskit import QuantumCircuit
|
|
426
|
-
from
|
|
427
|
-
from
|
|
426
|
+
from qiskit import QuantumCircuit
|
|
427
|
+
from qsharp import TargetProfile
|
|
428
|
+
from qsharp.interop.qiskit import ResourceEstimatorBackend
|
|
429
|
+
from pyqir import Context, Module
|
|
430
|
+
|
|
428
431
|
if isinstance(input_data, QuantumCircuit):
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
432
|
+
backend = ResourceEstimatorBackend()
|
|
433
|
+
target_profile = TargetProfile.from_str(self.target_profile)
|
|
434
|
+
qir_str = backend.qir(input_data, target_profile=target_profile)
|
|
435
|
+
context = Context()
|
|
436
|
+
module = Module.from_ir(context, qir_str)
|
|
437
|
+
|
|
438
|
+
err = module.verify()
|
|
439
|
+
if err is not None:
|
|
440
|
+
raise Exception(err)
|
|
433
441
|
input_data = module.bitcode
|
|
434
442
|
finally:
|
|
435
443
|
return super().submit(
|
|
@@ -80,9 +80,10 @@ class Pasqal(Target):
|
|
|
80
80
|
name: Union[PasqalTarget, str] = PasqalTarget.SIM_EMU_TN,
|
|
81
81
|
input_data_format: str = "pasqal.pulser.v1",
|
|
82
82
|
output_data_format: str = "pasqal.pulser-results.v1",
|
|
83
|
-
capability: str = "
|
|
83
|
+
capability: str = "",
|
|
84
84
|
provider_id: str = "pasqal",
|
|
85
85
|
encoding: str = "",
|
|
86
|
+
target_profile: Union[str, "TargetProfile"] = "Base",
|
|
86
87
|
**kwargs,
|
|
87
88
|
):
|
|
88
89
|
"""
|
|
@@ -102,7 +103,12 @@ class Pasqal(Target):
|
|
|
102
103
|
:type provider_id: str
|
|
103
104
|
:param encoding: "Content-Encoding" attribute value to set on input blob (ex. "gzip")
|
|
104
105
|
:type encoding: str
|
|
106
|
+
:param target_profile: Target QIR profile.
|
|
107
|
+
:type target_profile: str | TargetProfile
|
|
105
108
|
"""
|
|
109
|
+
if capability:
|
|
110
|
+
msg = "The 'capability' parameter is not used for the Quantinuum target."
|
|
111
|
+
warn(msg, DeprecationWarning)
|
|
106
112
|
|
|
107
113
|
super().__init__(
|
|
108
114
|
workspace=workspace,
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# Copyright (c) Microsoft Corporation.
|
|
3
3
|
# Licensed under the MIT License.
|
|
4
4
|
##
|
|
5
|
-
from typing import Any, Dict
|
|
5
|
+
from typing import Any, Dict, Union
|
|
6
6
|
from warnings import warn
|
|
7
7
|
|
|
8
8
|
from azure.quantum.target.target import (
|
|
@@ -34,12 +34,16 @@ class Quantinuum(Target):
|
|
|
34
34
|
name: str = "quantinuum.sim.h1-1sc",
|
|
35
35
|
input_data_format: str = "honeywell.openqasm.v1",
|
|
36
36
|
output_data_format: str = "honeywell.quantum-results.v1",
|
|
37
|
-
capability: str = "
|
|
37
|
+
capability: str = "",
|
|
38
38
|
provider_id: str = "quantinuum",
|
|
39
39
|
content_type: str = "application/qasm",
|
|
40
40
|
encoding: str = "",
|
|
41
|
+
target_profile: Union[str, "TargetProfile"] = "Adaptive_RI",
|
|
41
42
|
**kwargs
|
|
42
43
|
):
|
|
44
|
+
if capability:
|
|
45
|
+
msg = "The 'capability' parameter is not used for the Quantinuum target."
|
|
46
|
+
warn(msg, DeprecationWarning)
|
|
43
47
|
super().__init__(
|
|
44
48
|
workspace=workspace,
|
|
45
49
|
name=name,
|
|
@@ -49,6 +53,7 @@ class Quantinuum(Target):
|
|
|
49
53
|
provider_id=provider_id,
|
|
50
54
|
content_type=content_type,
|
|
51
55
|
encoding=encoding,
|
|
56
|
+
target_profile=target_profile,
|
|
52
57
|
**kwargs
|
|
53
58
|
)
|
|
54
59
|
|
|
@@ -98,7 +103,7 @@ class Quantinuum(Target):
|
|
|
98
103
|
|
|
99
104
|
def estimate_cost(
|
|
100
105
|
self,
|
|
101
|
-
circuit: str = None,
|
|
106
|
+
circuit: Union[str, Any] = None,
|
|
102
107
|
num_shots: int = None,
|
|
103
108
|
N_1q: int = None,
|
|
104
109
|
N_2q: int = None,
|
|
@@ -144,30 +149,35 @@ class Quantinuum(Target):
|
|
|
144
149
|
)
|
|
145
150
|
shots = num_shots
|
|
146
151
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
"
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
152
|
+
# If we use passthrough, else assume QIR
|
|
153
|
+
if (isinstance(circuit, str)):
|
|
154
|
+
if circuit is not None and (N_1q is None or N_2q is None or N_m is None):
|
|
155
|
+
try:
|
|
156
|
+
from qiskit.qasm2 import loads
|
|
157
|
+
from qiskit.converters.circuit_to_dag import circuit_to_dag
|
|
158
|
+
|
|
159
|
+
except ImportError:
|
|
160
|
+
raise ImportError(
|
|
161
|
+
"Missing dependency qiskit. Please run `pip install azure-quantum[qiskit]` " \
|
|
162
|
+
"to estimate the circuit cost. Alternatively, specify the number of one-qubit and two-qubit " \
|
|
163
|
+
"gates in the method input arguments.")
|
|
164
|
+
|
|
165
|
+
else:
|
|
166
|
+
from qiskit.dagcircuit.dagnode import DAGOpNode
|
|
167
|
+
circuit_obj = loads(string=circuit)
|
|
168
|
+
dag = circuit_to_dag(circuit=circuit_obj)
|
|
169
|
+
N_1q, N_2q, N_m = 0, 0, 0
|
|
170
|
+
for node in dag._multi_graph.nodes():
|
|
171
|
+
if isinstance(node, DAGOpNode):
|
|
172
|
+
if node.op.name in ["measure", "reset"]:
|
|
173
|
+
N_m += 1
|
|
174
|
+
elif node.op.num_qubits == 1:
|
|
175
|
+
N_1q += 1
|
|
176
|
+
else:
|
|
177
|
+
N_2q += 1
|
|
178
|
+
else:
|
|
179
|
+
N_1q, N_2q, N_m = Target._calculate_qir_module_gate_stats(circuit)
|
|
180
|
+
|
|
171
181
|
|
|
172
182
|
import re
|
|
173
183
|
is_emulator_regex = re.compile("^.*(-sim|-[0-9]*e)$")
|
|
@@ -14,6 +14,7 @@ __all__ = [
|
|
|
14
14
|
from dataclasses import dataclass
|
|
15
15
|
from enum import Enum
|
|
16
16
|
from typing import Union, Any, Dict, List, Optional
|
|
17
|
+
from warnings import warn
|
|
17
18
|
|
|
18
19
|
from ..target import Target
|
|
19
20
|
from ... import Job
|
|
@@ -29,7 +30,7 @@ class RigettiTarget(str, Enum):
|
|
|
29
30
|
QVM = "rigetti.sim.qvm"
|
|
30
31
|
"""A simulator target for Quil. See https://github.com/quil-lang/qvm for more info."""
|
|
31
32
|
|
|
32
|
-
|
|
33
|
+
ANKAA_9Q_3 = "rigetti.qpu.ankaa-9q-3"
|
|
33
34
|
|
|
34
35
|
def simulators() -> List[str]:
|
|
35
36
|
"""Returns a list of simulator targets"""
|
|
@@ -40,7 +41,7 @@ class RigettiTarget(str, Enum):
|
|
|
40
41
|
def qpus() -> List[str]:
|
|
41
42
|
"""Returns a list of QPU targets"""
|
|
42
43
|
return [
|
|
43
|
-
RigettiTarget.
|
|
44
|
+
RigettiTarget.ANKAA_9Q_3.value,
|
|
44
45
|
]
|
|
45
46
|
|
|
46
47
|
def num_qubits(target_name) -> int:
|
|
@@ -48,8 +49,8 @@ class RigettiTarget(str, Enum):
|
|
|
48
49
|
|
|
49
50
|
if target_name == RigettiTarget.QVM.value:
|
|
50
51
|
return 20
|
|
51
|
-
elif target_name == RigettiTarget.
|
|
52
|
-
return
|
|
52
|
+
elif target_name == RigettiTarget.ANKAA_9Q_3.value:
|
|
53
|
+
return 9
|
|
53
54
|
else:
|
|
54
55
|
raise ValueError(f"Unknown target {target_name}")
|
|
55
56
|
|
|
@@ -137,9 +138,10 @@ class Rigetti(Target):
|
|
|
137
138
|
name: Union[RigettiTarget, str] = RigettiTarget.QVM,
|
|
138
139
|
input_data_format: str = "rigetti.quil.v1",
|
|
139
140
|
output_data_format: str = "rigetti.quil-results.v1",
|
|
140
|
-
capability: str = "
|
|
141
|
+
capability: str = "",
|
|
141
142
|
provider_id: str = "rigetti",
|
|
142
143
|
encoding: str = "",
|
|
144
|
+
target_profile: Union[str, "TargetProfile"] = "Base",
|
|
143
145
|
**kwargs,
|
|
144
146
|
):
|
|
145
147
|
"""
|
|
@@ -159,8 +161,12 @@ class Rigetti(Target):
|
|
|
159
161
|
:type provider_id: str
|
|
160
162
|
:param encoding: "Content-Encoding" attribute value to set on input blob (ex. "gzip")
|
|
161
163
|
:type encoding: str
|
|
164
|
+
:param target_profile: Target QIR profile.
|
|
165
|
+
:type target_profile: str | TargetProfile
|
|
162
166
|
"""
|
|
163
|
-
|
|
167
|
+
if capability:
|
|
168
|
+
msg = "The 'capability' parameter is not used for the Quantinuum target."
|
|
169
|
+
warn(msg, DeprecationWarning)
|
|
164
170
|
super().__init__(
|
|
165
171
|
workspace=workspace,
|
|
166
172
|
name=name,
|
|
@@ -170,6 +176,7 @@ class Rigetti(Target):
|
|
|
170
176
|
provider_id=provider_id,
|
|
171
177
|
content_type="text/plain",
|
|
172
178
|
encoding=encoding,
|
|
179
|
+
target_profile=target_profile,
|
|
173
180
|
**kwargs,
|
|
174
181
|
)
|
|
175
182
|
|
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
|
|
|
@@ -59,7 +68,8 @@ class Target(abc.ABC, SessionHost):
|
|
|
59
68
|
content_type: ContentType = ContentType.json,
|
|
60
69
|
encoding: str = "",
|
|
61
70
|
average_queue_time: Union[float, None] = None,
|
|
62
|
-
current_availability: str = ""
|
|
71
|
+
current_availability: str = "",
|
|
72
|
+
target_profile: Union[str, "TargetProfile"] = "Base",
|
|
63
73
|
):
|
|
64
74
|
"""
|
|
65
75
|
Initializes a new target.
|
|
@@ -72,7 +82,7 @@ class Target(abc.ABC, SessionHost):
|
|
|
72
82
|
:type input_data_format: str
|
|
73
83
|
:param output_data_format: Format of output data (ex. "microsoft.resource-estimates.v1")
|
|
74
84
|
:type output_data_format: str
|
|
75
|
-
:param capability: QIR capability
|
|
85
|
+
:param capability: QIR capability. Deprecated, use `target_profile`
|
|
76
86
|
:type capability: str
|
|
77
87
|
:param provider_id: Id of provider (ex. "microsoft-qc")
|
|
78
88
|
:type provider_id: str
|
|
@@ -84,6 +94,8 @@ class Target(abc.ABC, SessionHost):
|
|
|
84
94
|
:type average_queue_time: float
|
|
85
95
|
:param current_availability: Set current availability (for internal use)
|
|
86
96
|
:type current_availability: str
|
|
97
|
+
:param target_profile: Target QIR profile.
|
|
98
|
+
:type target_profile: str | TargetProfile
|
|
87
99
|
"""
|
|
88
100
|
if not provider_id and "." in name:
|
|
89
101
|
provider_id = name.split(".")[0]
|
|
@@ -97,6 +109,7 @@ class Target(abc.ABC, SessionHost):
|
|
|
97
109
|
self.encoding = encoding
|
|
98
110
|
self._average_queue_time = average_queue_time
|
|
99
111
|
self._current_availability = current_availability
|
|
112
|
+
self.target_profile = target_profile
|
|
100
113
|
|
|
101
114
|
def __repr__(self):
|
|
102
115
|
return f"<Target name=\"{self.name}\", \
|
|
@@ -246,8 +259,17 @@ target '{self.name}' of provider '{self.provider_id}' not found."
|
|
|
246
259
|
input_params["arguments"] = input_params.get("arguments", [])
|
|
247
260
|
targetCapability = input_params.get("targetCapability", kwargs.pop("target_capability", self.capability))
|
|
248
261
|
if targetCapability:
|
|
262
|
+
warnings.warn(
|
|
263
|
+
"The 'targetCapability' parameter is deprecated and will be ignored in the future. "
|
|
264
|
+
"Please, use 'target_profile' parameter instead.",
|
|
265
|
+
category=DeprecationWarning,
|
|
266
|
+
)
|
|
249
267
|
input_params["targetCapability"] = targetCapability
|
|
250
|
-
|
|
268
|
+
if target_profile := input_params.get(
|
|
269
|
+
"target_profile", kwargs.pop("target_profile", self.target_profile)
|
|
270
|
+
):
|
|
271
|
+
input_params["target_profile"] = target_profile
|
|
272
|
+
input_data = input_data._repr_qir_(target=self.name)
|
|
251
273
|
else:
|
|
252
274
|
input_data_format = kwargs.pop("input_data_format", self.input_data_format)
|
|
253
275
|
output_data_format = kwargs.pop("output_data_format", self.output_data_format)
|
|
@@ -327,6 +349,57 @@ target '{self.name}' of provider '{self.provider_id}' not found."
|
|
|
327
349
|
def _get_azure_provider_id(self) -> str:
|
|
328
350
|
return self.provider_id
|
|
329
351
|
|
|
352
|
+
@classmethod
|
|
353
|
+
def _calculate_qir_module_gate_stats(self, qir_module) -> GateStats:
|
|
354
|
+
try:
|
|
355
|
+
from pyqir import Module, is_qubit_type, is_result_type, entry_point, is_entry_point, Function
|
|
356
|
+
|
|
357
|
+
except ImportError:
|
|
358
|
+
raise ImportError(
|
|
359
|
+
"Missing optional 'qiskit' dependencies. \
|
|
360
|
+
To install run: pip install azure-quantum[qiskit]"
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
module: Module = qir_module
|
|
364
|
+
|
|
365
|
+
one_qubit_gates = 0
|
|
366
|
+
multi_qubit_gates = 0
|
|
367
|
+
measurement_gates = 0
|
|
368
|
+
|
|
369
|
+
function_entry_points: list[Function] = filter(is_entry_point, module.functions)
|
|
370
|
+
|
|
371
|
+
# Iterate over the blocks and their instructions
|
|
372
|
+
for function in function_entry_points:
|
|
373
|
+
for block in function.basic_blocks:
|
|
374
|
+
for instruction in block.instructions:
|
|
375
|
+
qubit_count = 0
|
|
376
|
+
result_count = 0
|
|
377
|
+
|
|
378
|
+
# If the instruction is of type quantum rt, do not include this is the price calculation
|
|
379
|
+
if len(instruction.operands) > 0 and "__quantum__rt" not in instruction.operands[-1].name:
|
|
380
|
+
# Check each operand in the instruction
|
|
381
|
+
for operand in instruction.operands:
|
|
382
|
+
value_type = operand.type
|
|
383
|
+
|
|
384
|
+
if is_qubit_type(value_type):
|
|
385
|
+
qubit_count += 1
|
|
386
|
+
elif is_result_type(value_type):
|
|
387
|
+
result_count += 1
|
|
388
|
+
|
|
389
|
+
# Determine the type of gate based on the counts
|
|
390
|
+
if qubit_count == 1 and result_count == 0:
|
|
391
|
+
one_qubit_gates += 1
|
|
392
|
+
if qubit_count >= 2 and result_count == 0:
|
|
393
|
+
multi_qubit_gates += 1
|
|
394
|
+
if result_count > 0:
|
|
395
|
+
measurement_gates += 1
|
|
396
|
+
|
|
397
|
+
return GateStats (
|
|
398
|
+
one_qubit_gates,
|
|
399
|
+
multi_qubit_gates,
|
|
400
|
+
measurement_gates
|
|
401
|
+
)
|
|
402
|
+
|
|
330
403
|
|
|
331
404
|
def _determine_shots_or_deprecated_num_shots(
|
|
332
405
|
shots: int = None,
|
azure/quantum/version.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: azure-quantum
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.0
|
|
4
4
|
Summary: Python client for Azure Quantum
|
|
5
5
|
Home-page: https://github.com/microsoft/azure-quantum-python
|
|
6
6
|
Author: Microsoft
|
|
@@ -26,8 +26,8 @@ Requires-Dist: azure-devtools<2.0,>=1.2.0; extra == "all"
|
|
|
26
26
|
Requires-Dist: graphviz>=0.20.1; extra == "all"
|
|
27
27
|
Requires-Dist: pulser<0.19,>=0.18; extra == "all"
|
|
28
28
|
Requires-Dist: qiskit-ionq<0.6,>=0.5; extra == "all"
|
|
29
|
-
Requires-Dist: qiskit
|
|
30
|
-
Requires-Dist:
|
|
29
|
+
Requires-Dist: qsharp[qiskit]<2.0,>=1.9.0; extra == "all"
|
|
30
|
+
Requires-Dist: pyqir<0.11,>=0.10.6; extra == "all"
|
|
31
31
|
Requires-Dist: Markdown<4.0,>=3.4.1; extra == "all"
|
|
32
32
|
Requires-Dist: python-markdown-math<1.0,>=0.8.0; extra == "all"
|
|
33
33
|
Requires-Dist: qsharp<2.0,>=1.0.33; extra == "all"
|
|
@@ -43,8 +43,8 @@ Provides-Extra: pulser
|
|
|
43
43
|
Requires-Dist: pulser<0.19,>=0.18; extra == "pulser"
|
|
44
44
|
Provides-Extra: qiskit
|
|
45
45
|
Requires-Dist: qiskit-ionq<0.6,>=0.5; extra == "qiskit"
|
|
46
|
-
Requires-Dist: qiskit
|
|
47
|
-
Requires-Dist:
|
|
46
|
+
Requires-Dist: qsharp[qiskit]<2.0,>=1.9.0; extra == "qiskit"
|
|
47
|
+
Requires-Dist: pyqir<0.11,>=0.10.6; extra == "qiskit"
|
|
48
48
|
Requires-Dist: Markdown<4.0,>=3.4.1; extra == "qiskit"
|
|
49
49
|
Requires-Dist: python-markdown-math<1.0,>=0.8.0; extra == "qiskit"
|
|
50
50
|
Provides-Extra: qsharp
|