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.
Files changed (103) hide show
  1. classiq/_internals/api_wrapper.py +27 -0
  2. classiq/applications/chemistry/chemistry_model_constructor.py +0 -2
  3. classiq/applications/chemistry/hartree_fock.py +68 -0
  4. classiq/applications/chemistry/mapping.py +85 -0
  5. classiq/applications/chemistry/op_utils.py +79 -0
  6. classiq/applications/chemistry/problems.py +195 -0
  7. classiq/applications/chemistry/ucc.py +109 -0
  8. classiq/applications/chemistry/z2_symmetries.py +368 -0
  9. classiq/applications/combinatorial_helpers/pauli_helpers/pauli_utils.py +30 -1
  10. classiq/applications/combinatorial_optimization/combinatorial_problem.py +20 -42
  11. classiq/{model_expansions/evaluators → evaluators}/arg_type_match.py +12 -4
  12. classiq/{model_expansions/evaluators → evaluators}/argument_types.py +1 -1
  13. classiq/evaluators/classical_expression.py +53 -0
  14. classiq/{model_expansions/evaluators → evaluators}/classical_type_inference.py +3 -4
  15. classiq/{model_expansions/evaluators → evaluators}/parameter_types.py +17 -15
  16. classiq/execution/__init__.py +12 -1
  17. classiq/execution/execution_session.py +238 -49
  18. classiq/execution/jobs.py +26 -1
  19. classiq/execution/qnn.py +2 -2
  20. classiq/execution/user_budgets.py +39 -0
  21. classiq/interface/_version.py +1 -1
  22. classiq/interface/constants.py +1 -0
  23. classiq/interface/debug_info/debug_info.py +0 -4
  24. classiq/interface/execution/primitives.py +29 -1
  25. classiq/interface/executor/estimate_cost.py +35 -0
  26. classiq/interface/executor/execution_result.py +13 -0
  27. classiq/interface/executor/result.py +116 -1
  28. classiq/interface/executor/user_budget.py +26 -33
  29. classiq/interface/generator/expressions/atomic_expression_functions.py +10 -1
  30. classiq/interface/generator/expressions/proxies/classical/any_classical_value.py +0 -6
  31. classiq/interface/generator/functions/builtins/internal_operators.py +2 -0
  32. classiq/interface/generator/functions/classical_type.py +2 -35
  33. classiq/interface/generator/functions/concrete_types.py +20 -3
  34. classiq/interface/generator/functions/type_modifier.py +0 -19
  35. classiq/interface/generator/generated_circuit_data.py +5 -18
  36. classiq/interface/generator/types/compilation_metadata.py +0 -3
  37. classiq/interface/ide/operation_registry.py +45 -0
  38. classiq/interface/ide/visual_model.py +68 -3
  39. classiq/interface/model/bounds.py +12 -2
  40. classiq/interface/model/model.py +12 -7
  41. classiq/interface/model/port_declaration.py +2 -24
  42. classiq/interface/model/quantum_expressions/arithmetic_operation.py +7 -4
  43. classiq/interface/model/variable_declaration_statement.py +33 -6
  44. classiq/interface/pretty_print/__init__.py +0 -0
  45. classiq/{qmod/native → interface/pretty_print}/expression_to_qmod.py +18 -11
  46. classiq/interface/server/routes.py +4 -0
  47. classiq/model_expansions/atomic_expression_functions_defs.py +47 -6
  48. classiq/model_expansions/function_builder.py +4 -1
  49. classiq/model_expansions/interpreters/base_interpreter.py +3 -3
  50. classiq/model_expansions/interpreters/generative_interpreter.py +16 -1
  51. classiq/model_expansions/quantum_operations/allocate.py +1 -1
  52. classiq/model_expansions/quantum_operations/assignment_result_processor.py +64 -22
  53. classiq/model_expansions/quantum_operations/bind.py +2 -2
  54. classiq/model_expansions/quantum_operations/bounds.py +7 -1
  55. classiq/model_expansions/quantum_operations/call_emitter.py +26 -20
  56. classiq/model_expansions/quantum_operations/classical_var_emitter.py +16 -0
  57. classiq/model_expansions/quantum_operations/variable_decleration.py +31 -11
  58. classiq/model_expansions/scope.py +7 -0
  59. classiq/model_expansions/scope_initialization.py +3 -3
  60. classiq/model_expansions/transformers/model_renamer.py +6 -4
  61. classiq/model_expansions/transformers/type_modifier_inference.py +81 -43
  62. classiq/model_expansions/transformers/var_splitter.py +1 -1
  63. classiq/model_expansions/visitors/symbolic_param_inference.py +2 -3
  64. classiq/open_library/functions/__init__.py +3 -2
  65. classiq/open_library/functions/amplitude_amplification.py +10 -18
  66. classiq/open_library/functions/discrete_sine_cosine_transform.py +5 -5
  67. classiq/open_library/functions/grover.py +14 -6
  68. classiq/open_library/functions/modular_exponentiation.py +22 -20
  69. classiq/open_library/functions/qaoa_penalty.py +8 -1
  70. classiq/open_library/functions/state_preparation.py +18 -32
  71. classiq/qmod/__init__.py +2 -0
  72. classiq/qmod/builtins/enums.py +23 -0
  73. classiq/qmod/builtins/functions/__init__.py +2 -0
  74. classiq/qmod/builtins/functions/exponentiation.py +32 -4
  75. classiq/qmod/builtins/operations.py +65 -1
  76. classiq/qmod/builtins/structs.py +55 -3
  77. classiq/qmod/classical_variable.py +74 -0
  78. classiq/qmod/declaration_inferrer.py +3 -2
  79. classiq/qmod/native/pretty_printer.py +20 -20
  80. classiq/qmod/pretty_print/expression_to_python.py +2 -1
  81. classiq/qmod/pretty_print/pretty_printer.py +35 -21
  82. classiq/qmod/python_classical_type.py +12 -5
  83. classiq/qmod/qfunc.py +2 -19
  84. classiq/qmod/qmod_constant.py +2 -5
  85. classiq/qmod/qmod_parameter.py +2 -5
  86. classiq/qmod/qmod_variable.py +61 -23
  87. classiq/qmod/quantum_expandable.py +5 -3
  88. classiq/qmod/quantum_function.py +49 -4
  89. classiq/qmod/semantics/annotation/qstruct_annotator.py +1 -1
  90. classiq/qmod/semantics/validation/main_validation.py +1 -9
  91. classiq/qmod/symbolic_type.py +2 -1
  92. classiq/qmod/utilities.py +0 -2
  93. classiq/qmod/write_qmod.py +1 -1
  94. {classiq-0.83.0.dist-info → classiq-0.85.0.dist-info}/METADATA +4 -1
  95. {classiq-0.83.0.dist-info → classiq-0.85.0.dist-info}/RECORD +101 -90
  96. classiq/interface/model/quantum_variable_declaration.py +0 -7
  97. classiq/model_expansions/evaluators/classical_expression.py +0 -36
  98. /classiq/{model_expansions/evaluators → evaluators}/__init__.py +0 -0
  99. /classiq/{model_expansions/evaluators → evaluators}/control.py +0 -0
  100. /classiq/{model_expansions → evaluators}/expression_evaluator.py +0 -0
  101. /classiq/{model_expansions/evaluators → evaluators}/quantum_type_utils.py +0 -0
  102. /classiq/{model_expansions/evaluators → evaluators}/type_type_match.py +0 -0
  103. {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.model_expansions.closure import FunctionClosure
43
- from classiq.model_expansions.evaluators.arg_type_match import check_type_match
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.model_expansions.evaluators.classical_type_inference import (
45
+ from classiq.evaluators.classical_type_inference import (
48
46
  infer_classical_type,
49
47
  )
50
- from classiq.model_expansions.evaluators.quantum_type_utils import (
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, argument, Scope(parent=closure.scope | signature_scope)
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, argument: Evaluated, inner_scope: Scope
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, ClassicalList):
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(
@@ -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 get_budget, get_budget_async
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 ClassiqError, ClassiqValueError
9
- from classiq.interface.execution.primitives import EstimateInput, PrimitivesInput
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.model.quantum_type import QuantumBit, QuantumNumeric
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 = Union[list[QmodPyStruct], list[PauliTerm]]
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
- job = self.submit_estimate(hamiltonian=hamiltonian, parameters=parameters)
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, hamiltonian: Hamiltonian, parameters: Optional[ExecutionParams] = None
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
- job = self.submit_batch_estimate(hamiltonian=hamiltonian, parameters=parameters)
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, hamiltonian: Hamiltonian, parameters: list[ExecutionParams]
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 ResultsCollection
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
- pauli_operator_to_hamiltonian,
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 = pauli_operator_to_hamiltonian(observable.pauli_list)
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)
@@ -3,5 +3,5 @@ from packaging.version import Version
3
3
  # This file was generated automatically
4
4
  # Please don't track in version control (DONTTRACK)
5
5
 
6
- SEMVER_VERSION = '0.83.0'
6
+ SEMVER_VERSION = '0.85.0'
7
7
  VERSION = str(Version(SEMVER_VERSION))
@@ -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)