classiq 0.46.1__py3-none-any.whl → 0.48.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.
- classiq/_internals/api_wrapper.py +45 -8
- classiq/applications/combinatorial_helpers/pauli_helpers/pauli_utils.py +2 -7
- classiq/applications/grover/grover_model_constructor.py +2 -1
- classiq/execution/execution_session.py +133 -45
- classiq/execution/jobs.py +120 -1
- classiq/interface/_version.py +1 -1
- classiq/interface/backend/quantum_backend_providers.py +0 -1
- classiq/interface/debug_info/debug_info.py +23 -1
- classiq/interface/execution/primitives.py +17 -0
- classiq/interface/executor/iqae_result.py +3 -3
- classiq/interface/executor/result.py +3 -1
- classiq/interface/generator/arith/arithmetic_operations.py +5 -2
- classiq/interface/generator/arith/binary_ops.py +21 -14
- classiq/interface/generator/arith/extremum_operations.py +9 -1
- classiq/interface/generator/arith/number_utils.py +6 -0
- classiq/interface/generator/arith/register_user_input.py +30 -21
- classiq/interface/generator/arith/unary_ops.py +13 -1
- classiq/interface/generator/expressions/expression.py +8 -0
- classiq/interface/generator/functions/type_name.py +1 -3
- classiq/interface/generator/generated_circuit_data.py +47 -2
- classiq/interface/generator/quantum_program.py +10 -2
- classiq/interface/generator/synthesis_metadata/synthesis_execution_data.py +17 -3
- classiq/interface/ide/visual_model.py +10 -5
- classiq/interface/interface_version.py +1 -1
- classiq/interface/model/bind_operation.py +0 -3
- classiq/interface/model/phase_operation.py +11 -0
- classiq/interface/model/port_declaration.py +1 -12
- classiq/interface/model/quantum_expressions/arithmetic_operation.py +34 -6
- classiq/interface/model/quantum_lambda_function.py +4 -1
- classiq/interface/model/quantum_statement.py +16 -1
- classiq/interface/model/quantum_variable_declaration.py +0 -22
- classiq/interface/model/statement_block.py +3 -0
- classiq/interface/server/global_versions.py +4 -4
- classiq/interface/server/routes.py +0 -3
- classiq/model_expansions/capturing/propagated_var_stack.py +5 -2
- classiq/model_expansions/closure.py +7 -2
- classiq/model_expansions/evaluators/quantum_type_utils.py +0 -7
- classiq/model_expansions/generative_functions.py +146 -28
- classiq/model_expansions/interpreter.py +17 -5
- classiq/model_expansions/quantum_operations/classicalif.py +27 -10
- classiq/model_expansions/quantum_operations/control.py +22 -15
- classiq/model_expansions/quantum_operations/emitter.py +68 -7
- classiq/model_expansions/quantum_operations/expression_operation.py +25 -16
- classiq/model_expansions/quantum_operations/inplace_binary_operation.py +167 -95
- classiq/model_expansions/quantum_operations/invert.py +12 -6
- classiq/model_expansions/quantum_operations/phase.py +189 -0
- classiq/model_expansions/quantum_operations/power.py +9 -8
- classiq/model_expansions/quantum_operations/quantum_assignment_operation.py +20 -5
- classiq/model_expansions/quantum_operations/quantum_function_call.py +1 -1
- classiq/model_expansions/quantum_operations/repeat.py +32 -13
- classiq/model_expansions/quantum_operations/within_apply.py +19 -6
- classiq/model_expansions/scope.py +16 -5
- classiq/model_expansions/scope_initialization.py +11 -1
- classiq/model_expansions/sympy_conversion/expression_to_sympy.py +23 -1
- classiq/model_expansions/visitors/variable_references.py +11 -7
- classiq/qmod/builtins/__init__.py +10 -0
- classiq/qmod/builtins/constants.py +10 -0
- classiq/qmod/builtins/functions/state_preparation.py +4 -1
- classiq/qmod/builtins/operations.py +55 -161
- classiq/qmod/create_model_function.py +1 -1
- classiq/qmod/generative.py +14 -5
- classiq/qmod/native/pretty_printer.py +14 -4
- classiq/qmod/pretty_print/pretty_printer.py +14 -4
- classiq/qmod/qmod_constant.py +28 -18
- classiq/qmod/qmod_variable.py +43 -23
- classiq/qmod/quantum_expandable.py +14 -1
- classiq/qmod/semantics/static_semantics_visitor.py +10 -0
- classiq/qmod/semantics/validation/constants_validation.py +16 -0
- {classiq-0.46.1.dist-info → classiq-0.48.0.dist-info}/METADATA +9 -4
- {classiq-0.46.1.dist-info → classiq-0.48.0.dist-info}/RECORD +71 -66
- {classiq-0.46.1.dist-info → classiq-0.48.0.dist-info}/WHEEL +0 -0
@@ -1,5 +1,5 @@
|
|
1
1
|
import json
|
2
|
-
from typing import Dict, List, Optional, Protocol, Type, TypeVar
|
2
|
+
from typing import Any, Dict, List, Optional, Protocol, Type, TypeVar
|
3
3
|
|
4
4
|
import pydantic
|
5
5
|
|
@@ -7,6 +7,7 @@ import classiq.interface.executor.execution_result
|
|
7
7
|
import classiq.interface.pyomo_extension
|
8
8
|
from classiq.interface.analyzer import analysis_params, result as analysis_result
|
9
9
|
from classiq.interface.analyzer.analysis_params import AnalysisRBParams
|
10
|
+
from classiq.interface.analyzer.result import GraphStatus
|
10
11
|
from classiq.interface.chemistry import ground_state_problem, operator
|
11
12
|
from classiq.interface.enum_utils import StrEnum
|
12
13
|
from classiq.interface.exceptions import ClassiqAPIError, ClassiqValueError
|
@@ -16,7 +17,7 @@ from classiq.interface.execution.jobs import (
|
|
16
17
|
)
|
17
18
|
from classiq.interface.executor import execution_request
|
18
19
|
from classiq.interface.generator import quantum_program as generator_result
|
19
|
-
from classiq.interface.hardware import HardwareInformation
|
20
|
+
from classiq.interface.hardware import HardwareInformation, Provider
|
20
21
|
from classiq.interface.jobs import JobDescription, JobID, JSONObject
|
21
22
|
from classiq.interface.model.model import Model
|
22
23
|
from classiq.interface.server import routes
|
@@ -36,6 +37,7 @@ class HTTPMethod(StrEnum):
|
|
36
37
|
GET = "GET"
|
37
38
|
POST = "POST"
|
38
39
|
PATCH = "PATCH"
|
40
|
+
PUT = "PUT"
|
39
41
|
|
40
42
|
|
41
43
|
class StatusType(Protocol):
|
@@ -80,8 +82,9 @@ class ApiWrapper:
|
|
80
82
|
params: Optional[Dict] = None,
|
81
83
|
use_versioned_url: bool = True,
|
82
84
|
headers: Optional[Dict[str, str]] = None,
|
85
|
+
allow_none: bool = False,
|
83
86
|
) -> dict:
|
84
|
-
res = await client().call_api(
|
87
|
+
res: Any = await client().call_api(
|
85
88
|
http_method=http_method,
|
86
89
|
url=url,
|
87
90
|
body=body,
|
@@ -89,6 +92,8 @@ class ApiWrapper:
|
|
89
92
|
params=params,
|
90
93
|
use_versioned_url=use_versioned_url,
|
91
94
|
)
|
95
|
+
if allow_none and res is None:
|
96
|
+
return {}
|
92
97
|
if not isinstance(res, dict):
|
93
98
|
raise ClassiqValueError(f"Unexpected returned value: {res}")
|
94
99
|
return res
|
@@ -169,6 +174,18 @@ class ApiWrapper:
|
|
169
174
|
)
|
170
175
|
return ExecutionJobDetailsV1.parse_obj(data)
|
171
176
|
|
177
|
+
@classmethod
|
178
|
+
async def call_cancel_execution_job(
|
179
|
+
cls,
|
180
|
+
job_id: JobID,
|
181
|
+
) -> None:
|
182
|
+
await cls._call_task(
|
183
|
+
http_method=HTTPMethod.PUT,
|
184
|
+
url=f"{routes.EXECUTION_JOBS_NON_VERSIONED_FULL_PATH}/{job_id.job_id}/cancel",
|
185
|
+
use_versioned_url=False,
|
186
|
+
allow_none=True,
|
187
|
+
)
|
188
|
+
|
172
189
|
@classmethod
|
173
190
|
async def call_query_execution_jobs(
|
174
191
|
cls,
|
@@ -267,12 +284,32 @@ class ApiWrapper:
|
|
267
284
|
cls,
|
268
285
|
params: analysis_params.AnalysisOptionalDevicesParams,
|
269
286
|
) -> analysis_result.DevicesResult:
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
287
|
+
hardware_info = await cls.call_get_all_hardware_devices()
|
288
|
+
return cls._get_devices_from_hardware_info(hardware_info, params)
|
289
|
+
|
290
|
+
@staticmethod
|
291
|
+
def _get_devices_from_hardware_info(
|
292
|
+
hardware_info: List[HardwareInformation],
|
293
|
+
params: analysis_params.AnalysisOptionalDevicesParams,
|
294
|
+
) -> analysis_result.DevicesResult:
|
295
|
+
available_hardware: Dict[Provider, Dict[str, bool]] = {
|
296
|
+
Provider.IBM_QUANTUM: {},
|
297
|
+
Provider.AMAZON_BRAKET: {},
|
298
|
+
Provider.AZURE_QUANTUM: {},
|
299
|
+
}
|
300
|
+
for info in hardware_info:
|
301
|
+
if info.provider not in available_hardware:
|
302
|
+
continue
|
303
|
+
is_available = info.number_of_qubits >= params.qubit_count
|
304
|
+
available_hardware[info.provider][info.display_name] = is_available
|
305
|
+
return analysis_result.DevicesResult(
|
306
|
+
devices=analysis_result.AvailableHardware(
|
307
|
+
ibm_quantum=available_hardware[Provider.IBM_QUANTUM],
|
308
|
+
azure_quantum=available_hardware[Provider.AZURE_QUANTUM],
|
309
|
+
amazon_braket=available_hardware[Provider.AMAZON_BRAKET],
|
310
|
+
),
|
311
|
+
status=GraphStatus.SUCCESS,
|
274
312
|
)
|
275
|
-
return analysis_result.DevicesResult.parse_obj(data)
|
276
313
|
|
277
314
|
@classmethod
|
278
315
|
async def call_get_all_hardware_devices(cls) -> List[HardwareInformation]:
|
@@ -44,10 +44,5 @@ def _pauli_terms_to_qmod(hamiltonian: List[PauliTerm]) -> str:
|
|
44
44
|
return ", ".join(qmod_strings)
|
45
45
|
|
46
46
|
|
47
|
-
def
|
48
|
-
|
49
|
-
for struct in hamiltonian:
|
50
|
-
pauli_str = ", ".join([pauli_enum_to_str(p) for p in struct["pauli"]])
|
51
|
-
res.append(f'"pauli": [{pauli_str}], "coefficient": {struct["coefficient"]}')
|
52
|
-
|
53
|
-
return f"{{{', '.join(res)}}}"
|
47
|
+
def _pauli_dict_to_pauli_terms(hamiltonian: List[QmodPyStruct]) -> List[PauliTerm]:
|
48
|
+
return [PauliTerm(**struct) for struct in hamiltonian]
|
@@ -11,6 +11,7 @@ from classiq.interface.model.native_function_definition import NativeFunctionDef
|
|
11
11
|
from classiq.interface.model.port_declaration import PortDeclaration
|
12
12
|
from classiq.interface.model.quantum_expressions.arithmetic_operation import (
|
13
13
|
ArithmeticOperation,
|
14
|
+
ArithmeticOperationKind,
|
14
15
|
)
|
15
16
|
from classiq.interface.model.quantum_function_call import QuantumFunctionCall
|
16
17
|
from classiq.interface.model.quantum_lambda_function import QuantumLambdaFunction
|
@@ -110,7 +111,7 @@ def construct_grover_model(
|
|
110
111
|
ArithmeticOperation(
|
111
112
|
expression=Expression(expr=expression),
|
112
113
|
result_var=HandleBinding(name="res"),
|
113
|
-
|
114
|
+
operation_kind=ArithmeticOperationKind.InplaceXor,
|
114
115
|
),
|
115
116
|
],
|
116
117
|
),
|
@@ -1,22 +1,22 @@
|
|
1
1
|
import json
|
2
2
|
from typing import Any, Callable, Dict, List, Optional, Tuple, Union, cast
|
3
3
|
|
4
|
+
from classiq.interface.chemistry.operator import PauliOperator, pauli_integers_to_str
|
4
5
|
from classiq.interface.exceptions import ClassiqValueError
|
6
|
+
from classiq.interface.execution.primitives import EstimateInput, PrimitivesInput
|
5
7
|
from classiq.interface.executor.execution_preferences import ExecutionPreferences
|
6
|
-
from classiq.interface.executor.execution_result import ResultsCollection
|
7
8
|
from classiq.interface.executor.result import (
|
8
9
|
EstimationResult,
|
9
|
-
EstimationResults,
|
10
10
|
ExecutionDetails,
|
11
|
-
MultipleExecutionDetails,
|
12
11
|
)
|
13
12
|
from classiq.interface.generator.functions.qmod_python_interface import QmodPyStruct
|
14
13
|
from classiq.interface.generator.quantum_program import QuantumProgram
|
15
14
|
|
16
15
|
from classiq.applications.combinatorial_helpers.pauli_helpers.pauli_utils import (
|
17
|
-
|
16
|
+
_pauli_dict_to_pauli_terms,
|
18
17
|
_pauli_terms_to_qmod,
|
19
18
|
)
|
19
|
+
from classiq.execution.jobs import ExecutionJob
|
20
20
|
from classiq.executor import execute
|
21
21
|
from classiq.qmod.builtins import PauliTerm
|
22
22
|
from classiq.qmod.builtins.classical_execution_primitives import (
|
@@ -52,12 +52,26 @@ def _deserialize_program(program: Program) -> QuantumProgram:
|
|
52
52
|
)
|
53
53
|
|
54
54
|
|
55
|
+
def hamiltonian_to_pauli_terms(hamiltonian: Hamiltonian) -> List[PauliTerm]:
|
56
|
+
if isinstance(hamiltonian[0], PauliTerm):
|
57
|
+
return cast(List[PauliTerm], hamiltonian)
|
58
|
+
else:
|
59
|
+
return _pauli_dict_to_pauli_terms(cast(List[QmodPyStruct], hamiltonian))
|
60
|
+
|
61
|
+
|
55
62
|
def to_hamiltonian_str(hamiltonian: Hamiltonian) -> str:
|
56
|
-
return (
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
63
|
+
return _pauli_terms_to_qmod(hamiltonian_to_pauli_terms(hamiltonian))
|
64
|
+
|
65
|
+
|
66
|
+
def _hamiltonian_to_pauli_operator(hamiltonian: Hamiltonian) -> PauliOperator:
|
67
|
+
pauli_list = [
|
68
|
+
(
|
69
|
+
pauli_integers_to_str(elem.pauli), # type: ignore[arg-type]
|
70
|
+
elem.coefficient,
|
71
|
+
)
|
72
|
+
for elem in hamiltonian_to_pauli_terms(hamiltonian)
|
73
|
+
]
|
74
|
+
return PauliOperator(pauli_list=pauli_list)
|
61
75
|
|
62
76
|
|
63
77
|
def serialize(
|
@@ -165,14 +179,6 @@ class ExecutionSession:
|
|
165
179
|
if execution_preferences is not None:
|
166
180
|
self.program.model.execution_preferences = execution_preferences
|
167
181
|
|
168
|
-
def _execute_quantum_program(
|
169
|
-
self, operation: str, **kwargs: Any
|
170
|
-
) -> ResultsCollection:
|
171
|
-
self.program.model.classical_execution_code = generate_code_snippet(
|
172
|
-
operation, **kwargs
|
173
|
-
)
|
174
|
-
return execute(SerializedQuantumProgram(self.qprog)).result()
|
175
|
-
|
176
182
|
def sample(self, parameters: Optional[ExecutionParams] = None) -> ExecutionDetails:
|
177
183
|
"""
|
178
184
|
Samples the quantum program with the given parameters, if any.
|
@@ -180,16 +186,34 @@ class ExecutionSession:
|
|
180
186
|
Args:
|
181
187
|
parameters: The values to set for the parameters of the quantum program when sampling. Each key should be the name of a parameter in the quantum program (parameters of the main function), and the value should be the value to set for that parameter.
|
182
188
|
|
189
|
+
Returns:
|
190
|
+
The result of the sampling.
|
191
|
+
"""
|
192
|
+
job = self.submit_sample(parameters=parameters)
|
193
|
+
return job.get_sample_result()
|
194
|
+
|
195
|
+
def submit_sample(
|
196
|
+
self, parameters: Optional[ExecutionParams] = None
|
197
|
+
) -> ExecutionJob:
|
198
|
+
"""
|
199
|
+
Initiates an execution job with the `sample` primitive.
|
200
|
+
|
201
|
+
This is a non-blocking version of `sample`: it gets the same parameters and initiates the same execution job, but instead
|
202
|
+
of waiting for the result, it returns the job object immediately.
|
203
|
+
|
204
|
+
Args:
|
205
|
+
parameters: The values to set for the parameters of the quantum program when sampling. Each key should be the name of a parameter in the quantum program (parameters of the main function), and the value should be the value to set for that parameter.
|
183
206
|
|
184
207
|
Returns:
|
185
|
-
|
208
|
+
The execution job.
|
186
209
|
"""
|
187
|
-
|
188
|
-
|
189
|
-
self._execute_quantum_program(
|
190
|
-
SupportedPrimitives.SAMPLE, parameters=format_parameters(parameters)
|
191
|
-
)[0].value,
|
210
|
+
self.program.model.classical_execution_code = generate_code_snippet(
|
211
|
+
SupportedPrimitives.SAMPLE, parameters=format_parameters(parameters)
|
192
212
|
)
|
213
|
+
self.program.execution_primitives_input = PrimitivesInput(
|
214
|
+
sample=[parse_params(parameters)] if parameters is not None else [{}]
|
215
|
+
)
|
216
|
+
return execute(SerializedQuantumProgram(self.qprog))
|
193
217
|
|
194
218
|
def batch_sample(self, parameters: List[ExecutionParams]) -> List[ExecutionDetails]:
|
195
219
|
"""
|
@@ -201,13 +225,29 @@ class ExecutionSession:
|
|
201
225
|
Returns:
|
202
226
|
List[ExecutionDetails]: The results of all the sampling iterations.
|
203
227
|
"""
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
228
|
+
job = self.submit_batch_sample(parameters=parameters)
|
229
|
+
return job.get_batch_sample_result()
|
230
|
+
|
231
|
+
def submit_batch_sample(self, parameters: List[ExecutionParams]) -> ExecutionJob:
|
232
|
+
"""
|
233
|
+
Initiates an execution job with the `batch_sample` primitive.
|
234
|
+
|
235
|
+
This is a non-blocking version of `batch_sample`: it gets the same parameters and initiates the same execution job, but instead
|
236
|
+
of waiting for the result, it returns the job object immediately.
|
237
|
+
|
238
|
+
Args:
|
239
|
+
parameters: A list of the parameters for each iteration. Each item is a dictionary where each key should be the name of a parameter in the quantum program (parameters of the main function), and the value should be the value to set for that parameter.
|
240
|
+
|
241
|
+
Returns:
|
242
|
+
The execution job.
|
243
|
+
"""
|
244
|
+
self.program.model.classical_execution_code = generate_code_snippet(
|
245
|
+
SupportedPrimitives.BATCH_SAMPLE, parameters=format_parameters(parameters)
|
246
|
+
)
|
247
|
+
self.program.execution_primitives_input = PrimitivesInput(
|
248
|
+
sample=[parse_params(params) for params in parameters]
|
249
|
+
)
|
250
|
+
return execute(SerializedQuantumProgram(self.qprog))
|
211
251
|
|
212
252
|
def estimate(
|
213
253
|
self, hamiltonian: Hamiltonian, parameters: Optional[ExecutionParams] = None
|
@@ -222,14 +262,39 @@ class ExecutionSession:
|
|
222
262
|
Returns:
|
223
263
|
EstimationResult: The result of the estimation.
|
224
264
|
"""
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
265
|
+
job = self.submit_estimate(hamiltonian=hamiltonian, parameters=parameters)
|
266
|
+
return job.get_estimate_result()
|
267
|
+
|
268
|
+
def submit_estimate(
|
269
|
+
self, hamiltonian: Hamiltonian, parameters: Optional[ExecutionParams] = None
|
270
|
+
) -> ExecutionJob:
|
271
|
+
"""
|
272
|
+
Initiates an execution job with the `estimate` primitive.
|
273
|
+
|
274
|
+
This is a non-blocking version of `estimate`: it gets the same parameters and initiates the same execution job, but instead
|
275
|
+
of waiting for the result, it returns the job object immediately.
|
276
|
+
|
277
|
+
Args:
|
278
|
+
hamiltonian: The Hamiltonian to estimate the expectation value of.
|
279
|
+
parameters: The values to set for the parameters of the quantum program when estimating. Each key should be the name of a parameter in the quantum program (parameters of the main function), and the value should be the value to set for that parameter.
|
280
|
+
|
281
|
+
Returns:
|
282
|
+
The execution job.
|
283
|
+
"""
|
284
|
+
self.program.model.classical_execution_code = generate_code_snippet(
|
285
|
+
SupportedPrimitives.ESTIMATE,
|
286
|
+
parameters=format_parameters(parameters),
|
287
|
+
hamiltonian=to_hamiltonian_str(hamiltonian),
|
288
|
+
)
|
289
|
+
self.program.execution_primitives_input = PrimitivesInput(
|
290
|
+
estimate=EstimateInput(
|
291
|
+
hamiltonian=_hamiltonian_to_pauli_operator(hamiltonian),
|
292
|
+
parameters=(
|
293
|
+
[parse_params(parameters)] if parameters is not None else [{}]
|
294
|
+
),
|
295
|
+
)
|
232
296
|
)
|
297
|
+
return execute(SerializedQuantumProgram(self.qprog))
|
233
298
|
|
234
299
|
def batch_estimate(
|
235
300
|
self, hamiltonian: Hamiltonian, parameters: List[ExecutionParams]
|
@@ -244,11 +309,34 @@ class ExecutionSession:
|
|
244
309
|
Returns:
|
245
310
|
List[EstimationResult]: The results of all the estimation iterations.
|
246
311
|
"""
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
312
|
+
job = self.submit_batch_estimate(hamiltonian=hamiltonian, parameters=parameters)
|
313
|
+
return job.get_batch_estimate_result()
|
314
|
+
|
315
|
+
def submit_batch_estimate(
|
316
|
+
self, hamiltonian: Hamiltonian, parameters: List[ExecutionParams]
|
317
|
+
) -> ExecutionJob:
|
318
|
+
"""
|
319
|
+
Initiates an execution job with the `batch_estimate` primitive.
|
320
|
+
|
321
|
+
This is a non-blocking version of `batch_estimate`: it gets the same parameters and initiates the same execution job, but instead
|
322
|
+
of waiting for the result, it returns the job object immediately.
|
323
|
+
|
324
|
+
Args:
|
325
|
+
hamiltonian: The Hamiltonian to estimate the expectation value of.
|
326
|
+
parameters: A list of the parameters for each iteration. Each item is a dictionary where each key should be the name of a parameter in the quantum program (parameters of the main function), and the value should be the value to set for that parameter.
|
327
|
+
|
328
|
+
Returns:
|
329
|
+
The execution job.
|
330
|
+
"""
|
331
|
+
self.program.model.classical_execution_code = generate_code_snippet(
|
332
|
+
SupportedPrimitives.BATCH_ESTIMATE,
|
333
|
+
parameters=format_parameters(parameters),
|
334
|
+
hamiltonian=to_hamiltonian_str(hamiltonian),
|
335
|
+
)
|
336
|
+
self.program.execution_primitives_input = PrimitivesInput(
|
337
|
+
estimate=EstimateInput(
|
338
|
+
hamiltonian=_hamiltonian_to_pauli_operator(hamiltonian),
|
339
|
+
parameters=[parse_params(params) for params in parameters],
|
340
|
+
)
|
341
|
+
)
|
342
|
+
return execute(SerializedQuantumProgram(self.qprog))
|
classiq/execution/jobs.py
CHANGED
@@ -3,10 +3,19 @@ from datetime import datetime
|
|
3
3
|
from typing import Any, List, Optional, Union
|
4
4
|
from urllib.parse import urljoin
|
5
5
|
|
6
|
-
from classiq.interface.exceptions import
|
6
|
+
from classiq.interface.exceptions import (
|
7
|
+
ClassiqAPIError,
|
8
|
+
ClassiqError,
|
9
|
+
)
|
7
10
|
from classiq.interface.execution.jobs import ExecutionJobDetailsV1
|
8
11
|
from classiq.interface.executor.execution_request import ExecutionJobDetails
|
9
12
|
from classiq.interface.executor.execution_result import ResultsCollection
|
13
|
+
from classiq.interface.executor.result import (
|
14
|
+
EstimationResult,
|
15
|
+
EstimationResults,
|
16
|
+
ExecutionDetails,
|
17
|
+
MultipleExecutionDetails,
|
18
|
+
)
|
10
19
|
from classiq.interface.jobs import JobStatus, JSONObject
|
11
20
|
from classiq.interface.server.routes import EXECUTION_JOBS_NON_VERSIONED_FULL_PATH
|
12
21
|
|
@@ -21,6 +30,13 @@ _JOB_DETAILS_VERSION = "v1"
|
|
21
30
|
_JOB_RESULT_VERSION = "v1"
|
22
31
|
|
23
32
|
|
33
|
+
class ClassiqExecutionResultError(ClassiqError):
|
34
|
+
def __init__(self, primitive: str) -> None:
|
35
|
+
super().__init__(
|
36
|
+
f"Execution job does not contain a single {primitive!r} result, make sure you use the 'get_*_result' method matching the primitive you executed. You can use the 'result' method to see the general result."
|
37
|
+
)
|
38
|
+
|
39
|
+
|
24
40
|
class ExecutionJob:
|
25
41
|
_details: _JobDetails
|
26
42
|
_result: Optional[ResultsCollection]
|
@@ -112,6 +128,96 @@ class ExecutionJob:
|
|
112
128
|
def result_value(self, *args: Any, **kwargs: Any) -> Any:
|
113
129
|
return self.result(*args, **kwargs)[0].value
|
114
130
|
|
131
|
+
def get_sample_result(self) -> ExecutionDetails:
|
132
|
+
"""
|
133
|
+
Returns the job's result as a single sample result after validation. If the result is not yet available, waits for it.
|
134
|
+
|
135
|
+
Returns:
|
136
|
+
The sample result of the execution job.
|
137
|
+
|
138
|
+
Raises:
|
139
|
+
ClassiqExecutionResultError: In case the result does not contain a single sample result.
|
140
|
+
ClassiqAPIError: In case the job has failed.
|
141
|
+
"""
|
142
|
+
results = self.result()
|
143
|
+
if len(results) != 1:
|
144
|
+
raise ClassiqExecutionResultError("sample")
|
145
|
+
|
146
|
+
result = results[0].value
|
147
|
+
if isinstance(result, ExecutionDetails):
|
148
|
+
return result
|
149
|
+
if isinstance(result, MultipleExecutionDetails) and len(result.details) == 1:
|
150
|
+
return result.details[0]
|
151
|
+
raise ClassiqExecutionResultError("sample")
|
152
|
+
|
153
|
+
def get_batch_sample_result(self) -> List[ExecutionDetails]:
|
154
|
+
"""
|
155
|
+
Returns the job's result as a single batch_sample result after validation. If the result is not yet available, waits for it.
|
156
|
+
|
157
|
+
Returns:
|
158
|
+
The batch_sample result of the execution job.
|
159
|
+
|
160
|
+
Raises:
|
161
|
+
ClassiqExecutionResultError: In case the result does not contain a single batch_sample result.
|
162
|
+
ClassiqAPIError: In case the job has failed.
|
163
|
+
"""
|
164
|
+
results = self.result()
|
165
|
+
if len(results) != 1:
|
166
|
+
raise ClassiqExecutionResultError("batch_sample")
|
167
|
+
|
168
|
+
result = results[0].value
|
169
|
+
if isinstance(result, ExecutionDetails):
|
170
|
+
return [result]
|
171
|
+
if isinstance(result, MultipleExecutionDetails):
|
172
|
+
return result.details
|
173
|
+
|
174
|
+
raise ClassiqExecutionResultError("batch_sample")
|
175
|
+
|
176
|
+
def get_estimate_result(self) -> EstimationResult:
|
177
|
+
"""
|
178
|
+
Returns the job's result as a single estimate result after validation. If the result is not yet available, waits for it.
|
179
|
+
|
180
|
+
Returns:
|
181
|
+
The estimate result of the execution job.
|
182
|
+
|
183
|
+
Raises:
|
184
|
+
ClassiqExecutionResultError: In case the result does not contain a single estimate result.
|
185
|
+
ClassiqAPIError: In case the job has failed.
|
186
|
+
"""
|
187
|
+
results = self.result()
|
188
|
+
if len(results) != 1:
|
189
|
+
raise ClassiqExecutionResultError("estimate")
|
190
|
+
|
191
|
+
result = results[0].value
|
192
|
+
if isinstance(result, EstimationResult):
|
193
|
+
return result
|
194
|
+
if isinstance(result, EstimationResults) and len(result.results) == 1:
|
195
|
+
return result.results[0]
|
196
|
+
raise ClassiqExecutionResultError("estimate")
|
197
|
+
|
198
|
+
def get_batch_estimate_result(self) -> List[EstimationResult]:
|
199
|
+
"""
|
200
|
+
Returns the job's result as a single batch_estimate result after validation. If the result is not yet available, waits for it.
|
201
|
+
|
202
|
+
Returns:
|
203
|
+
The batch_estimate result of the execution job.
|
204
|
+
|
205
|
+
Raises:
|
206
|
+
ClassiqExecutionResultError: In case the result does not contain a single batch_estimate result.
|
207
|
+
ClassiqAPIError: In case the job has failed.
|
208
|
+
"""
|
209
|
+
results = self.result()
|
210
|
+
if len(results) != 1:
|
211
|
+
raise ClassiqExecutionResultError("batch_estimate")
|
212
|
+
|
213
|
+
result = results[0].value
|
214
|
+
if isinstance(result, EstimationResult):
|
215
|
+
return [result]
|
216
|
+
if isinstance(result, EstimationResults):
|
217
|
+
return result.results
|
218
|
+
|
219
|
+
raise ClassiqExecutionResultError("batch_estimate")
|
220
|
+
|
115
221
|
async def poll_async(self, timeout_sec: Optional[float] = None) -> None:
|
116
222
|
if not self.status.is_final():
|
117
223
|
await self._poll_job(timeout_sec=timeout_sec)
|
@@ -141,6 +247,19 @@ class ExecutionJob:
|
|
141
247
|
|
142
248
|
rename = syncify_function(rename_async)
|
143
249
|
|
250
|
+
async def cancel_async(self) -> None:
|
251
|
+
"""
|
252
|
+
Cancels the execution job. This implies the cancellation of any ongoing jobs
|
253
|
+
sent to the provider during this execution job.
|
254
|
+
|
255
|
+
The function returns without waiting to the actual cancellation. It is possible
|
256
|
+
to continue polling the job in order to ensure its cancellation, which might
|
257
|
+
not be immediate.
|
258
|
+
"""
|
259
|
+
await ApiWrapper.call_cancel_execution_job(self._job_id)
|
260
|
+
|
261
|
+
cancel = syncify_function(cancel_async)
|
262
|
+
|
144
263
|
@property
|
145
264
|
def ide_url(self) -> str:
|
146
265
|
base_url = client().config.ide
|
classiq/interface/_version.py
CHANGED
@@ -68,7 +68,6 @@ class AzureQuantumBackendNames(StrEnum):
|
|
68
68
|
IONQ_SIMULATOR = "ionq.simulator"
|
69
69
|
MICROSOFT_ESTIMATOR = "microsoft.estimator"
|
70
70
|
MICROSOFT_FULLSTATE_SIMULATOR = "microsoft.simulator.fullstate"
|
71
|
-
RIGETTI_ASPEN3 = "rigetti.qpu.aspen-m-3"
|
72
71
|
RIGETTI_SIMULATOR = "rigetti.sim.qvm"
|
73
72
|
RIGETTI_ANKAA2 = "rigetti.qpu.ankaa-2"
|
74
73
|
RIGETTI_ANKAA9 = "rigetti.qpu.ankaa-9q-1"
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import json
|
2
|
-
from typing import Any, Dict, Optional, Union
|
2
|
+
from typing import Any, Dict, Mapping, Optional, Tuple, Union
|
3
3
|
from uuid import UUID
|
4
4
|
|
5
5
|
from pydantic import BaseModel, Field
|
@@ -14,10 +14,12 @@ ParameterValue = Union[float, int, str, None]
|
|
14
14
|
|
15
15
|
class FunctionDebugInfo(BaseModel):
|
16
16
|
name: str
|
17
|
+
# Parameters describe classical parameters passed to function
|
17
18
|
# ParameterValue appears in type only for backwards compatibility
|
18
19
|
parameters: Dict[str, str] = Field(type=Dict[str, ParameterValue])
|
19
20
|
level: OperationLevel
|
20
21
|
is_allocate_or_free: bool = Field(default=False)
|
22
|
+
port_to_passed_variable_map: Dict[str, str] = Field(default_factory=dict)
|
21
23
|
|
22
24
|
@staticmethod
|
23
25
|
def param_controller(value: Any) -> str:
|
@@ -26,6 +28,26 @@ class FunctionDebugInfo(BaseModel):
|
|
26
28
|
except TypeError:
|
27
29
|
return repr(value)
|
28
30
|
|
31
|
+
def update_map_from_port_mapping(self, port_mapping: Mapping[str, str]) -> None:
|
32
|
+
new_port_to_passed_variable_map = self.port_to_passed_variable_map.copy()
|
33
|
+
for old_key, new_key in port_mapping.items():
|
34
|
+
if old_key in new_port_to_passed_variable_map:
|
35
|
+
new_port_to_passed_variable_map[new_key] = (
|
36
|
+
new_port_to_passed_variable_map.pop(old_key)
|
37
|
+
)
|
38
|
+
self.port_to_passed_variable_map = new_port_to_passed_variable_map
|
39
|
+
|
40
|
+
def update_map_from_inout_port_mapping(
|
41
|
+
self, port_mapping: Mapping[str, Tuple[str, str]]
|
42
|
+
) -> None:
|
43
|
+
new_port_to_passed_variable_map = self.port_to_passed_variable_map.copy()
|
44
|
+
for old_key, (new_key1, new_key2) in port_mapping.items():
|
45
|
+
if old_key in new_port_to_passed_variable_map:
|
46
|
+
value = new_port_to_passed_variable_map.pop(old_key)
|
47
|
+
new_port_to_passed_variable_map[new_key1] = value
|
48
|
+
new_port_to_passed_variable_map[new_key2] = value
|
49
|
+
self.port_to_passed_variable_map = new_port_to_passed_variable_map
|
50
|
+
|
29
51
|
|
30
52
|
class DebugInfoCollection(BaseModel):
|
31
53
|
# Pydantic only started supporting UUID as keys in Pydantic V2
|
@@ -0,0 +1,17 @@
|
|
1
|
+
from typing import List, Optional
|
2
|
+
|
3
|
+
from pydantic import BaseModel, Field
|
4
|
+
|
5
|
+
from classiq.interface.chemistry.operator import PauliOperator
|
6
|
+
from classiq.interface.executor.quantum_code import Arguments
|
7
|
+
from classiq.interface.helpers.custom_encoders import CUSTOM_ENCODERS
|
8
|
+
|
9
|
+
|
10
|
+
class EstimateInput(BaseModel, json_encoders=CUSTOM_ENCODERS):
|
11
|
+
hamiltonian: PauliOperator
|
12
|
+
parameters: List[Arguments]
|
13
|
+
|
14
|
+
|
15
|
+
class PrimitivesInput(BaseModel, json_encoders=CUSTOM_ENCODERS):
|
16
|
+
sample: Optional[List[Arguments]] = Field(default=None)
|
17
|
+
estimate: Optional[EstimateInput] = Field(default=None)
|
@@ -1,6 +1,6 @@
|
|
1
|
-
from typing import List
|
1
|
+
from typing import List
|
2
2
|
|
3
|
-
from pydantic import BaseModel
|
3
|
+
from pydantic import BaseModel, Field
|
4
4
|
|
5
5
|
from classiq.interface.executor.result import ExecutionDetails
|
6
6
|
from classiq.interface.generator.functions.classical_type import QmodPyObject
|
@@ -14,6 +14,6 @@ class IQAEIterationData(BaseModel):
|
|
14
14
|
|
15
15
|
class IQAEResult(VersionedModel, QmodPyObject):
|
16
16
|
estimation: float
|
17
|
-
confidence_interval:
|
17
|
+
confidence_interval: List[float] = Field(min_items=2, max_items=2)
|
18
18
|
iterations_data: List[IQAEIterationData]
|
19
19
|
warnings: List[str]
|
@@ -305,7 +305,9 @@ class EstimationMetadata(BaseModel, extra=pydantic.Extra.allow):
|
|
305
305
|
|
306
306
|
class EstimationResult(BaseModel, QmodPyObject):
|
307
307
|
value: Complex = pydantic.Field(..., description="Estimation for the operator")
|
308
|
-
variance: Complex = pydantic.Field(
|
308
|
+
variance: Optional[Complex] = pydantic.Field(
|
309
|
+
description="Variance of the estimation", default=None
|
310
|
+
)
|
309
311
|
metadata: EstimationMetadata = pydantic.Field(
|
310
312
|
..., description="Metadata for the estimation"
|
311
313
|
)
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import abc
|
2
|
-
from typing import ClassVar, Iterable, Optional, Tuple
|
2
|
+
from typing import ClassVar, Final, Iterable, Optional, Tuple
|
3
3
|
|
4
4
|
import pydantic
|
5
5
|
|
@@ -9,7 +9,10 @@ from classiq.interface.generator.arith.machine_precision import (
|
|
9
9
|
from classiq.interface.generator.arith.register_user_input import RegisterArithmeticInfo
|
10
10
|
from classiq.interface.generator.function_params import FunctionParams
|
11
11
|
|
12
|
-
DEFAULT_GARBAGE_OUT_NAME: str = "extra_qubits"
|
12
|
+
DEFAULT_GARBAGE_OUT_NAME: Final[str] = "extra_qubits"
|
13
|
+
MODULO_WITH_FRACTION_PLACES_ERROR_MSG: Final[str] = (
|
14
|
+
"Modulo with fraction places not supported"
|
15
|
+
)
|
13
16
|
|
14
17
|
|
15
18
|
class ArithmeticOperationParams(FunctionParams):
|