azure-quantum 2.2.0.dev6__py3-none-any.whl → 2.4.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/_client/py.typed +1 -0
- azure/quantum/argument_types/__init__.py +1 -1
- azure/quantum/cirq/service.py +0 -30
- azure/quantum/cirq/targets/ionq.py +0 -31
- azure/quantum/cirq/targets/quantinuum.py +0 -20
- azure/quantum/qiskit/backends/__init__.py +0 -5
- azure/quantum/qiskit/backends/backend.py +2 -19
- azure/quantum/qiskit/backends/ionq.py +0 -15
- azure/quantum/qiskit/backends/quantinuum.py +1 -32
- azure/quantum/qiskit/job.py +2 -8
- azure/quantum/target/ionq.py +0 -111
- azure/quantum/target/microsoft/elements/dft/job.py +108 -2
- azure/quantum/target/microsoft/elements/dft/target.py +158 -8
- azure/quantum/target/microsoft/target.py +0 -329
- azure/quantum/target/quantinuum.py +0 -123
- azure/quantum/target/rigetti/target.py +5 -0
- azure/quantum/target/target.py +1 -11
- azure/quantum/version.py +1 -1
- {azure_quantum-2.2.0.dev6.dist-info → azure_quantum-2.4.0.dist-info}/METADATA +3 -3
- {azure_quantum-2.2.0.dev6.dist-info → azure_quantum-2.4.0.dist-info}/RECORD +23 -28
- {azure_quantum-2.2.0.dev6.dist-info → azure_quantum-2.4.0.dist-info}/WHEEL +1 -1
- azure/quantum/qiskit/backends/microsoft.py +0 -149
- azure/quantum/qiskit/results/__init__.py +0 -0
- azure/quantum/qiskit/results/resource_estimator.py +0 -25
- azure/quantum/target/microsoft/__init__.py +0 -15
- azure/quantum/target/microsoft/job.py +0 -35
- azure/quantum/target/microsoft/result.py +0 -497
- {azure_quantum-2.2.0.dev6.dist-info → azure_quantum-2.4.0.dist-info}/top_level.txt +0 -0
|
@@ -5,8 +5,11 @@ from azure.quantum.job.job import Job
|
|
|
5
5
|
from azure.quantum.target.target import Target
|
|
6
6
|
from azure.quantum.workspace import Workspace
|
|
7
7
|
from azure.quantum.target.params import InputParams
|
|
8
|
-
from typing import Any, Dict, Type, Union
|
|
8
|
+
from typing import Any, Dict, Type, Union, List
|
|
9
9
|
from .job import MicrosoftElementsDftJob
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
import copy
|
|
12
|
+
import json
|
|
10
13
|
|
|
11
14
|
|
|
12
15
|
class MicrosoftElementsDft(Target):
|
|
@@ -73,15 +76,162 @@ class MicrosoftElementsDft(Target):
|
|
|
73
76
|
if shots is not None:
|
|
74
77
|
warnings.warn("The 'shots' parameter is ignored in Microsoft Elements Dft job.")
|
|
75
78
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
79
|
+
if isinstance(input_data, list):
|
|
80
|
+
|
|
81
|
+
qcschema_data = self._assemble_qcshema_from_files(input_data, input_params)
|
|
82
|
+
|
|
83
|
+
qcschema_blobs = {}
|
|
84
|
+
for i in range(len(qcschema_data)):
|
|
85
|
+
qcschema_blobs[f"inputData_{i}"] = self._encode_input_data(qcschema_data[i])
|
|
83
86
|
|
|
87
|
+
toc_str = self._create_table_of_contents(input_data, list(qcschema_blobs.keys()))
|
|
88
|
+
toc = self._encode_input_data(toc_str)
|
|
89
|
+
|
|
90
|
+
return self._get_job_class().from_input_data_container(
|
|
91
|
+
workspace=self.workspace,
|
|
92
|
+
name=name,
|
|
93
|
+
target=self.name,
|
|
94
|
+
input_data=toc,
|
|
95
|
+
batch_input_blobs=qcschema_blobs,
|
|
96
|
+
input_params={ 'numberOfFiles': len(qcschema_data), "inputFiles": list(qcschema_blobs.keys()), **input_params },
|
|
97
|
+
content_type=kwargs.pop('content_type', self.content_type),
|
|
98
|
+
encoding=kwargs.pop('encoding', self.encoding),
|
|
99
|
+
provider_id=self.provider_id,
|
|
100
|
+
input_data_format=kwargs.pop('input_data_format', 'microsoft.qc-schema.v1'),
|
|
101
|
+
output_data_format=kwargs.pop('output_data_format', self.output_data_format),
|
|
102
|
+
session_id=self.get_latest_session_id(),
|
|
103
|
+
**kwargs
|
|
104
|
+
)
|
|
105
|
+
else:
|
|
106
|
+
return super().submit(
|
|
107
|
+
input_data=input_data,
|
|
108
|
+
name=name,
|
|
109
|
+
shots=shots,
|
|
110
|
+
input_params=input_params,
|
|
111
|
+
**kwargs
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
@classmethod
|
|
117
|
+
def _assemble_qcshema_from_files(self, input_data: List[str], input_params: Dict) -> str:
|
|
118
|
+
"""
|
|
119
|
+
Convert a list of files to a list of qcshema objects serialized in json.
|
|
120
|
+
"""
|
|
121
|
+
|
|
122
|
+
qcshema_objects = []
|
|
123
|
+
for file in input_data:
|
|
124
|
+
file_path = Path(file)
|
|
125
|
+
if not file_path.exists():
|
|
126
|
+
raise FileNotFoundError(f"File {file} does not exist.")
|
|
127
|
+
|
|
128
|
+
file_data = file_path.read_text()
|
|
129
|
+
if file_path.suffix == '.xyz':
|
|
130
|
+
mol = self._xyz_to_qcschema_mol(file_data)
|
|
131
|
+
new_qcschema = self._new_qcshema( input_params, mol )
|
|
132
|
+
qcshema_objects.append(new_qcschema)
|
|
133
|
+
elif file_path.suffix == '.json':
|
|
134
|
+
if input_params is not None and len(input_params.keys()) > 0:
|
|
135
|
+
warnings.warn('Input parameters were given along with a QcSchema file which contains parameters, using QcSchema parameters as is.')
|
|
136
|
+
with open(file_path, 'r') as f:
|
|
137
|
+
qcshema_objects.append( json.load(f) )
|
|
138
|
+
else:
|
|
139
|
+
raise ValueError(f"File type '{file_path.suffix}' for file '{file_path}' is not supported. Please use xyz or QcSchema file formats.")
|
|
140
|
+
|
|
141
|
+
return qcshema_objects
|
|
142
|
+
|
|
143
|
+
@classmethod
|
|
144
|
+
def _new_qcshema( self, input_params: Dict[str,Any], mol: Dict[str,Any], ) -> Dict[str, Any]:
|
|
145
|
+
"""
|
|
146
|
+
Create a new default qcshema object.
|
|
147
|
+
"""
|
|
148
|
+
|
|
149
|
+
if input_params.get("driver") == "go":
|
|
150
|
+
copy_input_params = copy.deepcopy(input_params)
|
|
151
|
+
copy_input_params["driver"] = "gradient"
|
|
152
|
+
new_object = {
|
|
153
|
+
"schema_name": "qcschema_optimization_input",
|
|
154
|
+
"schema_version": 1,
|
|
155
|
+
"initial_molecule": mol,
|
|
156
|
+
}
|
|
157
|
+
if copy_input_params.get("keywords") and copy_input_params["keywords"].get("geometryOptimization"):
|
|
158
|
+
new_object["keywords"] = copy_input_params["keywords"].pop("geometryOptimization")
|
|
159
|
+
new_object["input_specification"] = copy_input_params
|
|
160
|
+
return new_object
|
|
161
|
+
elif input_params.get("driver") == "bomd":
|
|
162
|
+
copy_input_params = copy.deepcopy(input_params)
|
|
163
|
+
copy_input_params["driver"] = "gradient"
|
|
164
|
+
new_object = {
|
|
165
|
+
"schema_name": "madft_molecular_dynamics_input",
|
|
166
|
+
"schema_version": 1,
|
|
167
|
+
"initial_molecule": mol,
|
|
168
|
+
}
|
|
169
|
+
if copy_input_params.get("keywords") and copy_input_params["keywords"].get("molecularDynamics"):
|
|
170
|
+
new_object["keywords"] = copy_input_params["keywords"].pop("molecularDynamics")
|
|
171
|
+
new_object["input_specification"] = copy_input_params
|
|
172
|
+
return new_object
|
|
173
|
+
else:
|
|
174
|
+
new_object = copy.deepcopy(input_params)
|
|
175
|
+
new_object.update({
|
|
176
|
+
"schema_name": "qcschema_input",
|
|
177
|
+
"schema_version": 1,
|
|
178
|
+
"molecule": mol,
|
|
179
|
+
})
|
|
180
|
+
return new_object
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
@classmethod
|
|
184
|
+
def _xyz_to_qcschema_mol(self, file_data: str ) -> Dict[str, Any]:
|
|
185
|
+
"""
|
|
186
|
+
Convert xyz format to qcschema molecule.
|
|
187
|
+
"""
|
|
188
|
+
|
|
189
|
+
lines = file_data.split("\n")
|
|
190
|
+
if len(lines) < 3:
|
|
191
|
+
raise ValueError("Invalid xyz format.")
|
|
192
|
+
n_atoms = int(lines.pop(0))
|
|
193
|
+
comment = lines.pop(0)
|
|
194
|
+
mol = {
|
|
195
|
+
"geometry": [],
|
|
196
|
+
"symbols": [],
|
|
197
|
+
}
|
|
198
|
+
bohr_to_angstrom = 0.52917721092
|
|
199
|
+
for line in lines:
|
|
200
|
+
if line:
|
|
201
|
+
elements = line.split()
|
|
202
|
+
if len(elements) < 4:
|
|
203
|
+
raise ValueError("Invalid xyz format.")
|
|
204
|
+
symbol, x, y, z = elements
|
|
205
|
+
mol["symbols"].append(symbol)
|
|
206
|
+
mol["geometry"] += [float(x)/bohr_to_angstrom, float(y)/bohr_to_angstrom, float(z)/bohr_to_angstrom]
|
|
207
|
+
else:
|
|
208
|
+
break
|
|
209
|
+
|
|
210
|
+
if len(mol["symbols"]) != n_atoms:
|
|
211
|
+
raise ValueError("Number of inputs does not match the number of atoms in xyz file.")
|
|
212
|
+
|
|
213
|
+
return mol
|
|
84
214
|
|
|
85
215
|
@classmethod
|
|
86
216
|
def _get_job_class(cls) -> Type[Job]:
|
|
87
217
|
return MicrosoftElementsDftJob
|
|
218
|
+
|
|
219
|
+
@classmethod
|
|
220
|
+
def _create_table_of_contents(cls, input_files: List[str], input_blobs: List[str]) -> Dict[str,Any]:
|
|
221
|
+
"""Create the table of contents for a batched job that contains a description of file and the mapping between the file names and the blob names"""
|
|
222
|
+
|
|
223
|
+
assert len(input_files) == len(input_blobs), "Internal error: number of blobs is not that same as the number of files."
|
|
224
|
+
|
|
225
|
+
toc = []
|
|
226
|
+
for i in range(len(input_files)):
|
|
227
|
+
toc.append(
|
|
228
|
+
{
|
|
229
|
+
"inputFileName": input_files[i],
|
|
230
|
+
"qcschemaBlobName": input_blobs[i],
|
|
231
|
+
}
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
return {
|
|
235
|
+
"description": "This files contains the mapping between the xyz file name that were submitted and the qcschema blobs that are used for the calculation.",
|
|
236
|
+
"tableOfContents": toc,
|
|
237
|
+
}
|
|
@@ -13,29 +13,6 @@ from ...workspace import Workspace
|
|
|
13
13
|
from ..params import InputParams, InputParamsItem, AutoValidatingParams, \
|
|
14
14
|
validating_field
|
|
15
15
|
from ..target import Target
|
|
16
|
-
from . import MicrosoftEstimatorJob
|
|
17
|
-
|
|
18
|
-
class QubitParams:
|
|
19
|
-
"""
|
|
20
|
-
Resource estimator Qubit parameters.
|
|
21
|
-
"""
|
|
22
|
-
|
|
23
|
-
GATE_US_E3 = "qubit_gate_us_e3"
|
|
24
|
-
GATE_US_E4 = "qubit_gate_us_e4"
|
|
25
|
-
GATE_NS_E3 = "qubit_gate_ns_e3"
|
|
26
|
-
GATE_NS_E4 = "qubit_gate_ns_e4"
|
|
27
|
-
MAJ_NS_E4 = "qubit_maj_ns_e4"
|
|
28
|
-
MAJ_NS_E6 = "qubit_maj_ns_e6"
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
class QECScheme:
|
|
32
|
-
"""
|
|
33
|
-
Resource estimator QEC Scheme.
|
|
34
|
-
"""
|
|
35
|
-
|
|
36
|
-
SURFACE_CODE = "surface_code"
|
|
37
|
-
FLOQUET_CODE = "floquet_code"
|
|
38
|
-
|
|
39
16
|
|
|
40
17
|
def _check_error_rate(name, value):
|
|
41
18
|
if value <= 0.0 or value >= 1.0:
|
|
@@ -64,90 +41,6 @@ class MeasurementErrorRate(AutoValidatingParams):
|
|
|
64
41
|
process: float = field(metadata={"validate": _check_error_rate})
|
|
65
42
|
readout: float = field(metadata={"validate": _check_error_rate})
|
|
66
43
|
|
|
67
|
-
@dataclass
|
|
68
|
-
class MicrosoftEstimatorQubitParams(AutoValidatingParams):
|
|
69
|
-
@staticmethod
|
|
70
|
-
def check_instruction_set(name, value):
|
|
71
|
-
if value not in ["gate-based", "gate_based", "GateBased", "gateBased",
|
|
72
|
-
"Majorana", "majorana"]:
|
|
73
|
-
raise ValueError(f"{name} must be GateBased or Majorana")
|
|
74
|
-
|
|
75
|
-
name: Optional[str] = None
|
|
76
|
-
instruction_set: Optional[str] = validating_field(check_instruction_set)
|
|
77
|
-
one_qubit_measurement_time: Optional[str] = validating_field(check_time)
|
|
78
|
-
two_qubit_joint_measurement_time: Optional[str] = \
|
|
79
|
-
validating_field(check_time)
|
|
80
|
-
one_qubit_gate_time: Optional[str] = validating_field(check_time)
|
|
81
|
-
two_qubit_gate_time: Optional[str] = validating_field(check_time)
|
|
82
|
-
t_gate_time: Optional[str] = validating_field(check_time)
|
|
83
|
-
one_qubit_measurement_error_rate: Union[None, float, MeasurementErrorRate] = \
|
|
84
|
-
validating_field(_check_error_rate_or_process_and_readout)
|
|
85
|
-
two_qubit_joint_measurement_error_rate: Union[None, float, MeasurementErrorRate] = \
|
|
86
|
-
validating_field(_check_error_rate_or_process_and_readout)
|
|
87
|
-
one_qubit_gate_error_rate: Optional[float] = \
|
|
88
|
-
validating_field(_check_error_rate)
|
|
89
|
-
two_qubit_gate_error_rate: Optional[float] = \
|
|
90
|
-
validating_field(_check_error_rate)
|
|
91
|
-
t_gate_error_rate: Optional[float] = validating_field(_check_error_rate)
|
|
92
|
-
idle_error_rate: Optional[float] = validating_field(_check_error_rate)
|
|
93
|
-
|
|
94
|
-
_default_models = [QubitParams.GATE_US_E3, QubitParams.GATE_US_E4,
|
|
95
|
-
QubitParams.GATE_NS_E3, QubitParams.GATE_NS_E4,
|
|
96
|
-
QubitParams.MAJ_NS_E4, QubitParams.MAJ_NS_E6]
|
|
97
|
-
_gate_based = ["gate-based", "gate_based", "GateBased", "gateBased"]
|
|
98
|
-
_maj_based = ["Majorana", "majorana"]
|
|
99
|
-
|
|
100
|
-
def post_validation(self, result):
|
|
101
|
-
# check whether all fields have been specified in case a custom qubit
|
|
102
|
-
# model is specified
|
|
103
|
-
custom = result != {} and \
|
|
104
|
-
(self.name is None or self.name not in self._default_models)
|
|
105
|
-
|
|
106
|
-
# no further validation needed for non-custom models
|
|
107
|
-
if not custom:
|
|
108
|
-
return
|
|
109
|
-
|
|
110
|
-
# instruction set must be set
|
|
111
|
-
if self.instruction_set is None:
|
|
112
|
-
raise LookupError("instruction_set must be set for custom qubit "
|
|
113
|
-
"parameters")
|
|
114
|
-
|
|
115
|
-
# NOTE at this point, we know that instruction set must have valid
|
|
116
|
-
# value
|
|
117
|
-
if self.one_qubit_measurement_time is None:
|
|
118
|
-
raise LookupError("one_qubit_measurement_time must be set")
|
|
119
|
-
if self.one_qubit_measurement_error_rate is None:
|
|
120
|
-
raise LookupError("one_qubit_measurement_error_rate must be set")
|
|
121
|
-
|
|
122
|
-
# this only needs to be checked for gate based qubits
|
|
123
|
-
if self.instruction_set in self._gate_based:
|
|
124
|
-
if self.one_qubit_gate_time is None:
|
|
125
|
-
raise LookupError("one_qubit_gate_time must be set")
|
|
126
|
-
|
|
127
|
-
def as_dict(self, validate=True) -> Dict[str, Any]:
|
|
128
|
-
qubit_params = super().as_dict(validate)
|
|
129
|
-
if len(qubit_params) != 0:
|
|
130
|
-
if isinstance(self.one_qubit_measurement_error_rate, MeasurementErrorRate):
|
|
131
|
-
qubit_params["oneQubitMeasurementErrorRate"] = \
|
|
132
|
-
self.one_qubit_measurement_error_rate.as_dict(validate)
|
|
133
|
-
|
|
134
|
-
if isinstance(self.two_qubit_joint_measurement_error_rate, MeasurementErrorRate):
|
|
135
|
-
qubit_params["twoQubitJointMeasurementErrorRate"] = \
|
|
136
|
-
self.two_qubit_joint_measurement_error_rate.as_dict(validate)
|
|
137
|
-
|
|
138
|
-
return qubit_params
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
@dataclass
|
|
142
|
-
class MicrosoftEstimatorQecScheme(AutoValidatingParams):
|
|
143
|
-
name: Optional[str] = None
|
|
144
|
-
error_correction_threshold: Optional[float] = \
|
|
145
|
-
validating_field(_check_error_rate)
|
|
146
|
-
crossing_prefactor: Optional[float] = None
|
|
147
|
-
logical_cycle_time: Optional[str] = None
|
|
148
|
-
physical_qubits_per_logical_qubit: Optional[str] = None
|
|
149
|
-
|
|
150
|
-
|
|
151
44
|
@dataclass
|
|
152
45
|
class ProtocolSpecificDistillationUnitSpecification(AutoValidatingParams):
|
|
153
46
|
num_unit_qubits: Optional[int] = None
|
|
@@ -247,225 +140,3 @@ class DistillationUnitSpecification(AutoValidatingParams):
|
|
|
247
140
|
logical_qubit_specification_first_round_override_dict
|
|
248
141
|
|
|
249
142
|
return specification_dict
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
@dataclass
|
|
253
|
-
class ErrorBudgetPartition(AutoValidatingParams):
|
|
254
|
-
"""
|
|
255
|
-
Resource estimator error budget partition parameters.
|
|
256
|
-
"""
|
|
257
|
-
logical: float = 0.001 / 3
|
|
258
|
-
t_states: float = 0.001 / 3
|
|
259
|
-
rotations: float = 0.001 / 3
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
@dataclass
|
|
263
|
-
class MicrosoftEstimatorConstraints(AutoValidatingParams):
|
|
264
|
-
"""
|
|
265
|
-
Resource estimator constraints.
|
|
266
|
-
"""
|
|
267
|
-
|
|
268
|
-
@staticmethod
|
|
269
|
-
def at_least_one(name, value):
|
|
270
|
-
if value < 1:
|
|
271
|
-
raise ValueError(f"{name} must be at least 1")
|
|
272
|
-
|
|
273
|
-
logical_depth_factor: Optional[float] = validating_field(at_least_one)
|
|
274
|
-
max_t_factories: Optional[int] = validating_field(at_least_one)
|
|
275
|
-
max_duration: Optional[int] = validating_field(check_time)
|
|
276
|
-
max_physical_qubits: Optional[int] = validating_field(at_least_one)
|
|
277
|
-
|
|
278
|
-
def post_validation(self, result):
|
|
279
|
-
if self.max_duration is not None and self.max_physical_qubits is not None:
|
|
280
|
-
raise LookupError("Both duration and number of physical qubits constraints are provided, but only one is allowe at a time.")
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
@dataclass
|
|
284
|
-
class MicrosoftEstimatorProfiling(AutoValidatingParams):
|
|
285
|
-
@staticmethod
|
|
286
|
-
def at_most_30(name, value):
|
|
287
|
-
if value < 0 or value > 30:
|
|
288
|
-
raise ValueError(f"{name} must be nonnegative and at most 30")
|
|
289
|
-
|
|
290
|
-
call_stack_depth: Optional[int] = validating_field(at_most_30)
|
|
291
|
-
inline_functions: Optional[bool] = None
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
class MicrosoftEstimatorInputParamsItem(InputParamsItem):
|
|
295
|
-
"""
|
|
296
|
-
Input params for microsoft.estimator target
|
|
297
|
-
|
|
298
|
-
:ivar error_budget Total error budget for execution of the algorithm
|
|
299
|
-
"""
|
|
300
|
-
|
|
301
|
-
def __init__(self):
|
|
302
|
-
super().__init__()
|
|
303
|
-
|
|
304
|
-
self.qubit_params: MicrosoftEstimatorQubitParams = \
|
|
305
|
-
MicrosoftEstimatorQubitParams()
|
|
306
|
-
self.qec_scheme: MicrosoftEstimatorQecScheme = \
|
|
307
|
-
MicrosoftEstimatorQecScheme()
|
|
308
|
-
self.distillation_unit_specifications = [] # type: List[DistillationUnitSpecification]
|
|
309
|
-
self.constraints: MicrosoftEstimatorConstraints = \
|
|
310
|
-
MicrosoftEstimatorConstraints()
|
|
311
|
-
self.profiling: MicrosoftEstimatorProfiling = \
|
|
312
|
-
MicrosoftEstimatorProfiling()
|
|
313
|
-
self.error_budget: Optional[Union[float, ErrorBudgetPartition]] = None
|
|
314
|
-
|
|
315
|
-
def as_dict(self, validate=True) -> Dict[str, Any]:
|
|
316
|
-
result = super().as_dict(validate)
|
|
317
|
-
|
|
318
|
-
qubit_params = self.qubit_params.as_dict(validate)
|
|
319
|
-
if len(qubit_params) != 0:
|
|
320
|
-
result["qubitParams"] = qubit_params
|
|
321
|
-
|
|
322
|
-
qec_scheme = self.qec_scheme.as_dict(validate)
|
|
323
|
-
if len(qec_scheme) != 0:
|
|
324
|
-
result["qecScheme"] = qec_scheme
|
|
325
|
-
|
|
326
|
-
for specification in self.distillation_unit_specifications:
|
|
327
|
-
specification_dict = specification.as_dict(validate)
|
|
328
|
-
if len(specification_dict) != 0:
|
|
329
|
-
if result.get("distillationUnitSpecifications") is None:
|
|
330
|
-
result["distillationUnitSpecifications"] = []
|
|
331
|
-
|
|
332
|
-
result["distillationUnitSpecifications"].append(specification_dict)
|
|
333
|
-
|
|
334
|
-
constraints = self.constraints.as_dict(validate)
|
|
335
|
-
if len(constraints) != 0:
|
|
336
|
-
result["constraints"] = constraints
|
|
337
|
-
|
|
338
|
-
profiling = self.profiling.as_dict(validate)
|
|
339
|
-
if len(profiling) != 0:
|
|
340
|
-
result["profiling"] = profiling
|
|
341
|
-
|
|
342
|
-
if self.error_budget is not None:
|
|
343
|
-
if isinstance(self.error_budget, float) or \
|
|
344
|
-
isinstance(self.error_budget, int):
|
|
345
|
-
if validate and \
|
|
346
|
-
(self.error_budget <= 0 or self.error_budget >= 1):
|
|
347
|
-
message = "error_budget must be value between 0 and 1"
|
|
348
|
-
raise ValueError(message)
|
|
349
|
-
result["errorBudget"] = self.error_budget
|
|
350
|
-
elif isinstance(self.error_budget, ErrorBudgetPartition):
|
|
351
|
-
result["errorBudget"] = self.error_budget.as_dict(validate)
|
|
352
|
-
|
|
353
|
-
return result
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
class MicrosoftEstimatorParams(InputParams, MicrosoftEstimatorInputParamsItem):
|
|
357
|
-
"""
|
|
358
|
-
Resource estimator input parameters.
|
|
359
|
-
"""
|
|
360
|
-
def __init__(self, num_items: Optional[int] = None):
|
|
361
|
-
InputParams.__init__(
|
|
362
|
-
self,
|
|
363
|
-
num_items=num_items,
|
|
364
|
-
item_type=MicrosoftEstimatorInputParamsItem)
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
class MicrosoftEstimator(Target):
|
|
368
|
-
"""
|
|
369
|
-
Resource estimator target from the microsoft-qc provider.
|
|
370
|
-
"""
|
|
371
|
-
|
|
372
|
-
target_names = [
|
|
373
|
-
"microsoft.estimator"
|
|
374
|
-
]
|
|
375
|
-
|
|
376
|
-
def __init__(
|
|
377
|
-
self,
|
|
378
|
-
workspace: "Workspace",
|
|
379
|
-
name: str = "microsoft.estimator",
|
|
380
|
-
**kwargs
|
|
381
|
-
):
|
|
382
|
-
# There is only a single target name for this target
|
|
383
|
-
assert name == self.target_names[0]
|
|
384
|
-
|
|
385
|
-
# make sure to not pass argument twice
|
|
386
|
-
kwargs.pop("provider_id", None)
|
|
387
|
-
|
|
388
|
-
super().__init__(
|
|
389
|
-
workspace=workspace,
|
|
390
|
-
name=name,
|
|
391
|
-
input_data_format="qir.v1",
|
|
392
|
-
output_data_format="microsoft.resource-estimates.v1",
|
|
393
|
-
provider_id="microsoft-qc",
|
|
394
|
-
content_type=ContentType.json,
|
|
395
|
-
target_profile="Adaptive_RI",
|
|
396
|
-
**kwargs
|
|
397
|
-
)
|
|
398
|
-
|
|
399
|
-
def submit(
|
|
400
|
-
self,
|
|
401
|
-
input_data: Any,
|
|
402
|
-
name: str = "azure-quantum-job",
|
|
403
|
-
shots: int = None,
|
|
404
|
-
input_params: Union[Dict[str, Any], InputParams, None] = None,
|
|
405
|
-
**kwargs,
|
|
406
|
-
) -> Job:
|
|
407
|
-
"""
|
|
408
|
-
Submit an estimation job.
|
|
409
|
-
|
|
410
|
-
:param input_data: Input data
|
|
411
|
-
:type input_data: Any
|
|
412
|
-
:param name: Job name
|
|
413
|
-
:type name: str
|
|
414
|
-
:param shots: Number of shots. Ignored in estimation. Defaults to None
|
|
415
|
-
:type shots: int
|
|
416
|
-
:param input_params: Input parameters
|
|
417
|
-
:type input_params: Dict[str, Any]
|
|
418
|
-
:return: Azure Quantum job
|
|
419
|
-
:rtype: Job
|
|
420
|
-
"""
|
|
421
|
-
|
|
422
|
-
if shots is not None:
|
|
423
|
-
warnings.warn("The 'shots' parameter is ignored in resource estimation job.")
|
|
424
|
-
|
|
425
|
-
try:
|
|
426
|
-
from qiskit import QuantumCircuit
|
|
427
|
-
from qsharp import TargetProfile
|
|
428
|
-
from qsharp.interop.qiskit import ResourceEstimatorBackend
|
|
429
|
-
from pyqir import Context, Module
|
|
430
|
-
|
|
431
|
-
if isinstance(input_data, QuantumCircuit):
|
|
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
|
-
# Add NOOP for recording output tuples
|
|
438
|
-
# the service isn't set up to handle any output recording calls
|
|
439
|
-
# and the Q# compiler will always emit them.
|
|
440
|
-
noop_tuple_record_output = """; NOOP the extern calls to recording output tuples
|
|
441
|
-
define void @__quantum__rt__tuple_record_output(i64, i8*) {
|
|
442
|
-
ret void
|
|
443
|
-
}"""
|
|
444
|
-
noop_tuple_record_output_module = Module.from_ir(
|
|
445
|
-
context, noop_tuple_record_output
|
|
446
|
-
)
|
|
447
|
-
module.link(noop_tuple_record_output_module)
|
|
448
|
-
|
|
449
|
-
err = module.verify()
|
|
450
|
-
if err is not None:
|
|
451
|
-
raise Exception(err)
|
|
452
|
-
input_data = module.bitcode
|
|
453
|
-
finally:
|
|
454
|
-
return super().submit(
|
|
455
|
-
input_data=input_data,
|
|
456
|
-
name=name,
|
|
457
|
-
shots=shots,
|
|
458
|
-
input_params=input_params,
|
|
459
|
-
**kwargs
|
|
460
|
-
)
|
|
461
|
-
|
|
462
|
-
@classmethod
|
|
463
|
-
def _get_job_class(cls) -> Type[Job]:
|
|
464
|
-
return MicrosoftEstimatorJob
|
|
465
|
-
|
|
466
|
-
def _qir_output_data_format(self) -> str:
|
|
467
|
-
""""Fallback output data format in case of QIR job submission."""
|
|
468
|
-
return "microsoft.resource-estimates.v1"
|
|
469
|
-
|
|
470
|
-
def make_params(self, num_items: Optional[int] = None):
|
|
471
|
-
return MicrosoftEstimatorParams(num_items=num_items)
|
|
@@ -100,126 +100,3 @@ class Quantinuum(Target):
|
|
|
100
100
|
input_params=input_params,
|
|
101
101
|
**kwargs
|
|
102
102
|
)
|
|
103
|
-
|
|
104
|
-
def estimate_cost(
|
|
105
|
-
self,
|
|
106
|
-
circuit: Union[str, Any] = None,
|
|
107
|
-
num_shots: int = None,
|
|
108
|
-
N_1q: int = None,
|
|
109
|
-
N_2q: int = None,
|
|
110
|
-
N_m: int = None,
|
|
111
|
-
shots: int = None,
|
|
112
|
-
) -> CostEstimate:
|
|
113
|
-
"""Estimate the cost in HQC for a given circuit.
|
|
114
|
-
Optionally, you can provide the number of gate and measurement operations
|
|
115
|
-
manually.
|
|
116
|
-
The actual price charged by the provider may differ from this estimation.
|
|
117
|
-
|
|
118
|
-
For the most current pricing details, see
|
|
119
|
-
https://aka.ms/AQ/Quantinuum/Documentation
|
|
120
|
-
Or find your workspace and view pricing options in the "Provider" tab
|
|
121
|
-
of your workspace: https://aka.ms/aq/myworkspaces
|
|
122
|
-
|
|
123
|
-
:param circuit: Quantum circuit in OpenQASM 2.0 format
|
|
124
|
-
:type circuit: str
|
|
125
|
-
:param num_shots: Number of shots for which to estimate costs
|
|
126
|
-
:type num_shots: int
|
|
127
|
-
:param N_1q: Number of one-qubit gates, if not specified,
|
|
128
|
-
this is estimated from the circuit
|
|
129
|
-
:type N_1q: int
|
|
130
|
-
:param N_2q: Number of two-qubit gates, if not specified,
|
|
131
|
-
this is estimated from the circuit
|
|
132
|
-
:type N_2q: int
|
|
133
|
-
:param N_m: Number of measurement operations, if not specified,
|
|
134
|
-
this is estimated from the circuit
|
|
135
|
-
:type N_m: int
|
|
136
|
-
:param shots: Number of shots for which to estimate costs
|
|
137
|
-
:type shots: int
|
|
138
|
-
:raises ImportError: If N_1q, N_2q and N_m are not specified,
|
|
139
|
-
this will require a qiskit installation.
|
|
140
|
-
"""
|
|
141
|
-
|
|
142
|
-
if num_shots is None and shots is None:
|
|
143
|
-
raise ValueError("The 'shots' parameter has to be specified")
|
|
144
|
-
|
|
145
|
-
if num_shots is not None:
|
|
146
|
-
warn(
|
|
147
|
-
"The 'num_shots' parameter will be deprecated. Please, use 'shots' parameter instead.",
|
|
148
|
-
category=DeprecationWarning,
|
|
149
|
-
)
|
|
150
|
-
shots = num_shots
|
|
151
|
-
|
|
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
|
-
|
|
181
|
-
|
|
182
|
-
import re
|
|
183
|
-
is_emulator_regex = re.compile("^.*(-sim|-[0-9]*e)$")
|
|
184
|
-
is_syntax_checker_regex = re.compile("^.*(-apival|-[0-9]*sc)$")
|
|
185
|
-
|
|
186
|
-
if is_emulator_regex.match(self.name):
|
|
187
|
-
currency_code = "EHQC"
|
|
188
|
-
else:
|
|
189
|
-
currency_code = "HQC"
|
|
190
|
-
|
|
191
|
-
if is_syntax_checker_regex.match(self.name):
|
|
192
|
-
HQC = 0.0
|
|
193
|
-
else:
|
|
194
|
-
HQC = 5 + shots * (N_1q + 10 * N_2q + 5 * N_m) / 5000
|
|
195
|
-
|
|
196
|
-
return CostEstimate(
|
|
197
|
-
events=[
|
|
198
|
-
UsageEvent(
|
|
199
|
-
dimension_id="gates1q",
|
|
200
|
-
dimension_name="1Q Gates",
|
|
201
|
-
measure_unit="1q gates",
|
|
202
|
-
amount_billed=0.0,
|
|
203
|
-
amount_consumed=N_1q,
|
|
204
|
-
unit_price=0.0
|
|
205
|
-
),
|
|
206
|
-
UsageEvent(
|
|
207
|
-
dimension_id="gates2q",
|
|
208
|
-
dimension_name="2Q Gates",
|
|
209
|
-
measure_unit="2q gates",
|
|
210
|
-
amount_billed=0.0,
|
|
211
|
-
amount_consumed=N_2q,
|
|
212
|
-
unit_price=0.0
|
|
213
|
-
),
|
|
214
|
-
UsageEvent(
|
|
215
|
-
dimension_id="measops",
|
|
216
|
-
dimension_name="Measurement operations",
|
|
217
|
-
measure_unit="measurement operations",
|
|
218
|
-
amount_billed=0.0,
|
|
219
|
-
amount_consumed=N_m,
|
|
220
|
-
unit_price=0.0
|
|
221
|
-
)
|
|
222
|
-
],
|
|
223
|
-
currency_code=currency_code,
|
|
224
|
-
estimated_total=HQC
|
|
225
|
-
)
|
|
@@ -30,6 +30,8 @@ class RigettiTarget(str, Enum):
|
|
|
30
30
|
QVM = "rigetti.sim.qvm"
|
|
31
31
|
"""A simulator target for Quil. See https://github.com/quil-lang/qvm for more info."""
|
|
32
32
|
|
|
33
|
+
ANKAA_3 = "rigetti.qpu.ankaa-3"
|
|
34
|
+
|
|
33
35
|
ANKAA_9Q_3 = "rigetti.qpu.ankaa-9q-3"
|
|
34
36
|
|
|
35
37
|
def simulators() -> List[str]:
|
|
@@ -41,6 +43,7 @@ class RigettiTarget(str, Enum):
|
|
|
41
43
|
def qpus() -> List[str]:
|
|
42
44
|
"""Returns a list of QPU targets"""
|
|
43
45
|
return [
|
|
46
|
+
RigettiTarget.ANKAA_3.value,
|
|
44
47
|
RigettiTarget.ANKAA_9Q_3.value,
|
|
45
48
|
]
|
|
46
49
|
|
|
@@ -49,6 +52,8 @@ class RigettiTarget(str, Enum):
|
|
|
49
52
|
|
|
50
53
|
if target_name == RigettiTarget.QVM.value:
|
|
51
54
|
return 20
|
|
55
|
+
elif target_name == RigettiTarget.ANKAA_3.value:
|
|
56
|
+
return 84
|
|
52
57
|
elif target_name == RigettiTarget.ANKAA_9Q_3.value:
|
|
53
58
|
return 9
|
|
54
59
|
else:
|