classiq 0.60.1__py3-none-any.whl → 0.62.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 (108) hide show
  1. classiq/__init__.py +2 -0
  2. classiq/_internals/client.py +6 -1
  3. classiq/applications/__init__.py +1 -1
  4. classiq/applications/chemistry/__init__.py +7 -7
  5. classiq/applications/chemistry/chemistry_model_constructor.py +17 -6
  6. classiq/applications/combinatorial_helpers/optimization_model.py +9 -2
  7. classiq/applications/combinatorial_helpers/pyomo_utils.py +60 -9
  8. classiq/applications/combinatorial_optimization/__init__.py +7 -1
  9. classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +2 -0
  10. classiq/applications/combinatorial_optimization/combinatorial_problem.py +230 -0
  11. classiq/applications/finance/finance_model_constructor.py +6 -6
  12. classiq/applications/grover/grover_model_constructor.py +3 -0
  13. classiq/applications/libraries/qmci_library.py +1 -10
  14. classiq/applications/qnn/__init__.py +1 -1
  15. classiq/applications/qnn/datasets/__init__.py +8 -8
  16. classiq/applications/qsvm/qsvm.py +1 -1
  17. classiq/execution/__init__.py +0 -2
  18. classiq/execution/execution_session.py +6 -0
  19. classiq/execution/jobs.py +9 -1
  20. classiq/executor.py +1 -1
  21. classiq/interface/_version.py +1 -1
  22. classiq/interface/backend/backend_preferences.py +33 -15
  23. classiq/interface/backend/quantum_backend_providers.py +3 -1
  24. classiq/interface/executor/execution_preferences.py +1 -1
  25. classiq/interface/generator/application_apis/chemistry_declarations.py +1 -1
  26. classiq/interface/generator/application_apis/finance_declarations.py +2 -2
  27. classiq/interface/generator/arith/arithmetic.py +16 -1
  28. classiq/interface/generator/arith/arithmetic_expression_validator.py +4 -3
  29. classiq/interface/generator/copy.py +47 -0
  30. classiq/interface/generator/expressions/expression_constants.py +3 -0
  31. classiq/interface/generator/function_param_list_without_self_reference.py +2 -0
  32. classiq/interface/generator/generated_circuit_data.py +58 -20
  33. classiq/interface/generator/model/__init__.py +1 -1
  34. classiq/interface/generator/model/preferences/preferences.py +1 -1
  35. classiq/interface/generator/model/quantum_register.py +3 -3
  36. classiq/interface/generator/standard_gates/controlled_standard_gates.py +20 -32
  37. classiq/interface/generator/types/compilation_metadata.py +2 -1
  38. classiq/interface/ide/visual_model.py +1 -0
  39. classiq/interface/interface_version.py +1 -1
  40. classiq/interface/model/model.py +2 -3
  41. classiq/interface/model/quantum_function_call.py +4 -7
  42. classiq/interface/model/quantum_function_declaration.py +7 -0
  43. classiq/interface/model/quantum_lambda_function.py +10 -1
  44. classiq/interface/model/quantum_type.py +3 -1
  45. classiq/model_expansions/atomic_expression_functions_defs.py +3 -1
  46. classiq/model_expansions/capturing/captured_vars.py +36 -17
  47. classiq/model_expansions/capturing/mangling_utils.py +23 -15
  48. classiq/model_expansions/closure.py +6 -9
  49. classiq/model_expansions/evaluators/arg_type_match.py +7 -7
  50. classiq/model_expansions/expression_evaluator.py +5 -2
  51. classiq/model_expansions/function_builder.py +26 -4
  52. classiq/model_expansions/generative_functions.py +13 -91
  53. classiq/model_expansions/interpreter.py +75 -46
  54. classiq/model_expansions/quantum_operations/call_emitter.py +42 -29
  55. classiq/model_expansions/quantum_operations/classicalif.py +1 -1
  56. classiq/model_expansions/quantum_operations/control.py +5 -31
  57. classiq/model_expansions/quantum_operations/emitter.py +29 -17
  58. classiq/model_expansions/quantum_operations/expression_operation.py +3 -5
  59. classiq/model_expansions/quantum_operations/inplace_binary_operation.py +57 -15
  60. classiq/model_expansions/quantum_operations/invert.py +1 -6
  61. classiq/model_expansions/quantum_operations/phase.py +2 -5
  62. classiq/model_expansions/quantum_operations/power.py +0 -4
  63. classiq/model_expansions/quantum_operations/quantum_assignment_operation.py +19 -30
  64. classiq/model_expansions/quantum_operations/quantum_function_call.py +4 -2
  65. classiq/model_expansions/quantum_operations/shallow_emitter.py +161 -0
  66. classiq/model_expansions/quantum_operations/within_apply.py +0 -14
  67. classiq/model_expansions/scope.py +12 -16
  68. classiq/model_expansions/scope_initialization.py +0 -11
  69. classiq/model_expansions/sympy_conversion/expression_to_sympy.py +7 -0
  70. classiq/model_expansions/sympy_conversion/sympy_to_python.py +12 -2
  71. classiq/model_expansions/transformers/ast_renamer.py +26 -0
  72. classiq/model_expansions/transformers/var_splitter.py +11 -12
  73. classiq/model_expansions/visitors/variable_references.py +20 -12
  74. classiq/qmod/builtins/classical_execution_primitives.py +6 -6
  75. classiq/qmod/builtins/classical_functions.py +10 -10
  76. classiq/qmod/builtins/functions/__init__.py +89 -103
  77. classiq/qmod/builtins/functions/amplitude_estimation.py +1 -1
  78. classiq/qmod/builtins/functions/arithmetic.py +1 -1
  79. classiq/qmod/builtins/functions/discrete_sine_cosine_transform.py +6 -6
  80. classiq/qmod/builtins/functions/grover.py +5 -5
  81. classiq/qmod/builtins/functions/hea.py +1 -1
  82. classiq/qmod/builtins/functions/linear_pauli_rotation.py +2 -2
  83. classiq/qmod/builtins/functions/modular_exponentiation.py +8 -8
  84. classiq/qmod/builtins/functions/operators.py +1 -1
  85. classiq/qmod/builtins/functions/qaoa_penalty.py +5 -5
  86. classiq/qmod/builtins/functions/qft_functions.py +2 -2
  87. classiq/qmod/builtins/functions/qpe.py +9 -12
  88. classiq/qmod/builtins/functions/qsvt.py +177 -15
  89. classiq/qmod/builtins/functions/state_preparation.py +9 -9
  90. classiq/qmod/builtins/functions/swap_test.py +1 -1
  91. classiq/qmod/builtins/functions/utility_functions.py +2 -2
  92. classiq/qmod/builtins/functions/variational.py +2 -2
  93. classiq/qmod/builtins/operations.py +22 -83
  94. classiq/qmod/builtins/structs.py +9 -9
  95. classiq/qmod/native/pretty_printer.py +17 -19
  96. classiq/qmod/pretty_print/pretty_printer.py +9 -6
  97. classiq/qmod/python_classical_type.py +1 -5
  98. classiq/qmod/qmod_variable.py +2 -5
  99. classiq/qmod/quantum_expandable.py +20 -5
  100. classiq/qmod/quantum_function.py +23 -10
  101. classiq/qmod/semantics/static_semantics_visitor.py +34 -16
  102. classiq/qmod/semantics/validation/func_call_validation.py +9 -5
  103. classiq/qmod/semantics/validation/function_name_collisions_validation.py +23 -0
  104. classiq/qmod/symbolic.py +47 -47
  105. {classiq-0.60.1.dist-info → classiq-0.62.0.dist-info}/METADATA +2 -1
  106. {classiq-0.60.1.dist-info → classiq-0.62.0.dist-info}/RECORD +107 -103
  107. classiq/execution/qaoa.py +0 -86
  108. {classiq-0.60.1.dist-info → classiq-0.62.0.dist-info}/WHEEL +0 -0
