classiq 0.83.0__py3-none-any.whl → 0.85.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 +27 -0
- classiq/applications/chemistry/chemistry_model_constructor.py +0 -2
- classiq/applications/chemistry/hartree_fock.py +68 -0
- classiq/applications/chemistry/mapping.py +85 -0
- classiq/applications/chemistry/op_utils.py +79 -0
- classiq/applications/chemistry/problems.py +195 -0
- classiq/applications/chemistry/ucc.py +109 -0
- classiq/applications/chemistry/z2_symmetries.py +368 -0
- classiq/applications/combinatorial_helpers/pauli_helpers/pauli_utils.py +30 -1
- classiq/applications/combinatorial_optimization/combinatorial_problem.py +20 -42
- classiq/{model_expansions/evaluators → evaluators}/arg_type_match.py +12 -4
- classiq/{model_expansions/evaluators → evaluators}/argument_types.py +1 -1
- classiq/evaluators/classical_expression.py +53 -0
- classiq/{model_expansions/evaluators → evaluators}/classical_type_inference.py +3 -4
- classiq/{model_expansions/evaluators → evaluators}/parameter_types.py +17 -15
- classiq/execution/__init__.py +12 -1
- classiq/execution/execution_session.py +238 -49
- classiq/execution/jobs.py +26 -1
- classiq/execution/qnn.py +2 -2
- classiq/execution/user_budgets.py +39 -0
- classiq/interface/_version.py +1 -1
- classiq/interface/constants.py +1 -0
- classiq/interface/debug_info/debug_info.py +0 -4
- classiq/interface/execution/primitives.py +29 -1
- classiq/interface/executor/estimate_cost.py +35 -0
- classiq/interface/executor/execution_result.py +13 -0
- classiq/interface/executor/result.py +116 -1
- classiq/interface/executor/user_budget.py +26 -33
- classiq/interface/generator/expressions/atomic_expression_functions.py +10 -1
- classiq/interface/generator/expressions/proxies/classical/any_classical_value.py +0 -6
- classiq/interface/generator/functions/builtins/internal_operators.py +2 -0
- classiq/interface/generator/functions/classical_type.py +2 -35
- classiq/interface/generator/functions/concrete_types.py +20 -3
- classiq/interface/generator/functions/type_modifier.py +0 -19
- classiq/interface/generator/generated_circuit_data.py +5 -18
- classiq/interface/generator/types/compilation_metadata.py +0 -3
- classiq/interface/ide/operation_registry.py +45 -0
- classiq/interface/ide/visual_model.py +68 -3
- classiq/interface/model/bounds.py +12 -2
- classiq/interface/model/model.py +12 -7
- classiq/interface/model/port_declaration.py +2 -24
- classiq/interface/model/quantum_expressions/arithmetic_operation.py +7 -4
- classiq/interface/model/variable_declaration_statement.py +33 -6
- classiq/interface/pretty_print/__init__.py +0 -0
- classiq/{qmod/native → interface/pretty_print}/expression_to_qmod.py +18 -11
- classiq/interface/server/routes.py +4 -0
- classiq/model_expansions/atomic_expression_functions_defs.py +47 -6
- classiq/model_expansions/function_builder.py +4 -1
- classiq/model_expansions/interpreters/base_interpreter.py +3 -3
- classiq/model_expansions/interpreters/generative_interpreter.py +16 -1
- classiq/model_expansions/quantum_operations/allocate.py +1 -1
- classiq/model_expansions/quantum_operations/assignment_result_processor.py +64 -22
- classiq/model_expansions/quantum_operations/bind.py +2 -2
- classiq/model_expansions/quantum_operations/bounds.py +7 -1
- classiq/model_expansions/quantum_operations/call_emitter.py +26 -20
- classiq/model_expansions/quantum_operations/classical_var_emitter.py +16 -0
- classiq/model_expansions/quantum_operations/variable_decleration.py +31 -11
- classiq/model_expansions/scope.py +7 -0
- classiq/model_expansions/scope_initialization.py +3 -3
- classiq/model_expansions/transformers/model_renamer.py +6 -4
- classiq/model_expansions/transformers/type_modifier_inference.py +81 -43
- classiq/model_expansions/transformers/var_splitter.py +1 -1
- classiq/model_expansions/visitors/symbolic_param_inference.py +2 -3
- classiq/open_library/functions/__init__.py +3 -2
- classiq/open_library/functions/amplitude_amplification.py +10 -18
- classiq/open_library/functions/discrete_sine_cosine_transform.py +5 -5
- classiq/open_library/functions/grover.py +14 -6
- classiq/open_library/functions/modular_exponentiation.py +22 -20
- classiq/open_library/functions/qaoa_penalty.py +8 -1
- classiq/open_library/functions/state_preparation.py +18 -32
- classiq/qmod/__init__.py +2 -0
- classiq/qmod/builtins/enums.py +23 -0
- classiq/qmod/builtins/functions/__init__.py +2 -0
- classiq/qmod/builtins/functions/exponentiation.py +32 -4
- classiq/qmod/builtins/operations.py +65 -1
- classiq/qmod/builtins/structs.py +55 -3
- classiq/qmod/classical_variable.py +74 -0
- classiq/qmod/declaration_inferrer.py +3 -2
- classiq/qmod/native/pretty_printer.py +20 -20
- classiq/qmod/pretty_print/expression_to_python.py +2 -1
- classiq/qmod/pretty_print/pretty_printer.py +35 -21
- classiq/qmod/python_classical_type.py +12 -5
- classiq/qmod/qfunc.py +2 -19
- classiq/qmod/qmod_constant.py +2 -5
- classiq/qmod/qmod_parameter.py +2 -5
- classiq/qmod/qmod_variable.py +61 -23
- classiq/qmod/quantum_expandable.py +5 -3
- classiq/qmod/quantum_function.py +49 -4
- classiq/qmod/semantics/annotation/qstruct_annotator.py +1 -1
- classiq/qmod/semantics/validation/main_validation.py +1 -9
- classiq/qmod/symbolic_type.py +2 -1
- classiq/qmod/utilities.py +0 -2
- classiq/qmod/write_qmod.py +1 -1
- {classiq-0.83.0.dist-info → classiq-0.85.0.dist-info}/METADATA +4 -1
- {classiq-0.83.0.dist-info → classiq-0.85.0.dist-info}/RECORD +101 -90
- classiq/interface/model/quantum_variable_declaration.py +0 -7
- classiq/model_expansions/evaluators/classical_expression.py +0 -36
- /classiq/{model_expansions/evaluators → evaluators}/__init__.py +0 -0
- /classiq/{model_expansions/evaluators → evaluators}/control.py +0 -0
- /classiq/{model_expansions → evaluators}/expression_evaluator.py +0 -0
- /classiq/{model_expansions/evaluators → evaluators}/quantum_type_utils.py +0 -0
- /classiq/{model_expansions/evaluators → evaluators}/type_type_match.py +0 -0
- {classiq-0.83.0.dist-info → classiq-0.85.0.dist-info}/WHEEL +0 -0
@@ -12,7 +12,6 @@ from classiq.interface.generator.expressions.proxies.classical.any_classical_val
|
|
12
12
|
)
|
13
13
|
from classiq.interface.generator.functions.classical_type import (
|
14
14
|
ClassicalArray,
|
15
|
-
ClassicalList,
|
16
15
|
ClassicalTuple,
|
17
16
|
ClassicalType,
|
18
17
|
)
|
@@ -39,20 +38,20 @@ from classiq.interface.model.quantum_type import (
|
|
39
38
|
QuantumType,
|
40
39
|
)
|
41
40
|
|
42
|
-
from classiq.
|
43
|
-
from classiq.
|
44
|
-
from classiq.model_expansions.evaluators.classical_expression import (
|
41
|
+
from classiq.evaluators.arg_type_match import check_type_match
|
42
|
+
from classiq.evaluators.classical_expression import (
|
45
43
|
evaluate_classical_expression,
|
46
44
|
)
|
47
|
-
from classiq.
|
45
|
+
from classiq.evaluators.classical_type_inference import (
|
48
46
|
infer_classical_type,
|
49
47
|
)
|
50
|
-
from classiq.
|
48
|
+
from classiq.evaluators.quantum_type_utils import (
|
51
49
|
copy_type_information,
|
52
50
|
set_element_type,
|
53
51
|
set_length,
|
54
52
|
set_size,
|
55
53
|
)
|
54
|
+
from classiq.model_expansions.closure import FunctionClosure
|
56
55
|
from classiq.model_expansions.scope import (
|
57
56
|
Evaluated,
|
58
57
|
QuantumSymbol,
|
@@ -81,7 +80,10 @@ def evaluate_parameter_types_from_args(
|
|
81
80
|
|
82
81
|
return [
|
83
82
|
_evaluate_type_from_arg(
|
84
|
-
parameter,
|
83
|
+
parameter,
|
84
|
+
argument,
|
85
|
+
Scope(parent=closure.scope | signature_scope),
|
86
|
+
closure.name,
|
85
87
|
)
|
86
88
|
for parameter, argument in zip(parameters, arguments)
|
87
89
|
]
|
@@ -142,8 +144,14 @@ def _cast(
|
|
142
144
|
|
143
145
|
|
144
146
|
def _evaluate_type_from_arg(
|
145
|
-
parameter: PositionalArg,
|
147
|
+
parameter: PositionalArg,
|
148
|
+
argument: Evaluated,
|
149
|
+
inner_scope: Scope,
|
150
|
+
function_name: str,
|
146
151
|
) -> PositionalArg:
|
152
|
+
# FIXME: Remove suzuki_trotter overloading (CLS-2912)
|
153
|
+
if function_name == "suzuki_trotter" and parameter.name == "pauli_operator":
|
154
|
+
return parameter
|
147
155
|
if isinstance(parameter, ClassicalParameterDeclaration):
|
148
156
|
updated_classical_type = evaluate_type_in_classical_symbol(
|
149
157
|
parameter.classical_type.model_copy(), inner_scope, parameter.name
|
@@ -310,13 +318,7 @@ def evaluate_type_in_classical_symbol(
|
|
310
318
|
type_to_update: ClassicalType, scope: Scope, param_name: str
|
311
319
|
) -> ClassicalType:
|
312
320
|
updated_type: ClassicalType
|
313
|
-
if isinstance(type_to_update,
|
314
|
-
updated_type = ClassicalArray(
|
315
|
-
element_type=evaluate_type_in_classical_symbol(
|
316
|
-
type_to_update.element_type, scope, param_name
|
317
|
-
)
|
318
|
-
)
|
319
|
-
elif isinstance(type_to_update, ClassicalArray):
|
321
|
+
if isinstance(type_to_update, ClassicalArray):
|
320
322
|
length = type_to_update.length
|
321
323
|
if length is not None:
|
322
324
|
new_length = _eval_expr(
|
classiq/execution/__init__.py
CHANGED
@@ -11,7 +11,14 @@ from .execution_session import ExecutionSession
|
|
11
11
|
from .iqcc import generate_iqcc_token, generate_iqcc_token_async
|
12
12
|
from .jobs import ExecutionJob, get_execution_jobs, get_execution_jobs_async
|
13
13
|
from .qnn import execute_qnn
|
14
|
-
from .user_budgets import
|
14
|
+
from .user_budgets import (
|
15
|
+
clear_budget_limit,
|
16
|
+
clear_budget_limit_async,
|
17
|
+
get_budget,
|
18
|
+
get_budget_async,
|
19
|
+
set_budget_limit,
|
20
|
+
set_budget_limit_async,
|
21
|
+
)
|
15
22
|
|
16
23
|
__all__ = (
|
17
24
|
_be_all
|
@@ -30,6 +37,10 @@ __all__ = (
|
|
30
37
|
"generate_iqcc_token_async",
|
31
38
|
"get_budget",
|
32
39
|
"get_budget_async",
|
40
|
+
"set_budget_limit",
|
41
|
+
"set_budget_limit_async",
|
42
|
+
"clear_budget_limit",
|
43
|
+
"clear_budget_limit_async",
|
33
44
|
]
|
34
45
|
)
|
35
46
|
|
@@ -1,24 +1,41 @@
|
|
1
|
+
import inspect
|
1
2
|
import random
|
3
|
+
import warnings
|
2
4
|
from types import TracebackType
|
3
|
-
from typing import Callable, Optional, Union, cast
|
4
|
-
|
5
|
-
import numpy as np
|
5
|
+
from typing import Any, Callable, Optional, Union, cast
|
6
6
|
|
7
7
|
from classiq.interface.chemistry.operator import PauliOperator, pauli_integers_to_str
|
8
|
-
from classiq.interface.exceptions import
|
9
|
-
|
8
|
+
from classiq.interface.exceptions import (
|
9
|
+
ClassiqDeprecationWarning,
|
10
|
+
ClassiqError,
|
11
|
+
ClassiqValueError,
|
12
|
+
)
|
13
|
+
from classiq.interface.execution.primitives import (
|
14
|
+
EstimateInput,
|
15
|
+
MinimizeClassicalCostInput,
|
16
|
+
MinimizeQuantumCostInput,
|
17
|
+
PrimitivesInput,
|
18
|
+
)
|
19
|
+
from classiq.interface.executor.estimate_cost import estimate_cost
|
10
20
|
from classiq.interface.executor.execution_preferences import ExecutionPreferences
|
21
|
+
from classiq.interface.executor.execution_result import TaggedMinimizeResult
|
11
22
|
from classiq.interface.executor.result import (
|
12
23
|
EstimationResult,
|
13
24
|
ExecutionDetails,
|
14
25
|
ParsedState,
|
15
26
|
)
|
16
27
|
from classiq.interface.generator.arith import number_utils
|
28
|
+
from classiq.interface.generator.expressions.expression import Expression
|
17
29
|
from classiq.interface.generator.functions.qmod_python_interface import QmodPyStruct
|
18
30
|
from classiq.interface.generator.quantum_program import (
|
19
31
|
QuantumProgram,
|
20
32
|
)
|
21
|
-
from classiq.interface.
|
33
|
+
from classiq.interface.helpers.custom_pydantic_types import PydanticPauliList
|
34
|
+
from classiq.interface.model.quantum_type import (
|
35
|
+
QuantumBit,
|
36
|
+
QuantumNumeric,
|
37
|
+
RegisterQuantumTypeDict,
|
38
|
+
)
|
22
39
|
|
23
40
|
from classiq._internals import async_utils
|
24
41
|
from classiq._internals.api_wrapper import ApiWrapper
|
@@ -27,38 +44,24 @@ from classiq.applications.combinatorial_helpers.pauli_helpers.pauli_utils import
|
|
27
44
|
_pauli_dict_to_pauli_terms,
|
28
45
|
)
|
29
46
|
from classiq.execution.jobs import ExecutionJob
|
30
|
-
from classiq.qmod.builtins import PauliTerm
|
31
47
|
from classiq.qmod.builtins.classical_execution_primitives import (
|
32
48
|
CARRAY_SEPARATOR,
|
33
49
|
ExecutionParams,
|
34
50
|
)
|
51
|
+
from classiq.qmod.builtins.structs import PauliTerm, SparsePauliOp
|
52
|
+
from classiq.qmod.qmod_variable import (
|
53
|
+
QmodExpressionCreator,
|
54
|
+
create_qvar_from_quantum_type,
|
55
|
+
)
|
35
56
|
|
36
|
-
Hamiltonian =
|
37
|
-
ParsedExecutionParams = dict[str, Union[float, int]]
|
57
|
+
Hamiltonian = SparsePauliOp
|
38
58
|
ExecutionParameters = Optional[Union[ExecutionParams, list[ExecutionParams]]]
|
59
|
+
ParsedExecutionParams = dict[str, Union[float, int]]
|
39
60
|
ParsedExecutionParameters = Optional[
|
40
61
|
Union[ParsedExecutionParams, list[ParsedExecutionParams]]
|
41
62
|
]
|
42
63
|
|
43
64
|
|
44
|
-
def hamiltonian_to_pauli_terms(hamiltonian: Hamiltonian) -> list[PauliTerm]:
|
45
|
-
if isinstance(hamiltonian[0], PauliTerm):
|
46
|
-
return cast(list[PauliTerm], hamiltonian)
|
47
|
-
else:
|
48
|
-
return _pauli_dict_to_pauli_terms(cast(list[QmodPyStruct], hamiltonian))
|
49
|
-
|
50
|
-
|
51
|
-
def _hamiltonian_to_pauli_operator(hamiltonian: Hamiltonian) -> PauliOperator:
|
52
|
-
pauli_list = [
|
53
|
-
(
|
54
|
-
pauli_integers_to_str(elem.pauli), # type: ignore[arg-type]
|
55
|
-
elem.coefficient,
|
56
|
-
)
|
57
|
-
for elem in hamiltonian_to_pauli_terms(hamiltonian)
|
58
|
-
]
|
59
|
-
return PauliOperator(pauli_list=pauli_list)
|
60
|
-
|
61
|
-
|
62
65
|
def parse_params(params: ExecutionParams) -> ParsedExecutionParams:
|
63
66
|
result = {}
|
64
67
|
for key, values in params.items():
|
@@ -73,6 +76,20 @@ def parse_params(params: ExecutionParams) -> ParsedExecutionParams:
|
|
73
76
|
return result
|
74
77
|
|
75
78
|
|
79
|
+
def _hamiltonian_deprecation_warning(hamiltonian: Any) -> None:
|
80
|
+
if isinstance(hamiltonian, list):
|
81
|
+
warnings.warn(
|
82
|
+
(
|
83
|
+
"Parameter type list[PauliTerm] to 'ExecutionSession' methods is "
|
84
|
+
"deprecated and will no longer be supported starting on 21/7/2025 "
|
85
|
+
"at the earliest. Instead, send a 'SparsePauliOp' (see "
|
86
|
+
"https://docs.classiq.io/latest/qmod-reference/language-reference/classical-types/#hamiltonians)."
|
87
|
+
),
|
88
|
+
ClassiqDeprecationWarning,
|
89
|
+
stacklevel=3,
|
90
|
+
)
|
91
|
+
|
92
|
+
|
76
93
|
class ExecutionSession:
|
77
94
|
"""
|
78
95
|
A session for executing a quantum program.
|
@@ -230,11 +247,18 @@ class ExecutionSession:
|
|
230
247
|
Returns:
|
231
248
|
EstimationResult: The result of the estimation.
|
232
249
|
"""
|
233
|
-
|
250
|
+
_hamiltonian_deprecation_warning(hamiltonian)
|
251
|
+
job = self.submit_estimate(
|
252
|
+
hamiltonian=hamiltonian, parameters=parameters, _check_deprecation=False
|
253
|
+
)
|
234
254
|
return job.get_estimate_result(_http_client=self._async_client)
|
235
255
|
|
236
256
|
def submit_estimate(
|
237
|
-
self,
|
257
|
+
self,
|
258
|
+
hamiltonian: Hamiltonian,
|
259
|
+
parameters: Optional[ExecutionParams] = None,
|
260
|
+
*,
|
261
|
+
_check_deprecation: bool = True,
|
238
262
|
) -> ExecutionJob:
|
239
263
|
"""
|
240
264
|
Initiates an execution job with the `estimate` primitive.
|
@@ -249,9 +273,11 @@ class ExecutionSession:
|
|
249
273
|
Returns:
|
250
274
|
The execution job.
|
251
275
|
"""
|
276
|
+
if _check_deprecation:
|
277
|
+
_hamiltonian_deprecation_warning(hamiltonian)
|
252
278
|
execution_primitives_input = PrimitivesInput(
|
253
279
|
estimate=EstimateInput(
|
254
|
-
hamiltonian=_hamiltonian_to_pauli_operator(hamiltonian),
|
280
|
+
hamiltonian=self._hamiltonian_to_pauli_operator(hamiltonian),
|
255
281
|
parameters=(
|
256
282
|
[parse_params(parameters)] if parameters is not None else [{}]
|
257
283
|
),
|
@@ -272,11 +298,18 @@ class ExecutionSession:
|
|
272
298
|
Returns:
|
273
299
|
List[EstimationResult]: The results of all the estimation iterations.
|
274
300
|
"""
|
275
|
-
|
301
|
+
_hamiltonian_deprecation_warning(hamiltonian)
|
302
|
+
job = self.submit_batch_estimate(
|
303
|
+
hamiltonian=hamiltonian, parameters=parameters, _check_deprecation=False
|
304
|
+
)
|
276
305
|
return job.get_batch_estimate_result(_http_client=self._async_client)
|
277
306
|
|
278
307
|
def submit_batch_estimate(
|
279
|
-
self,
|
308
|
+
self,
|
309
|
+
hamiltonian: Hamiltonian,
|
310
|
+
parameters: list[ExecutionParams],
|
311
|
+
*,
|
312
|
+
_check_deprecation: bool = True,
|
280
313
|
) -> ExecutionJob:
|
281
314
|
"""
|
282
315
|
Initiates an execution job with the `batch_estimate` primitive.
|
@@ -291,14 +324,119 @@ class ExecutionSession:
|
|
291
324
|
Returns:
|
292
325
|
The execution job.
|
293
326
|
"""
|
327
|
+
if _check_deprecation:
|
328
|
+
_hamiltonian_deprecation_warning(hamiltonian)
|
294
329
|
execution_primitives_input = PrimitivesInput(
|
295
330
|
estimate=EstimateInput(
|
296
|
-
hamiltonian=_hamiltonian_to_pauli_operator(hamiltonian),
|
331
|
+
hamiltonian=self._hamiltonian_to_pauli_operator(hamiltonian),
|
297
332
|
parameters=[parse_params(params) for params in parameters],
|
298
333
|
)
|
299
334
|
)
|
300
335
|
return self._execute(execution_primitives_input)
|
301
336
|
|
337
|
+
def minimize(
|
338
|
+
self,
|
339
|
+
cost_function: Union[Hamiltonian, QmodExpressionCreator],
|
340
|
+
initial_params: ExecutionParams,
|
341
|
+
max_iteration: int,
|
342
|
+
quantile: float = 1.0,
|
343
|
+
) -> list[tuple[float, ExecutionParams]]:
|
344
|
+
"""
|
345
|
+
Minimizes the given cost function using the quantum program.
|
346
|
+
Args:
|
347
|
+
cost_function: The cost function to minimize. It can be one of the following:
|
348
|
+
- A quantum cost function defined by a Hamiltonian.
|
349
|
+
- A classical cost function represented as a callable that returns a Qmod expression.
|
350
|
+
The callable should accept `QVar`s as arguments and use names matching the Model outputs.
|
351
|
+
initial_params: The initial parameters for the minimization.
|
352
|
+
Only Models with exactly one execution parameter are supported. This parameter must be of type
|
353
|
+
`CReal` or `CArray`. The dictionary must contain a single key-value pair, where:
|
354
|
+
- The key is the name of the parameter.
|
355
|
+
- The value is either a float or a list of floats.
|
356
|
+
max_iteration: The maximum number of iterations for the minimization.
|
357
|
+
quantile: The quantile to use for cost estimation.
|
358
|
+
Returns:
|
359
|
+
A list of tuples, each containing the estimated cost and the corresponding parameters for that iteration.
|
360
|
+
`cost` is a float, and `parameters` is a dictionary matching the execution parameter format.
|
361
|
+
"""
|
362
|
+
_hamiltonian_deprecation_warning(cost_function)
|
363
|
+
job = self.submit_minimize(
|
364
|
+
cost_function=cost_function,
|
365
|
+
initial_params=initial_params,
|
366
|
+
max_iteration=max_iteration,
|
367
|
+
quantile=quantile,
|
368
|
+
_check_deprecation=False,
|
369
|
+
)
|
370
|
+
result = job.get_minimization_result(_http_client=self._async_client)
|
371
|
+
|
372
|
+
return self._minimize_result_to_result(
|
373
|
+
result=result, initial_params=initial_params
|
374
|
+
)
|
375
|
+
|
376
|
+
def submit_minimize(
|
377
|
+
self,
|
378
|
+
cost_function: Union[Hamiltonian, QmodExpressionCreator],
|
379
|
+
initial_params: ExecutionParams,
|
380
|
+
max_iteration: int,
|
381
|
+
quantile: float = 1.0,
|
382
|
+
*,
|
383
|
+
_check_deprecation: bool = True,
|
384
|
+
) -> ExecutionJob:
|
385
|
+
"""
|
386
|
+
Initiates an execution job with the `minimize` primitive.
|
387
|
+
|
388
|
+
This is a non-blocking version of `minimize`: it gets the same parameters and initiates the same execution job, but instead
|
389
|
+
of waiting for the result, it returns the job object immediately.
|
390
|
+
|
391
|
+
Args:
|
392
|
+
cost_function: The cost function to minimize. It can be one of the following:
|
393
|
+
- A quantum cost function defined by a Hamiltonian.
|
394
|
+
- A classical cost function represented as a callable that returns a Qmod expression.
|
395
|
+
The callable should accept `QVar`s as arguments and use names matching the Model outputs.
|
396
|
+
initial_params: The initial parameters for the minimization.
|
397
|
+
Only Models with exactly one execution parameter are supported. This parameter must be of type
|
398
|
+
`CReal` or `CArray`. The dictionary must contain a single key-value pair, where:
|
399
|
+
- The key is the name of the parameter.
|
400
|
+
- The value is either a float or a list of floats.
|
401
|
+
max_iteration: The maximum number of iterations for the minimization.
|
402
|
+
quantile: The quantile to use for cost estimation.
|
403
|
+
|
404
|
+
Returns:
|
405
|
+
The execution job.
|
406
|
+
"""
|
407
|
+
if _check_deprecation:
|
408
|
+
_hamiltonian_deprecation_warning(cost_function)
|
409
|
+
if len(initial_params) != 1:
|
410
|
+
raise ClassiqValueError(
|
411
|
+
"The initial parameters must be a dictionary with a single key-value pair."
|
412
|
+
)
|
413
|
+
|
414
|
+
_cost_function: Union[PauliOperator, Expression]
|
415
|
+
_initial_params = parse_params(initial_params)
|
416
|
+
minimize: Union[MinimizeQuantumCostInput, MinimizeClassicalCostInput]
|
417
|
+
if callable(cost_function):
|
418
|
+
circuit_output_types = self.program.model.circuit_output_types
|
419
|
+
_cost_function = self._create_qmod_expression(
|
420
|
+
circuit_output_types, cost_function
|
421
|
+
)
|
422
|
+
minimize = MinimizeClassicalCostInput(
|
423
|
+
cost_function=_cost_function,
|
424
|
+
initial_params=_initial_params,
|
425
|
+
max_iteration=max_iteration,
|
426
|
+
quantile=quantile,
|
427
|
+
)
|
428
|
+
else:
|
429
|
+
_cost_function = self._hamiltonian_to_pauli_operator(cost_function)
|
430
|
+
minimize = MinimizeQuantumCostInput(
|
431
|
+
cost_function=_cost_function,
|
432
|
+
initial_params=_initial_params,
|
433
|
+
max_iteration=max_iteration,
|
434
|
+
quantile=quantile,
|
435
|
+
)
|
436
|
+
|
437
|
+
execution_primitives_input = PrimitivesInput(minimize=minimize)
|
438
|
+
return self._execute(execution_primitives_input)
|
439
|
+
|
302
440
|
def estimate_cost(
|
303
441
|
self,
|
304
442
|
cost_func: Callable[[ParsedState], float],
|
@@ -319,23 +457,8 @@ class ExecutionSession:
|
|
319
457
|
See Also:
|
320
458
|
sample
|
321
459
|
"""
|
322
|
-
if quantile < 0 or quantile > 1:
|
323
|
-
raise ClassiqValueError("'quantile' must be between 0 and 1")
|
324
460
|
res = self.sample(parameters)
|
325
|
-
|
326
|
-
counts = np.array(res.parsed_counts)
|
327
|
-
costs = np.vectorize(lambda sample: cost_func(sample.state))(counts)
|
328
|
-
shots = np.vectorize(lambda sample: sample.shots)(counts)
|
329
|
-
|
330
|
-
if quantile == 1:
|
331
|
-
return float(np.average(costs, weights=shots))
|
332
|
-
costs = np.repeat(costs, shots)
|
333
|
-
sort_idx = costs.argsort()
|
334
|
-
sort_idx = sort_idx[: int(quantile * len(costs))]
|
335
|
-
costs = costs[sort_idx]
|
336
|
-
if costs.size == 0:
|
337
|
-
return np.nan
|
338
|
-
return float(np.average(costs))
|
461
|
+
return estimate_cost(cost_func, res.parsed_counts, quantile=quantile)
|
339
462
|
|
340
463
|
def set_measured_state_filter(
|
341
464
|
self,
|
@@ -387,3 +510,69 @@ class ExecutionSession:
|
|
387
510
|
)
|
388
511
|
|
389
512
|
self.program.model.register_filter_bitstrings[output_name] = legal_bitstrings
|
513
|
+
|
514
|
+
@staticmethod
|
515
|
+
def _hamiltonian_to_pauli_operator(hamiltonian: Hamiltonian) -> PauliOperator:
|
516
|
+
pauli_list: PydanticPauliList
|
517
|
+
# FIXME: Remove compatibility (CLS-2912)
|
518
|
+
if isinstance(hamiltonian, list): # type:ignore[unreachable]
|
519
|
+
pauli_list = [ # type:ignore[unreachable]
|
520
|
+
(
|
521
|
+
pauli_integers_to_str(elem.pauli),
|
522
|
+
cast(complex, elem.coefficient),
|
523
|
+
)
|
524
|
+
for elem in ExecutionSession._hamiltonian_to_pauli_terms(hamiltonian)
|
525
|
+
]
|
526
|
+
return PauliOperator(pauli_list=pauli_list)
|
527
|
+
pauli_list = []
|
528
|
+
for term in cast(list, hamiltonian.terms):
|
529
|
+
paulis = ["I"] * cast(int, hamiltonian.num_qubits)
|
530
|
+
for indexed_pauli in term.paulis:
|
531
|
+
paulis[len(paulis) - indexed_pauli.index - 1] = indexed_pauli.pauli.name
|
532
|
+
pauli_list.append(("".join(paulis), term.coefficient))
|
533
|
+
return PauliOperator(pauli_list=pauli_list)
|
534
|
+
|
535
|
+
@staticmethod
|
536
|
+
def _create_qmod_expression(
|
537
|
+
circuit_output_types: RegisterQuantumTypeDict,
|
538
|
+
qmod_expression_creator: QmodExpressionCreator,
|
539
|
+
) -> Expression:
|
540
|
+
symbolic_output = {
|
541
|
+
name: create_qvar_from_quantum_type(reg.quantum_types, name)
|
542
|
+
for name, reg in circuit_output_types.items()
|
543
|
+
}
|
544
|
+
for name in inspect.signature(qmod_expression_creator).parameters.keys():
|
545
|
+
if name not in symbolic_output:
|
546
|
+
raise ClassiqValueError(
|
547
|
+
f"The provided QVar: {name} does not match the model outputs: {tuple(circuit_output_types.keys())}. "
|
548
|
+
)
|
549
|
+
|
550
|
+
qmod_expression = qmod_expression_creator(**symbolic_output)
|
551
|
+
return Expression(expr=str(qmod_expression))
|
552
|
+
|
553
|
+
@staticmethod
|
554
|
+
def _hamiltonian_to_pauli_terms(hamiltonian: list) -> list[PauliTerm]:
|
555
|
+
if isinstance(hamiltonian[0], PauliTerm):
|
556
|
+
return cast(list[PauliTerm], hamiltonian)
|
557
|
+
else:
|
558
|
+
return _pauli_dict_to_pauli_terms(cast(list[QmodPyStruct], hamiltonian))
|
559
|
+
|
560
|
+
@staticmethod
|
561
|
+
def _minimize_result_to_result(
|
562
|
+
result: TaggedMinimizeResult, initial_params: ExecutionParams
|
563
|
+
) -> list[tuple[float, ExecutionParams]]:
|
564
|
+
param_name = next(iter(initial_params.keys()))
|
565
|
+
param_value = initial_params[param_name]
|
566
|
+
return [
|
567
|
+
(
|
568
|
+
res.expectation_value,
|
569
|
+
{
|
570
|
+
param_name: (
|
571
|
+
res.parameters[0]
|
572
|
+
if isinstance(param_value, (float, int))
|
573
|
+
else res.parameters
|
574
|
+
)
|
575
|
+
},
|
576
|
+
)
|
577
|
+
for res in result.value
|
578
|
+
]
|
classiq/execution/jobs.py
CHANGED
@@ -10,7 +10,10 @@ from classiq.interface.exceptions import (
|
|
10
10
|
ClassiqError,
|
11
11
|
)
|
12
12
|
from classiq.interface.executor.execution_request import ExecutionJobDetails, JobCost
|
13
|
-
from classiq.interface.executor.execution_result import
|
13
|
+
from classiq.interface.executor.execution_result import (
|
14
|
+
ResultsCollection,
|
15
|
+
TaggedMinimizeResult,
|
16
|
+
)
|
14
17
|
from classiq.interface.executor.result import (
|
15
18
|
EstimationResult,
|
16
19
|
EstimationResults,
|
@@ -244,6 +247,28 @@ class ExecutionJob:
|
|
244
247
|
|
245
248
|
raise ClassiqExecutionResultError("batch_estimate")
|
246
249
|
|
250
|
+
def get_minimization_result(
|
251
|
+
self, _http_client: Optional[httpx.AsyncClient] = None
|
252
|
+
) -> TaggedMinimizeResult:
|
253
|
+
"""
|
254
|
+
Returns the job's result as a single minimization result after validation. If the result is not yet available, waits for it.
|
255
|
+
|
256
|
+
Returns:
|
257
|
+
The minimization result of the execution job.
|
258
|
+
|
259
|
+
Raises:
|
260
|
+
ClassiqExecutionResultError: In case the result does not contain a single minimization result.
|
261
|
+
ClassiqAPIError: In case the job has failed.
|
262
|
+
"""
|
263
|
+
results = self.result(_http_client=_http_client)
|
264
|
+
if len(results) != 1:
|
265
|
+
raise ClassiqExecutionResultError("minimization")
|
266
|
+
|
267
|
+
result = results[0]
|
268
|
+
if isinstance(result, TaggedMinimizeResult):
|
269
|
+
return result
|
270
|
+
raise ClassiqExecutionResultError("minimization")
|
271
|
+
|
247
272
|
async def poll_async(
|
248
273
|
self,
|
249
274
|
timeout_sec: Optional[float] = None,
|
classiq/execution/qnn.py
CHANGED
@@ -15,7 +15,7 @@ from classiq.interface.executor.quantum_code import Arguments, MultipleArguments
|
|
15
15
|
|
16
16
|
from classiq import QuantumProgram
|
17
17
|
from classiq.applications.combinatorial_helpers.pauli_helpers.pauli_utils import (
|
18
|
-
|
18
|
+
pauli_operator_to_sparse_hamiltonian,
|
19
19
|
)
|
20
20
|
from classiq.execution.execution_session import ExecutionSession
|
21
21
|
|
@@ -27,7 +27,7 @@ def _execute_qnn_estimate(
|
|
27
27
|
arguments: list[Arguments],
|
28
28
|
observable: PauliOperator,
|
29
29
|
) -> ResultsCollection:
|
30
|
-
hamiltonian =
|
30
|
+
hamiltonian = pauli_operator_to_sparse_hamiltonian(observable.pauli_list)
|
31
31
|
return [
|
32
32
|
TaggedEstimationResult(
|
33
33
|
name=DEFAULT_RESULT_NAME,
|
@@ -36,3 +36,42 @@ async def get_budget_async(
|
|
36
36
|
|
37
37
|
|
38
38
|
get_budget = syncify_function(get_budget_async)
|
39
|
+
|
40
|
+
|
41
|
+
async def set_budget_limit_async(
|
42
|
+
provider_vendor: ProviderVendor,
|
43
|
+
limit: float,
|
44
|
+
) -> UserBudgets:
|
45
|
+
provider = PROVIDER_MAPPER.get(provider_vendor, None)
|
46
|
+
if not provider:
|
47
|
+
raise ValueError(f"Unsupported provider: {provider_vendor}")
|
48
|
+
|
49
|
+
budget = get_budget(provider_vendor)
|
50
|
+
if budget is None:
|
51
|
+
raise ValueError(f"No budget found for provider: {provider_vendor}")
|
52
|
+
|
53
|
+
if limit <= 0:
|
54
|
+
raise ValueError("Budget limit must be greater than zero.")
|
55
|
+
|
56
|
+
if limit > budget.budgets[0].available_budget:
|
57
|
+
print( # noqa: T201
|
58
|
+
f"Budget limit {limit} exceeds available budget {budget.budgets[0].available_budget} for provider {provider_vendor}.\n"
|
59
|
+
"Setting budget limit to the maximum available budget."
|
60
|
+
)
|
61
|
+
budgets_list = await ApiWrapper().call_set_budget_limit(provider, limit)
|
62
|
+
return UserBudgets(budgets=[budgets_list])
|
63
|
+
|
64
|
+
|
65
|
+
set_budget_limit = syncify_function(set_budget_limit_async)
|
66
|
+
|
67
|
+
|
68
|
+
async def clear_budget_limit_async(provider_vendor: ProviderVendor) -> UserBudgets:
|
69
|
+
provider = PROVIDER_MAPPER.get(provider_vendor, None)
|
70
|
+
if not provider:
|
71
|
+
raise ValueError(f"Unsupported provider: {provider_vendor}")
|
72
|
+
|
73
|
+
budgets_list = await ApiWrapper().call_clear_budget_limit(provider)
|
74
|
+
return UserBudgets(budgets=[budgets_list])
|
75
|
+
|
76
|
+
|
77
|
+
clear_budget_limit = syncify_function(clear_budget_limit_async)
|
classiq/interface/_version.py
CHANGED
@@ -0,0 +1 @@
|
|
1
|
+
DEFAULT_DECIMAL_PRECISION = 4
|
@@ -9,7 +9,6 @@ from classiq.interface.generator.generated_circuit_data import (
|
|
9
9
|
FunctionDebugInfoInterface,
|
10
10
|
StatementType,
|
11
11
|
)
|
12
|
-
from classiq.interface.model.block import Block
|
13
12
|
from classiq.interface.model.handle_binding import ConcreteHandleBinding
|
14
13
|
from classiq.interface.model.port_declaration import PortDeclaration
|
15
14
|
from classiq.interface.model.quantum_function_call import ArgValue
|
@@ -82,9 +81,6 @@ def get_back_refs(
|
|
82
81
|
) -> list[ConcreteQuantumStatement]:
|
83
82
|
back_refs: list[ConcreteQuantumStatement] = []
|
84
83
|
while (node := debug_info.node) is not None:
|
85
|
-
# For backwards compatibility, we make sure that the back_ref is not a block
|
86
|
-
# Remove this check when we start saving blocks in the debug info collection.
|
87
|
-
assert not isinstance(node, Block)
|
88
84
|
if len(back_refs) > 0 and node.back_ref == back_refs[0].back_ref:
|
89
85
|
break
|
90
86
|
back_refs.insert(0, node)
|
@@ -1,9 +1,10 @@
|
|
1
|
-
from typing import Optional
|
1
|
+
from typing import Annotated, Literal, Optional, Union
|
2
2
|
|
3
3
|
from pydantic import BaseModel, Field
|
4
4
|
|
5
5
|
from classiq.interface.chemistry.operator import PauliOperator
|
6
6
|
from classiq.interface.executor.quantum_code import Arguments
|
7
|
+
from classiq.interface.generator.expressions.expression import Expression
|
7
8
|
from classiq.interface.helpers.custom_encoders import CUSTOM_ENCODERS
|
8
9
|
|
9
10
|
|
@@ -12,7 +13,34 @@ class EstimateInput(BaseModel, json_encoders=CUSTOM_ENCODERS):
|
|
12
13
|
parameters: list[Arguments]
|
13
14
|
|
14
15
|
|
16
|
+
class MinimizeCostInput(BaseModel, json_encoders=CUSTOM_ENCODERS):
|
17
|
+
initial_params: Arguments
|
18
|
+
max_iteration: int
|
19
|
+
quantile: float
|
20
|
+
|
21
|
+
|
22
|
+
class MinimizeClassicalCostInput(MinimizeCostInput):
|
23
|
+
cost_function: Expression
|
24
|
+
kind: Literal["MinimizeClassicalCostInput"] = Field(
|
25
|
+
default="MinimizeClassicalCostInput"
|
26
|
+
)
|
27
|
+
|
28
|
+
|
29
|
+
class MinimizeQuantumCostInput(MinimizeCostInput):
|
30
|
+
cost_function: PauliOperator
|
31
|
+
kind: Literal["MinimizeQuantumCostInput"] = Field(
|
32
|
+
default="MinimizeQuantumCostInput"
|
33
|
+
)
|
34
|
+
|
35
|
+
|
36
|
+
ConcreteMinimizeCostInput = Annotated[
|
37
|
+
Union[MinimizeQuantumCostInput, MinimizeClassicalCostInput],
|
38
|
+
Field(discriminator="kind"),
|
39
|
+
]
|
40
|
+
|
41
|
+
|
15
42
|
class PrimitivesInput(BaseModel, json_encoders=CUSTOM_ENCODERS):
|
16
43
|
sample: Optional[list[Arguments]] = Field(default=None)
|
17
44
|
estimate: Optional[EstimateInput] = Field(default=None)
|
45
|
+
minimize: Optional[ConcreteMinimizeCostInput] = Field(default=None)
|
18
46
|
random_seed: Optional[int] = Field(default=None)
|