classiq/__init__.py CHANGED
@@ -28,8 +28,10 @@ from classiq.applications.chemistry import (
28
28
  molecule_problem_to_qmod,
29
29
  )
30
30
  from classiq.applications.combinatorial_optimization import (
31
+ CombinatorialProblem,
31
32
  compute_qaoa_initial_point,
32
33
  construct_combinatorial_optimization_model,
34
+ execute_qaoa,
33
35
  pyo_model_to_hamiltonian,
34
36
  )
35
37
  from classiq.applications.finance import construct_finance_model
@@ -290,7 +290,12 @@ class Client:
290
290
  if http_client is None:
291
291
  return self.async_client()
292
292
  else:
293
- return _AsyncNullContext(enter_result=http_client)
293
+ if sys.version_info[0:2] < (3, 10):
294
+ # remove this `if` and the `_AsyncNullContext` class when we stop
295
+ # supporting python 3.9
296
+ return _AsyncNullContext(enter_result=http_client)
297
+ else:
298
+ return contextlib.nullcontext(enter_result=http_client)
294
299
 
295
300
  def sync_call_api(
296
301
  self,
@@ -1,8 +1,8 @@
1
1
  from classiq.applications import chemistry, combinatorial_optimization, finance, qsvm
2
2
 
3
3
  __all__ = [
4
- "combinatorial_optimization",
5
4
  "chemistry",
5
+ "combinatorial_optimization",
6
6
  "finance",
7
7
  "qsvm",
8
8
  ]
@@ -19,18 +19,18 @@ from .chemistry_model_constructor import (
19
19
  )
20
20
 
21
21
  __all__ = [
22
- "Molecule",
23
- "MoleculeProblem",
22
+ "ChemistryExecutionParameters",
23
+ "FermionicOperator",
24
24
  "GroundStateProblem",
25
+ "HEAParameters",
26
+ "HVAParameters",
25
27
  "HamiltonianProblem",
26
- "PauliOperators",
28
+ "Molecule",
29
+ "MoleculeProblem",
27
30
  "PauliOperator",
28
- "FermionicOperator",
31
+ "PauliOperators",
29
32
  "SummedFermionicOperator",
30
33
  "UCCParameters",
31
- "HVAParameters",
32
- "HEAParameters",
33
- "ChemistryExecutionParameters",
34
34
  "construct_chemistry_model",
35
35
  "molecule_problem_to_qmod",
36
36
  ]
@@ -1,6 +1,6 @@
1
1
  # flake8: noqa
2
2
 
3
- from typing import Optional
3
+ from typing import Optional, cast
4
4
  from collections.abc import Mapping
5
5
 
6
6
  from classiq.interface.chemistry.fermionic_operator import (
@@ -53,6 +53,7 @@ from classiq.applications.chemistry.chemistry_execution_parameters import (
53
53
  )
54
54
  from classiq.interface.exceptions import ClassiqError
55
55
  from classiq.qmod.utilities import qmod_val_to_expr_str
56
+ from classiq.qmod.builtins.functions.hea import full_hea
56
57
 
57
58
  _LADDER_OPERATOR_TYPE_INDICATOR_TO_QMOD_MAPPING: dict[str, str] = {
58
59
  "+": "PLUS",
@@ -489,12 +490,22 @@ def construct_chemistry_model(
489
490
  ansatz_parameters: AnsatzParameters,
490
491
  execution_parameters: ChemistryExecutionParameters,
491
492
  ) -> SerializedModel:
493
+ chemistry_functions = [
494
+ _get_chemistry_quantum_main(
495
+ chemistry_problem,
496
+ use_hartree_fock,
497
+ ansatz_parameters,
498
+ )
499
+ ]
500
+ if isinstance(ansatz_parameters, HEAParameters):
501
+ chemistry_functions.append(
502
+ cast(
503
+ NativeFunctionDefinition,
504
+ full_hea.create_model().function_dict["full_hea"],
505
+ )
506
+ )
492
507
  model = Model(
493
- functions=[
494
- _get_chemistry_quantum_main(
495
- chemistry_problem, use_hartree_fock, ansatz_parameters
496
- ),
497
- ],
508
+ functions=chemistry_functions,
498
509
  classical_execution_code=_get_chemistry_classical_code(
499
510
  chemistry_problem, execution_parameters
500
511
  ),
@@ -23,7 +23,10 @@ from classiq.applications.combinatorial_helpers import (
23
23
  )
24
24
  from classiq.applications.combinatorial_helpers.encoding_mapping import EncodingMapping
25
25
  from classiq.applications.combinatorial_helpers.memory import InternalQuantumReg
26
- from classiq.applications.combinatorial_helpers.pyomo_utils import get_field_name
26
+ from classiq.applications.combinatorial_helpers.pyomo_utils import (
27
+ get_field_name,
28
+ is_index_var,
29
+ )
27
30
  from classiq.applications.combinatorial_helpers.transformations import (
28
31
  encoding,
29
32
  ising_converter,
@@ -176,7 +179,11 @@ class OptimizationModel:
176
179
  for key, value in penalty_map.pyomo2sympy.items():
177
180
  objective_map.pyomo2sympy[key] = value
178
181
  sympy_mapping = {
179
- sympy_var: get_field_name(pyomo_var)
182
+ sympy_var: (
183
+ get_field_name(pyomo_var)
184
+ if not is_index_var(pyomo_var)
185
+ else (get_field_name(pyomo_var.parent_component()), pyomo_var.index())
186
+ )
180
187
  for pyomo_var, sympy_var in objective_map.pyomo2sympy.items()
181
188
  }
182
189
  self.objective_not_encoded_sympy = sympy_mapping, objective_expr
@@ -1,5 +1,6 @@
1
1
  import math
2
2
  import re
3
+ from collections import defaultdict
3
4
  from enum import Enum
4
5
  from typing import Any, Optional, TypeVar, Union
5
6
 
@@ -24,7 +25,7 @@ from classiq.interface.generator.expressions.expression import Expression
24
25
  from classiq.interface.generator.functions.classical_type import Integer
25
26
  from classiq.interface.generator.types.struct_declaration import StructDeclaration
26
27
 
27
- from classiq.qmod.qmod_variable import QBit, QNum, QStruct, QVar
28
+ from classiq.qmod.qmod_variable import QArray, QBit, QNum, QStruct, QVar
28
29
  from classiq.qmod.symbolic_expr import SymbolicExpr
29
30
 
30
31
  ListVars = list[_GeneralVarData]
@@ -262,14 +263,51 @@ def pyomo_to_qmod_qstruct(
262
263
  struct_name: str, vars: list[_GeneralVarData]
263
264
  ) -> type[QStruct]:
264
265
  qmod_struct = type(struct_name, (QStruct,), {})
265
- var_names = {get_field_name(var): var for var in vars}
266
- qmod_struct.__annotations__ = {
267
- var_name: _get_qmod_field_type(var_name, var_data)
268
- for var_name, var_data in var_names.items()
269
- }
266
+ qmod_struct.__annotations__ = _get_qstruct_fields(vars)
270
267
  return qmod_struct
271
268
 
272
269
 
270
+ def _get_qstruct_fields(vars: list[_GeneralVarData]) -> dict[str, type[QVar]]:
271
+ array_types = _get_array_types(vars)
272
+ array_type_sizes = _get_array_sizes(array_types)
273
+ fields: dict[str, type[QVar]] = {}
274
+ for var in vars:
275
+ _add_qmod_field(var, array_type_sizes, fields)
276
+ return fields
277
+
278
+
279
+ def _get_array_types(vars: list[_GeneralVarData]) -> dict[str, set[int]]:
280
+ array_types: dict[str, set[int]] = defaultdict(set)
281
+ for var in vars:
282
+ if is_index_var(var):
283
+ array_types[var.parent_component().name].add(var.index())
284
+ return array_types
285
+
286
+
287
+ def _get_array_sizes(array_types: dict[str, set[int]]) -> dict[str, int]:
288
+ return {
289
+ name: array_size
290
+ for name, indices in array_types.items()
291
+ if indices == set(range(array_size := len(indices)))
292
+ }
293
+
294
+
295
+ def _add_qmod_field(
296
+ var: _GeneralVarData,
297
+ array_type_sizes: dict[str, int],
298
+ fields: dict[str, type[QVar]],
299
+ ) -> None:
300
+ parent_name = var.parent_component().name
301
+ if parent_name not in array_type_sizes:
302
+ var_name = get_field_name(var)
303
+ fields[var_name] = _get_qmod_field_type(var_name, var)
304
+ elif var.index() == 0:
305
+ fields[parent_name] = QArray[ # type:ignore[misc]
306
+ _get_qmod_field_type(parent_name, var),
307
+ array_type_sizes[parent_name],
308
+ ]
309
+
310
+
273
311
  def _get_qmod_field_type(var_name: str, var_data: _GeneralVarData) -> type[QVar]:
274
312
  if var_data.domain not in SUPPORTED_TYPES:
275
313
  raise ClassiqValueError(
@@ -294,11 +332,17 @@ def _get_qmod_field_type(var_name: str, var_data: _GeneralVarData) -> type[QVar]
294
332
 
295
333
 
296
334
  def evaluate_objective(
297
- var_mapping: dict, sympy_expr: sympy.Expr, struct_obj: Any
335
+ var_mapping: dict[Any, Union[str, tuple[str, int]]],
336
+ sympy_expr: sympy.Expr,
337
+ struct_obj: Any,
298
338
  ) -> Any:
299
339
  sympy_assignment = {
300
- sympy_var: getattr(struct_obj, field_name)
301
- for sympy_var, field_name in var_mapping.items()
340
+ sympy_var: (
341
+ getattr(struct_obj, field_accessor)
342
+ if isinstance(field_accessor, str)
343
+ else getattr(struct_obj, field_accessor[0])[field_accessor[1]]
344
+ )
345
+ for sympy_var, field_accessor in var_mapping.items()
302
346
  }
303
347
 
304
348
  # classical objective evaluation
@@ -315,3 +359,10 @@ def evaluate_objective(
315
359
 
316
360
  def get_field_name(var: _GeneralVarData) -> str:
317
361
  return var.local_name.replace("[", "_").replace("]", "")
362
+
363
+
364
+ def is_index_var(var: _GeneralVarData) -> bool:
365
+ return (
366
+ isinstance(var.index(), int)
367
+ and "[" not in var.parent_component().name # nested arrays not supported
368
+ )
@@ -12,17 +12,23 @@ from .combinatorial_optimization_config import OptimizerConfig, QAOAConfig
12
12
  from .combinatorial_optimization_model_constructor import (
13
13
  construct_combinatorial_optimization_model,
14
14
  )
15
+ from .combinatorial_problem import (
16
+ CombinatorialProblem,
17
+ execute_qaoa,
18
+ )
15
19
 
16
20
  __all__ = [
21
+ "CombinatorialProblem",
17
22
  "EncodingType",
18
23
  "OptimizerConfig",
19
24
  "QAOAConfig",
20
25
  "QSolver",
26
+ "compute_qaoa_initial_point",
21
27
  "construct_combinatorial_optimization_model",
22
28
  "examples",
29
+ "execute_qaoa",
23
30
  "get_optimization_solution_from_pyo",
24
31
  "pyo_model_to_hamiltonian",
25
- "compute_qaoa_initial_point",
26
32
  ]
27
33
 
28
34
 
@@ -33,6 +33,7 @@ from classiq.applications.combinatorial_helpers.pauli_helpers.pauli_utils import
33
33
  _pauli_terms_to_qmod,
34
34
  )
35
35
  from classiq.applications.combinatorial_optimization import OptimizerConfig, QAOAConfig
36
+ from classiq.qmod.builtins.functions import qaoa_penalty
36
37
 
37
38
 
38
39
  def construct_combi_opt_py_model(
@@ -106,6 +107,7 @@ def construct_combi_opt_py_model(
106
107
  ),
107
108
  ],
108
109
  ),
110
+ *[f for f in qaoa_penalty.create_model().functions if f.name != "main"],
109
111
  ],
110
112
  classical_execution_code=f"""
111
113
  vqe_result = vqe(
@@ -0,0 +1,230 @@
1
+ import math
2
+ import re
3
+ from typing import Callable, Optional
4
+
5
+ import numpy as np
6
+ import pandas as pd
7
+ import pyomo.core as pyo
8
+ import scipy
9
+ from tqdm import tqdm
10
+
11
+ from classiq.interface.executor.execution_preferences import ExecutionPreferences
12
+ from classiq.interface.executor.result import ExecutionDetails
13
+ from classiq.interface.model.model import SerializedModel
14
+
15
+ from classiq import Constraints, Preferences
16
+ from classiq.applications.combinatorial_helpers.combinatorial_problem_utils import (
17
+ pyo_model_to_qmod_problem,
18
+ )
19
+ from classiq.execution import ExecutionSession
20
+ from classiq.qmod.builtins.functions import (
21
+ RX,
22
+ allocate,
23
+ apply_to_all,
24
+ hadamard_transform,
25
+ )
26
+ from classiq.qmod.builtins.operations import phase, repeat
27
+ from classiq.qmod.cparam import CReal
28
+ from classiq.qmod.create_model_function import create_model
29
+ from classiq.qmod.qfunc import qfunc
30
+ from classiq.qmod.qmod_parameter import CArray
31
+ from classiq.qmod.qmod_variable import Output, QVar
32
+ from classiq.synthesis import SerializedQuantumProgram, synthesize
33
+
34
+
35
+ class CombinatorialProblem:
36
+ def __init__(
37
+ self,
38
+ pyo_model: pyo.ConcreteModel,
39
+ num_layers: int,
40
+ penalty_factor: int = 1,
41
+ ):
42
+ self.problem_vars_, self.cost_func = pyo_model_to_qmod_problem(
43
+ pyo_model, penalty_factor
44
+ )
45
+ self.num_layers_ = num_layers
46
+ self.model_ = None
47
+ self.qprog_ = None
48
+ self.es_ = None
49
+ self.optimized_params_ = None
50
+ self.params_trace_: list[np.ndarray] = []
51
+ self.cost_trace_: list = []
52
+
53
+ @property
54
+ def cost_trace(self) -> list:
55
+ return self.cost_trace_
56
+
57
+ @property
58
+ def params_trace(self) -> list[np.ndarray]:
59
+ return self.params_trace_
60
+
61
+ @property
62
+ def optimized_params(self) -> list:
63
+ return self.optimized_params_ # type:ignore[return-value]
64
+
65
+ def get_model(
66
+ self,
67
+ constraints: Optional[Constraints] = None,
68
+ preferences: Optional[Preferences] = None,
69
+ ) -> SerializedModel:
70
+ @qfunc
71
+ def main(
72
+ params: CArray[CReal, self.num_layers_ * 2], # type:ignore[valid-type]
73
+ v: Output[self.problem_vars_], # type:ignore[name-defined]
74
+ ) -> None:
75
+ allocate(v.size, v)
76
+ hadamard_transform(v)
77
+ repeat(
78
+ self.num_layers_,
79
+ lambda i: [ # type:ignore[arg-type]
80
+ phase(
81
+ -self.cost_func(v), params[i]
82
+ ), # type:ignore[func-returns-value]
83
+ apply_to_all(lambda q: RX(params[self.num_layers_ + i], q), v),
84
+ ],
85
+ )
86
+
87
+ self.model_ = create_model(
88
+ main, constraints=constraints, preferences=preferences
89
+ ) # type:ignore[assignment]
90
+ return self.model_ # type:ignore[return-value]
91
+
92
+ def get_qprog(self) -> SerializedQuantumProgram:
93
+ if self.model_ is None:
94
+ self.get_model()
95
+ self.qprog_ = synthesize(self.model_) # type:ignore[assignment,arg-type]
96
+ return self.qprog_ # type:ignore[return-value]
97
+
98
+ def optimize(
99
+ self,
100
+ execution_preferences: Optional[ExecutionPreferences] = None,
101
+ maxiter: int = 20,
102
+ quantile: float = 1.0,
103
+ ) -> list[float]:
104
+ if self.qprog_ is None:
105
+ self.get_qprog()
106
+ self.es_ = ExecutionSession(
107
+ self.qprog_, execution_preferences # type:ignore[assignment,arg-type]
108
+ )
109
+ self.params_trace_ = []
110
+ self.cost_trace_ = []
111
+
112
+ def estimate_cost_wrapper(params: np.ndarray) -> float:
113
+ cost = self.es_.estimate_cost( # type:ignore[attr-defined]
114
+ lambda state: self.cost_func(state["v"]),
115
+ {"params": params.tolist()},
116
+ quantile=quantile,
117
+ )
118
+ self.cost_trace_.append(cost)
119
+ self.params_trace_.append(params)
120
+ return cost
121
+
122
+ initial_params = (
123
+ np.concatenate(
124
+ (
125
+ np.linspace(1 / self.num_layers_, 1, self.num_layers_),
126
+ np.linspace(1, 1 / self.num_layers_, self.num_layers_),
127
+ )
128
+ )
129
+ * math.pi
130
+ )
131
+
132
+ with tqdm(total=maxiter, desc="Optimization Progress", leave=True) as pbar:
133
+
134
+ def _minimze_callback(xk: np.ndarray) -> None:
135
+ pbar.update(1) # increment progress bar
136
+ self.optimized_params_ = xk.tolist() # save recent optimized value
137
+
138
+ self.optimized_params_ = scipy.optimize.minimize(
139
+ estimate_cost_wrapper,
140
+ callback=_minimze_callback,
141
+ x0=initial_params,
142
+ method="COBYLA",
143
+ options={"maxiter": maxiter},
144
+ ).x.tolist()
145
+
146
+ return self.optimized_params_ # type:ignore[return-value]
147
+
148
+ def sample_uniform(self) -> pd.DataFrame:
149
+ return self.sample([0] * self.num_layers_ * 2)
150
+
151
+ def sample(self, params: list) -> pd.DataFrame:
152
+ assert self.es_ is not None
153
+ res = self.es_.sample( # type:ignore[unreachable]
154
+ {"params": params}
155
+ )
156
+ parsed_result = [
157
+ {
158
+ "solution": {
159
+ key: value
160
+ for key, value in sampled.state["v"].items()
161
+ if not re.match(".*_slack_var_.*", key)
162
+ },
163
+ "probability": sampled.shots / res.num_shots,
164
+ "cost": self.cost_func(sampled.state["v"]),
165
+ }
166
+ for sampled in res.parsed_counts
167
+ ]
168
+ return pd.DataFrame.from_records(parsed_result)
169
+
170
+
171
+ def execute_qaoa(
172
+ problem_vars: type[QVar],
173
+ cost_func: Callable,
174
+ num_layers: int,
175
+ maxiter: int,
176
+ execution_preferences: Optional[ExecutionPreferences] = None,
177
+ ) -> tuple[SerializedModel, SerializedQuantumProgram, ExecutionDetails]:
178
+ """
179
+ Implements a simple QAOA algorithm, including the creation and synthesis of the QAOA
180
+ ansatz and the classical optimization loop.
181
+
182
+ Args:
183
+ problem_vars: the quantum type (scalar, array, or struct) of the problem variable(s)
184
+ cost_func: the arithmetic expression that evaluates the cost given an instance of the problem_vars type
185
+ num_layers: the number of layers of the QAOA ansatz
186
+ maxiter: the maximum number of iterations for the classical optimization loop
187
+ execution_preferences: the execution settings for running the QAOA ansatz
188
+
189
+ Returns:
190
+ a tuple containing the model of the QAOA ansatz, the corresponding synthesized quantum program,
191
+ and the result of the execution with the optimized parameters
192
+ """
193
+
194
+ @qfunc
195
+ def main(
196
+ params: CArray[CReal, num_layers * 2], # type:ignore[valid-type]
197
+ v: Output[problem_vars], # type:ignore[valid-type]
198
+ ) -> None:
199
+ allocate(v.size, v) # type:ignore[attr-defined]
200
+ hadamard_transform(v)
201
+ repeat(
202
+ num_layers,
203
+ lambda i: [ # type:ignore[arg-type]
204
+ phase(-cost_func(v), params[i]), # type:ignore[func-returns-value]
205
+ apply_to_all(lambda q: RX(params[num_layers + i], q), v),
206
+ ],
207
+ )
208
+
209
+ model = create_model(main)
210
+ qprog = synthesize(model)
211
+
212
+ with ExecutionSession(qprog, execution_preferences) as es:
213
+ initial_params = (
214
+ np.concatenate(
215
+ (np.linspace(0, 1, num_layers), np.linspace(1, 0, num_layers))
216
+ )
217
+ * math.pi
218
+ )
219
+ final_params = scipy.optimize.minimize(
220
+ lambda params: es.estimate_cost(
221
+ lambda state: cost_func(state["v"]),
222
+ {"params": params.tolist()},
223
+ ),
224
+ x0=initial_params,
225
+ method="COBYLA",
226
+ options={"maxiter": maxiter},
227
+ ).x.tolist()
228
+ result = es.sample({"params": final_params})
229
+
230
+ return model, qprog, result
@@ -15,7 +15,7 @@ from classiq.interface.model.native_function_definition import NativeFunctionDef
15
15
  from classiq.interface.model.port_declaration import PortDeclaration
16
16
  from classiq.interface.model.quantum_function_call import QuantumFunctionCall
17
17
  from classiq.interface.model.quantum_lambda_function import QuantumLambdaFunction
18
- from classiq.interface.model.quantum_type import QuantumBitvector
18
+ from classiq.interface.model.quantum_type import QuantumNumeric
19
19
  from classiq.interface.model.variable_declaration_statement import (
20
20
  VariableDeclarationStatement,
21
21
  )
@@ -68,8 +68,10 @@ def construct_finance_model(
68
68
  positional_arg_declarations=[
69
69
  PortDeclaration(
70
70
  name="phase_port",
71
- quantum_type=QuantumBitvector(
72
- length=Expression(expr=f"{phase_port_size}")
71
+ quantum_type=QuantumNumeric(
72
+ size=Expression(expr=f"{phase_port_size}"),
73
+ is_signed=Expression(expr="False"),
74
+ fraction_digits=Expression(expr=f"{phase_port_size}"),
73
75
  ),
74
76
  direction=PortDeclarationDirection.Output,
75
77
  ),
@@ -84,10 +86,8 @@ def construct_finance_model(
84
86
  ],
85
87
  ),
86
88
  QuantumFunctionCall(
87
- function="allocate_num",
89
+ function="allocate",
88
90
  positional_args=[
89
- Expression(expr=f"{phase_port_size}"),
90
- Expression(expr="False"),
91
91
  Expression(expr=f"{phase_port_size}"),
92
92
  HandleBinding(name="phase_port"),
93
93
  ],
@@ -19,6 +19,7 @@ from classiq.interface.model.variable_declaration_statement import (
19
19
  )
20
20
 
21
21
  from classiq import RegisterUserInput
22
+ from classiq.qmod.builtins.functions.grover import grover_search, phase_oracle
22
23
 
23
24
  _OUTPUT_VARIABLE_NAME = "result"
24
25
 
@@ -151,6 +152,8 @@ def construct_grover_model(
151
152
  ),
152
153
  ],
153
154
  ),
155
+ *[f for f in grover_search.create_model().functions if f.name != "main"],
156
+ *[f for f in phase_oracle.create_model().functions if f.name != "main"],
154
157
  ],
155
158
  )
156
159
  return grover_model.get_model()
@@ -1,7 +1,3 @@
1
- from typing import cast
2
-
3
- from classiq.interface.model.native_function_definition import NativeFunctionDefinition
4
-
5
1
  from classiq.qmod.builtins.functions import Z, amplitude_estimation
6
2
  from classiq.qmod.qfunc import qfunc
7
3
  from classiq.qmod.qmod_variable import QArray, QBit, QNum
@@ -22,9 +18,4 @@ def qmci(
22
18
  )
23
19
 
24
20
 
25
- QMCI_LIBRARY = [
26
- cast(
27
- NativeFunctionDefinition,
28
- qmci.create_model().function_dict["qmci"],
29
- ),
30
- ]
21
+ QMCI_LIBRARY = [func for func in qmci.create_model().functions if func.name != "main"]
@@ -8,7 +8,7 @@ except ImportError as exc:
8
8
  from ..qnn import datasets, types
9
9
  from ..qnn.qlayer import QLayer
10
10
 
11
- __all__ = ["datasets", "types", "QLayer"]
11
+ __all__ = ["QLayer", "datasets", "types"]
12
12
 
13
13
 
14
14
  def __dir__() -> list[str]:
@@ -13,19 +13,19 @@ from ..datasets.dataset_xor import DatasetXor
13
13
  from ..datasets.datasets_utils import state_to_label, state_to_weights
14
14
 
15
15
  __all__ = [
16
- "builtin_datasets",
17
- "DatasetNot",
18
- "DATASET_NOT",
19
16
  "DATALOADER_NOT",
20
- "DatasetXor",
21
- "DATASET_XOR",
17
+ "DATALOADER_SUBSET_PARITY",
22
18
  "DATALOADER_XOR",
23
- "DatasetSubsetParity",
19
+ "DATASET_NOT",
24
20
  "DATASET_SUBSET_PARITY",
25
- "DATALOADER_SUBSET_PARITY",
21
+ "DATASET_XOR",
22
+ "DatasetNot",
26
23
  "DatasetParity",
27
- "state_to_weights",
24
+ "DatasetSubsetParity",
25
+ "DatasetXor",
26
+ "builtin_datasets",
28
27
  "state_to_label",
28
+ "state_to_weights",
29
29
  ]
30
30
 
31
31
 
@@ -1,9 +1,9 @@
1
1
  from classiq.interface.applications.qsvm import Data, Labels, QSVMData
2
2
 
3
3
  __all__ = [
4
- "QSVMData",
5
4
  "Data",
6
5
  "Labels",
6
+ "QSVMData",
7
7
  ]
8
8
 
9
9
 
@@ -10,7 +10,6 @@ from ..interface.executor.vqe_result import VQESolverResult
10
10
  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
- from .qaoa import execute_qaoa
14
13
  from .qnn import execute_qnn
15
14
 
16
15
  __all__ = (
@@ -25,7 +24,6 @@ __all__ = (
25
24
  "get_execution_jobs",
26
25
  "get_execution_jobs_async",
27
26
  "ExecutionSession",
28
- "execute_qaoa",
29
27
  "execute_qnn",
30
28
  "generate_iqcc_token",
31
29
  "generate_iqcc_token_async",
@@ -1,4 +1,5 @@
1
1
  import json
2
+ import random
2
3
  from functools import cached_property
3
4
  from types import TracebackType
4
5
  from typing import Any, Callable, Optional, Union, cast
@@ -168,6 +169,9 @@ class ExecutionSession:
168
169
  # because cmain is expected in some cases
169
170
  self.program.model.classical_execution_code = "dummy"
170
171
 
172
+ self._random_seed = self.program.model.execution_preferences.random_seed
173
+ self._rng = random.Random(self._random_seed) # noqa: S311
174
+
171
175
  self._async_client = client().async_client()
172
176
 
173
177
  def __enter__(self) -> "ExecutionSession":
@@ -197,6 +201,8 @@ class ExecutionSession:
197
201
  self, classical_execution_code: str, primitives_input: PrimitivesInput
198
202
  ) -> ExecutionJob:
199
203
  execution_input = self._execution_input.copy()
204
+ execution_input["execution_preferences"]["random_seed"] = self._random_seed
205
+ self._random_seed = self._rng.randint(0, 2**32 - 1)
200
206
  execution_input["classical_execution_code"] = classical_execution_code
201
207
  # The use of `model_dump_json` is necessary for complex numbers serialization
202
208
  execution_input["primitives_input"] = json.loads(