classiq 0.82.1__py3-none-any.whl → 0.84.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 (96) hide show
  1. classiq/_internals/api_wrapper.py +27 -0
  2. classiq/applications/chemistry/chemistry_model_constructor.py +2 -4
  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_optimization_model_constructor.py +2 -2
  11. classiq/{model_expansions/evaluators → evaluators}/arg_type_match.py +12 -4
  12. classiq/{model_expansions/evaluators → evaluators}/argument_types.py +3 -3
  13. classiq/{model_expansions/evaluators → evaluators}/classical_expression.py +1 -1
  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 +189 -43
  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/execution/primitives.py +29 -1
  24. classiq/interface/executor/estimate_cost.py +35 -0
  25. classiq/interface/executor/execution_result.py +13 -0
  26. classiq/interface/executor/result.py +116 -1
  27. classiq/interface/executor/user_budget.py +26 -33
  28. classiq/interface/generator/application_apis/finance_declarations.py +3 -3
  29. classiq/interface/generator/expressions/atomic_expression_functions.py +11 -3
  30. classiq/interface/generator/expressions/proxies/classical/any_classical_value.py +0 -6
  31. classiq/interface/generator/functions/classical_type.py +2 -35
  32. classiq/interface/generator/functions/concrete_types.py +0 -3
  33. classiq/interface/generator/functions/type_modifier.py +22 -0
  34. classiq/interface/generator/generated_circuit_data.py +5 -16
  35. classiq/interface/generator/model/model.py +8 -0
  36. classiq/interface/generator/quantum_program.py +0 -13
  37. classiq/interface/generator/types/compilation_metadata.py +0 -3
  38. classiq/interface/helpers/model_normalizer.py +2 -2
  39. classiq/interface/ide/visual_model.py +6 -2
  40. classiq/interface/model/model.py +12 -7
  41. classiq/interface/model/port_declaration.py +4 -2
  42. classiq/interface/pretty_print/__init__.py +0 -0
  43. classiq/{qmod/native → interface/pretty_print}/expression_to_qmod.py +18 -11
  44. classiq/interface/server/routes.py +4 -0
  45. classiq/model_expansions/atomic_expression_functions_defs.py +42 -5
  46. classiq/model_expansions/capturing/captured_vars.py +21 -8
  47. classiq/model_expansions/interpreters/base_interpreter.py +3 -3
  48. classiq/model_expansions/quantum_operations/allocate.py +1 -1
  49. classiq/model_expansions/quantum_operations/assignment_result_processor.py +1 -1
  50. classiq/model_expansions/quantum_operations/bind.py +2 -2
  51. classiq/model_expansions/quantum_operations/call_emitter.py +42 -36
  52. classiq/model_expansions/quantum_operations/variable_decleration.py +1 -1
  53. classiq/model_expansions/scope_initialization.py +3 -3
  54. classiq/model_expansions/transformers/model_renamer.py +16 -5
  55. classiq/model_expansions/transformers/{type_qualifier_inference.py → type_modifier_inference.py} +134 -100
  56. classiq/model_expansions/visitors/symbolic_param_inference.py +10 -7
  57. classiq/open_library/functions/__init__.py +3 -0
  58. classiq/open_library/functions/amplitude_amplification.py +10 -18
  59. classiq/open_library/functions/discrete_sine_cosine_transform.py +5 -5
  60. classiq/open_library/functions/grover.py +14 -6
  61. classiq/open_library/functions/modular_exponentiation.py +22 -20
  62. classiq/open_library/functions/state_preparation.py +18 -1
  63. classiq/qmod/__init__.py +2 -2
  64. classiq/qmod/builtins/enums.py +23 -0
  65. classiq/qmod/builtins/functions/__init__.py +2 -0
  66. classiq/qmod/builtins/functions/allocation.py +2 -2
  67. classiq/qmod/builtins/functions/arithmetic.py +16 -8
  68. classiq/qmod/builtins/functions/exponentiation.py +32 -4
  69. classiq/qmod/builtins/functions/standard_gates.py +7 -7
  70. classiq/qmod/builtins/structs.py +55 -3
  71. classiq/qmod/declaration_inferrer.py +8 -7
  72. classiq/qmod/native/pretty_printer.py +7 -11
  73. classiq/qmod/pretty_print/expression_to_python.py +2 -1
  74. classiq/qmod/pretty_print/pretty_printer.py +7 -12
  75. classiq/qmod/python_classical_type.py +12 -5
  76. classiq/qmod/qfunc.py +1 -1
  77. classiq/qmod/qmod_constant.py +2 -5
  78. classiq/qmod/qmod_parameter.py +2 -5
  79. classiq/qmod/qmod_variable.py +66 -25
  80. classiq/qmod/quantum_expandable.py +4 -2
  81. classiq/qmod/quantum_function.py +7 -2
  82. classiq/qmod/semantics/annotation/qstruct_annotator.py +1 -1
  83. classiq/qmod/semantics/validation/main_validation.py +1 -9
  84. classiq/qmod/semantics/validation/type_hints.py +9 -9
  85. classiq/qmod/utilities.py +0 -2
  86. classiq/qmod/write_qmod.py +1 -1
  87. classiq/synthesis.py +0 -2
  88. {classiq-0.82.1.dist-info → classiq-0.84.0.dist-info}/METADATA +4 -1
  89. {classiq-0.82.1.dist-info → classiq-0.84.0.dist-info}/RECORD +95 -86
  90. classiq/interface/generator/functions/type_qualifier.py +0 -22
  91. /classiq/{model_expansions/evaluators → evaluators}/__init__.py +0 -0
  92. /classiq/{model_expansions/evaluators → evaluators}/control.py +0 -0
  93. /classiq/{model_expansions → evaluators}/expression_evaluator.py +0 -0
  94. /classiq/{model_expansions/evaluators → evaluators}/quantum_type_utils.py +0 -0
  95. /classiq/{model_expansions/evaluators → evaluators}/type_type_match.py +0 -0
  96. {classiq-0.82.1.dist-info → classiq-0.84.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,368 @@
1
+ from collections.abc import Sequence
2
+ from functools import cached_property
3
+ from typing import TYPE_CHECKING, Any, Optional
4
+
5
+ import numpy as np
6
+ from openfermion.ops.operators.fermion_operator import FermionOperator
7
+ from openfermion.ops.operators.qubit_operator import QubitOperator
8
+ from openfermion.transforms import taper_off_qubits
9
+ from openfermion.utils.commutators import anticommutator, commutator
10
+
11
+ from classiq.interface.exceptions import ClassiqValueError
12
+
13
+ from classiq.applications.chemistry.mapping import FermionToQubitMapper, MappingMethod
14
+ from classiq.applications.chemistry.op_utils import (
15
+ qubit_op_to_xz_matrix,
16
+ xz_matrix_to_qubit_op,
17
+ )
18
+ from classiq.applications.chemistry.problems import FermionHamiltonianProblem
19
+
20
+
21
+ class Z2SymTaperMapper(FermionToQubitMapper):
22
+ """
23
+ Mapper between fermionic operators to qubits operators, using one of the supported
24
+ mapping methods (see `MappingMethod`), and taking advantage of Z2 symmetries in
25
+ order to taper off qubits.
26
+
27
+ Attributes:
28
+ method (MappingMethod): The mapping method.
29
+ generators (tuple[QubitOperator, ...]): Generators representing the Z2
30
+ symmetries.
31
+ x_ops (tuple[QubitOperator, ...]): Single-qubit X operations, such that each
32
+ operation anti-commutes with its matching generator and commutes with all
33
+ other generators.
34
+ """
35
+
36
+ def __init__(
37
+ self,
38
+ generators: Sequence[QubitOperator],
39
+ x_ops: Sequence[QubitOperator],
40
+ method: MappingMethod = MappingMethod.JORDAN_WIGNER,
41
+ sector: Optional[Sequence[int]] = None,
42
+ tol: float = 1e-14,
43
+ ) -> None:
44
+ """
45
+ Initializes a `Z2SymTaperMapper` object from the given configuration.
46
+
47
+ Args:
48
+ generators (Sequence[QubitOperator]): Generators representing the Z2
49
+ symmetries.
50
+ x_ops (Sequence[QubitOperator]): Single-qubit X operations, such that each
51
+ operation anti-commutes with its matching generator and commutes with all
52
+ other generators.
53
+ method (MappingMethod): The mapping method.
54
+ sector: (Sequence[int]): Symmetry sector coefficients, each is 1 or -1.
55
+ If not specified, all coefficients defaults to 1.
56
+ tol (float): Tolerance for trimming off terms.
57
+ """
58
+ super().__init__(method=method)
59
+
60
+ self._validate_symmetries(generators, x_ops)
61
+
62
+ self._generators = generators
63
+ self._x_ops = x_ops
64
+ self._tol = tol
65
+
66
+ self.set_sector(sector or [1] * len(self._generators))
67
+
68
+ @staticmethod
69
+ def _validate_symmetries(
70
+ generators: Sequence[QubitOperator],
71
+ x_ops: Sequence[QubitOperator],
72
+ ) -> None:
73
+ if len(generators) != len(x_ops):
74
+ raise ClassiqValueError(
75
+ "Generators and X operations must have the same length."
76
+ )
77
+
78
+ for i, x_op in enumerate(x_ops):
79
+ for j, gen in enumerate(generators):
80
+ if i == j:
81
+ if anticommutator(x_op, gen) != QubitOperator():
82
+ raise ClassiqValueError(
83
+ f"x_{i}={x_op} and generator_{j}={gen} should anti-commute but don't."
84
+ )
85
+ else:
86
+ if commutator(x_op, gen) != QubitOperator():
87
+ raise ClassiqValueError(
88
+ f"x_{i}={x_op} and generator_{j}={gen} should commute but don't."
89
+ )
90
+
91
+ def set_sector(self, sector: Sequence[int]) -> None:
92
+ """
93
+ Sets the symmetry sector coefficients.
94
+
95
+ Args:
96
+ sector: (Sequence[int]): Symmetry sector coefficients, each is 1 or -1.
97
+ """
98
+ if len(sector) != len(self._generators):
99
+ raise ClassiqValueError(
100
+ "Sector must have the same length as the generators."
101
+ )
102
+ self._sector = sector
103
+
104
+ @property
105
+ def generators(self) -> tuple[QubitOperator, ...]:
106
+ """
107
+ Generators representing the Z2 symmetries.
108
+ """
109
+ return tuple(self._generators)
110
+
111
+ @property
112
+ def x_ops(self) -> tuple[QubitOperator, ...]:
113
+ """
114
+ Single-qubit X operations, such that each operation anti-commutes with its
115
+ matching generator and commutes with all other generators.
116
+ """
117
+ return tuple(self._x_ops)
118
+
119
+ def map(
120
+ self,
121
+ fermion_op: FermionOperator,
122
+ *args: Any,
123
+ is_invariant: bool = False,
124
+ **kwargs: Any,
125
+ ) -> QubitOperator:
126
+ """
127
+ Maps the given fermionic operator to qubits operator by using the
128
+ mapper's method, and subsequently by tapering off qubits according to Z2
129
+ symmetries.
130
+
131
+ Args:
132
+ fermion_op (FermionOperator): A fermionic operator.
133
+ is_invariant (bool): If `False`, the operator is not necessarily in the
134
+ symmetry subspace, and thus gets projected onto it before tapering.
135
+
136
+ Returns:
137
+ The mapped qubits operator.
138
+ """
139
+ qubit_op = super().map(fermion_op)
140
+ sectored_x_ops = [s * x_op for s, x_op in zip(self._sector, self._x_ops)]
141
+
142
+ if not is_invariant:
143
+ qubit_op = _project_operator_to_subspace(qubit_op, self._generators)
144
+
145
+ block_diagonal_op = self._block_diagnolize(qubit_op)
146
+ tapered_op = taper_off_qubits(block_diagonal_op, sectored_x_ops)
147
+ if TYPE_CHECKING:
148
+ assert isinstance(tapered_op, QubitOperator)
149
+ tapered_op.compress(self._tol)
150
+ return tapered_op
151
+
152
+ def get_num_qubits(self, problem: FermionHamiltonianProblem) -> int:
153
+ """
154
+ Gets the number of qubits after mapping the given problem into qubits space.
155
+
156
+ Args:
157
+ problem (FermionHamiltonianProblem): The fermion problem.
158
+
159
+ Returns:
160
+ The number of qubits.
161
+ """
162
+ return super().get_num_qubits(problem) - len(self._generators)
163
+
164
+ @classmethod
165
+ def from_problem(
166
+ cls,
167
+ problem: FermionHamiltonianProblem,
168
+ method: MappingMethod = MappingMethod.JORDAN_WIGNER,
169
+ sector_from_hartree_fock: bool = True,
170
+ tol: float = 1e-14,
171
+ ) -> "Z2SymTaperMapper":
172
+ """
173
+ Initializes a `Z2SymTaperMapper` object from a fermion problem (i.e. computing
174
+ the Z2 symmetries from the problem definition).
175
+
176
+ Args:
177
+ problem (FermionHamiltonianProblem): The fermion problem.
178
+ method (MappingMethod): The mapping method.
179
+ sector_from_hartree_fock (bool): Whether to compute the symmetry sector
180
+ coefficients according to the Hartree-Fock state.
181
+ tol (float): Tolerance for trimming off terms.
182
+
183
+ Returns:
184
+ The Z2 symmetries taper mapper.
185
+ """
186
+ mapper = FermionToQubitMapper(method)
187
+ n_qubits = mapper.get_num_qubits(problem)
188
+
189
+ qubit_op = mapper.map(problem.fermion_hamiltonian)
190
+ qubit_op.compress(tol)
191
+
192
+ generators = _get_z2_symmetries_generators(qubit_op, n_qubits)
193
+ x_ops = _get_x_ops_for_generators(generators, n_qubits)
194
+
195
+ sector: Optional[list[int]] = None
196
+ if sector_from_hartree_fock:
197
+ from classiq.applications.chemistry.hartree_fock import get_hf_state
198
+
199
+ if not (generators[:, :n_qubits] == 0).all():
200
+ raise ClassiqValueError(
201
+ "The Hartree-Fock state is not in the symmetry space spanned by the generators, please set `sector_from_hartree_fock=False`. You can later set the sector manually with `set_sector`."
202
+ )
203
+
204
+ state = get_hf_state(problem, mapper)
205
+ sector = _get_sector_for_basis_state(generators[:, n_qubits:], state)
206
+
207
+ return cls(
208
+ generators=[xz_matrix_to_qubit_op(gen) for gen in generators],
209
+ x_ops=x_ops,
210
+ sector=sector,
211
+ tol=tol,
212
+ method=method,
213
+ )
214
+
215
+ @cached_property
216
+ def _block_diagonalizing_clifford(self) -> QubitOperator:
217
+ op = QubitOperator(())
218
+ for gen, x_op in zip(self._generators, self._x_ops):
219
+ op *= (2 ** (-0.5)) * (x_op + gen)
220
+ return op
221
+
222
+ def _block_diagnolize(self, op: QubitOperator) -> QubitOperator:
223
+ transformed_op = (
224
+ self._block_diagonalizing_clifford * op * self._block_diagonalizing_clifford
225
+ )
226
+ transformed_op.compress(self._tol)
227
+ return transformed_op
228
+
229
+
230
+ def _get_z2_symmetries_generators(op: QubitOperator, n_qubits: int) -> np.ndarray:
231
+ """
232
+ Gets the Z2 symmetries generators of an operator.
233
+
234
+ It can be shown that each vector in the kernel subspace of the operator's XZ matrix,
235
+ after replacing Xs ans Zs, commutes with the operator.
236
+ """
237
+ xz_mat = qubit_op_to_xz_matrix(op, n_qubits)
238
+ kernel = _get_kernel(xz_mat)
239
+ return np.hstack((kernel[:, n_qubits:], kernel[:, :n_qubits]))
240
+
241
+
242
+ def _get_x_ops_for_generators(
243
+ generators: np.ndarray, n_qubits: int
244
+ ) -> list[QubitOperator]:
245
+ """
246
+ Tries to find single-qubit X operations for the given generators, such that each X
247
+ operation anti-commutes with its matching generator and commutes with all the rest.
248
+ """
249
+ x_ops: list[QubitOperator] = []
250
+ for row in range(len(generators)):
251
+
252
+ # we look for a column in the Z-part of the matrix which is populated only with
253
+ # 0s except for a 1 in the current generator: a X operation in this column's
254
+ # qubit will anti-commute with the current generator and commute with all others
255
+ found_col: Optional[int] = None
256
+ for col in range(n_qubits):
257
+ if (
258
+ generators[row, n_qubits + col] == 1
259
+ and np.all(generators[:row, n_qubits + col] == 0)
260
+ and np.all(generators[row + 1 :, n_qubits + col] == 0)
261
+ ):
262
+ found_col = col
263
+ break
264
+ else:
265
+ raise ClassiqValueError(
266
+ "Failed to find X operator for the Z2 symmetry generator."
267
+ )
268
+
269
+ x_ops.append(QubitOperator(((found_col, "X"),)))
270
+
271
+ return x_ops
272
+
273
+
274
+ def _get_sector_for_basis_state(
275
+ generators_z_part: np.ndarray, state: list[bool]
276
+ ) -> list[int]:
277
+ """
278
+ Computes the sector coefficients of a basis state by applying the generators.
279
+ """
280
+ sector: list[int] = []
281
+ for gen in generators_z_part:
282
+ coeff = 1
283
+ for qubit in range(len(state)):
284
+ if state[qubit] and gen[qubit] == 1:
285
+ coeff *= -1
286
+ sector.append(coeff)
287
+ return sector
288
+
289
+
290
+ def _get_kernel(mat: np.ndarray) -> np.ndarray:
291
+ """
292
+ Computes the kernel subspace of the given Z2 matrix.
293
+
294
+ Note: this function changes the given matrix inplace.
295
+ """
296
+ _transform_to_rref(mat)
297
+ return _get_kernel_from_rref(mat)
298
+
299
+
300
+ def _transform_to_rref(mat: np.ndarray) -> None:
301
+ """
302
+ Transforms the given Z2 matrix into RREF (Reduced Row Echelon Form).
303
+
304
+ Note: this function changes the given matrix inplace.
305
+ """
306
+ n_rows, n_cols = mat.shape
307
+ col = 0
308
+
309
+ for row in range(n_rows):
310
+ while col < n_cols and mat[row, col] == 0:
311
+ # find 1 in the current column and swap rows or move to the next column
312
+ for krow in range(row + 1, n_rows):
313
+ if mat[krow, col] == 1:
314
+ mat[[row, krow], col:] = mat[[krow, row], col:]
315
+ break
316
+ else:
317
+ col += 1
318
+
319
+ if col < n_cols:
320
+ # eliminate 1s in current column by XORing their rows with the current row
321
+ curr_row = mat[row, col:]
322
+ mat[:row, col:] ^= np.outer(mat[:row, col], curr_row)
323
+ mat[row + 1 :, col:] ^= np.outer(mat[row + 1 :, col], curr_row)
324
+ col += 1
325
+
326
+
327
+ def _get_kernel_from_rref(mat: np.ndarray) -> np.ndarray:
328
+ """
329
+ Computes the kernel subspace of the given Z2 matrix which is in RREF.
330
+ """
331
+ # remove all-zero rows
332
+ mat = mat[~np.all(mat == 0, axis=1)]
333
+
334
+ n_cols = mat.shape[1]
335
+
336
+ # pivots are indices of columns with leading 1, free columns are the rest
337
+ pivots = np.argmax(mat, axis=1)
338
+ free_cols = np.setdiff1d(np.arange(n_cols), pivots)
339
+
340
+ # for each free column we have a vector in the kernel with 1 in the free column
341
+ # index and possibly 1s in pivots indices
342
+ kernel = np.zeros((free_cols.size, n_cols), dtype=np.int8)
343
+ for vec, free_col in zip(kernel, free_cols):
344
+ vec[free_col] = 1
345
+
346
+ for row, pivot in zip(mat, pivots):
347
+ if row[free_col] == 1:
348
+ vec[pivot] = 1
349
+
350
+ return kernel
351
+
352
+
353
+ def _project_operator_to_subspace(
354
+ op: QubitOperator, generators: Sequence[QubitOperator]
355
+ ) -> QubitOperator:
356
+ """
357
+ Projects the given operator onto the symmetry subspace defined by the given
358
+ generators.
359
+ """
360
+ projected_op = QubitOperator()
361
+ for term, coeff in op.terms.items():
362
+ single_term_op = QubitOperator(term, coeff)
363
+ if all(
364
+ commutator(single_term_op, gen) == QubitOperator() for gen in generators
365
+ ):
366
+ projected_op += single_term_op
367
+
368
+ return projected_op
@@ -3,7 +3,12 @@ from classiq.interface.generator.functions.qmod_python_interface import QmodPySt
3
3
  from classiq.interface.helpers.custom_pydantic_types import PydanticPauliList
4
4
 
5
5
  from classiq.qmod.builtins.enums import Pauli
6
- from classiq.qmod.builtins.structs import PauliTerm
6
+ from classiq.qmod.builtins.structs import (
7
+ IndexedPauli,
8
+ PauliTerm,
9
+ SparsePauliOp,
10
+ SparsePauliTerm,
11
+ )
7
12
 
8
13
 
9
14
  def pauli_operator_to_hamiltonian(pauli_list: PydanticPauliList) -> list[PauliTerm]:
@@ -22,6 +27,30 @@ def pauli_operator_to_hamiltonian(pauli_list: PydanticPauliList) -> list[PauliTe
22
27
  return pauli_terms
23
28
 
24
29
 
30
+ def pauli_operator_to_sparse_hamiltonian(
31
+ pauli_list: PydanticPauliList,
32
+ ) -> SparsePauliOp:
33
+ pauli_terms: list[SparsePauliTerm] = []
34
+ for pauli_term in pauli_list:
35
+ if not isinstance(pauli_term[1], complex) or pauli_term[1].imag != 0:
36
+ raise ClassiqNonNumericCoefficientInPauliError(
37
+ "Coefficient is not a number."
38
+ )
39
+ term = SparsePauliTerm(
40
+ paulis=[ # type:ignore[arg-type]
41
+ IndexedPauli(pauli=Pauli[p], index=i) # type:ignore[arg-type]
42
+ for i, p in enumerate(pauli_term[0][::-1])
43
+ ],
44
+ coefficient=pauli_term[1].real, # type: ignore[arg-type]
45
+ )
46
+ pauli_terms.append(term)
47
+
48
+ return SparsePauliOp(
49
+ terms=pauli_terms, # type:ignore[arg-type]
50
+ num_qubits=len(pauli_list[0][0]), # type:ignore[arg-type]
51
+ )
52
+
53
+
25
54
  def pauli_enum_to_str(pauli: Pauli) -> str:
26
55
  return {
27
56
  Pauli.I: "Pauli.I",
@@ -12,8 +12,8 @@ from classiq.interface.generator.functions.classical_type import (
12
12
  from classiq.interface.generator.functions.port_declaration import (
13
13
  PortDeclarationDirection,
14
14
  )
15
+ from classiq.interface.generator.functions.type_modifier import TypeModifier
15
16
  from classiq.interface.generator.functions.type_name import Struct
16
- from classiq.interface.generator.functions.type_qualifier import TypeQualifier
17
17
  from classiq.interface.model.allocate import Allocate
18
18
  from classiq.interface.model.classical_parameter_declaration import (
19
19
  ClassicalParameterDeclaration,
@@ -96,7 +96,7 @@ def construct_combi_opt_py_model(
96
96
  length=Expression(expr=f"{len_hamiltonian}"),
97
97
  ),
98
98
  direction=PortDeclarationDirection.Output,
99
- type_qualifier=TypeQualifier.Quantum,
99
+ type_modifier=TypeModifier.Mutable,
100
100
  ),
101
101
  ],
102
102
  body=[
@@ -25,8 +25,8 @@ from classiq.interface.model.quantum_function_declaration import (
25
25
  AnonQuantumOperandDeclaration,
26
26
  )
27
27
 
28
+ from classiq.evaluators.type_type_match import check_signature_match
28
29
  from classiq.model_expansions.closure import FunctionClosure
29
- from classiq.model_expansions.evaluators.type_type_match import check_signature_match
30
30
  from classiq.model_expansions.scope import Evaluated, QuantumVariable
31
31
  from classiq.qmod.model_state_container import QMODULE
32
32
  from classiq.qmod.qmod_parameter import CInt, get_qmod_type
@@ -69,7 +69,7 @@ def check_arg_type_match(
69
69
  error_message = (
70
70
  message_prefix + f"expected {_resolve_type_name(parameter.classical_type)}"
71
71
  )
72
- _check_classical_type_match(argument, parameter, error_message)
72
+ _check_classical_type_match(argument, parameter, error_message, function_name)
73
73
  else:
74
74
  raise ClassiqExpansionError(
75
75
  f"unexpected parameter declaration type: {type(parameter).__name__}"
@@ -118,7 +118,10 @@ def _check_operand_type_match(
118
118
 
119
119
 
120
120
  def _check_classical_type_match(
121
- argument: Any, parameter: AnonClassicalParameterDeclaration, error_message: str
121
+ argument: Any,
122
+ parameter: AnonClassicalParameterDeclaration,
123
+ error_message: str,
124
+ function_name: str,
122
125
  ) -> None:
123
126
  classical_type = parameter.classical_type
124
127
  type_name = _resolve_type_name(classical_type)
@@ -132,12 +135,17 @@ def _check_classical_type_match(
132
135
  )
133
136
  arg_is_qvar = isinstance(argument, QmodSizedProxy)
134
137
  arg_is_builtin = argument.__class__.__module__ == "builtins"
138
+ arg_is_int = isinstance(argument, int)
135
139
  arg_is_enum = isinstance(argument, Enum)
136
140
  arg_is_struct = isinstance(argument, QmodStructInstance)
137
141
  arg_struct_name = None if not arg_is_struct else argument.struct_declaration.name
142
+ # FIXME: Remove suzuki_trotter overloading (CLS-2912)
143
+ if function_name == "suzuki_trotter" and parameter.name == "pauli_operator":
144
+ return
138
145
  if (
139
146
  arg_is_qvar
140
- or (arg_is_builtin and (type_is_struct or type_is_enum))
147
+ or (arg_is_builtin and type_is_struct)
148
+ or (arg_is_builtin and not arg_is_int and type_is_enum)
141
149
  or (arg_is_struct and (not type_is_struct or arg_struct_name != type_name))
142
150
  or (
143
151
  arg_is_enum
@@ -3,12 +3,12 @@ from collections.abc import Sequence
3
3
  from classiq.interface.generator.functions.port_declaration import (
4
4
  PortDeclarationDirection,
5
5
  )
6
- from classiq.interface.generator.functions.type_qualifier import TypeQualifier
6
+ from classiq.interface.generator.functions.type_modifier import TypeModifier
7
7
  from classiq.interface.model.port_declaration import AnonPortDeclaration
8
8
  from classiq.interface.model.quantum_function_declaration import AnonPositionalArg
9
9
  from classiq.interface.model.quantum_type import QuantumNumeric
10
10
 
11
- from classiq.model_expansions.evaluators.quantum_type_utils import copy_type_information
11
+ from classiq.evaluators.quantum_type_utils import copy_type_information
12
12
  from classiq.model_expansions.scope import Evaluated, QuantumVariable
13
13
 
14
14
 
@@ -56,7 +56,7 @@ def handle_args_numeric_bounds(
56
56
 
57
57
  if (
58
58
  parameter.direction != PortDeclarationDirection.Output
59
- and parameter.type_qualifier != TypeQualifier.Const
59
+ and parameter.type_modifier != TypeModifier.Const
60
60
  and isinstance(argument_as_quantum_symbol.quantum_type, QuantumNumeric)
61
61
  ):
62
62
  argument_as_quantum_symbol.quantum_type.reset_bounds()
@@ -10,7 +10,7 @@ from classiq.interface.generator.expressions.proxies.classical.any_classical_val
10
10
  )
11
11
  from classiq.interface.model.handle_binding import HandleBinding
12
12
 
13
- from classiq.model_expansions.expression_evaluator import evaluate
13
+ from classiq.evaluators.expression_evaluator import evaluate
14
14
  from classiq.model_expansions.scope import Evaluated, QuantumSymbol, Scope
15
15
 
16
16
 
@@ -17,7 +17,6 @@ from classiq.interface.generator.expressions.proxies.classical.qmod_struct_insta
17
17
  )
18
18
  from classiq.interface.generator.functions.classical_type import (
19
19
  ClassicalArray,
20
- ClassicalList,
21
20
  ClassicalTuple,
22
21
  ClassicalType,
23
22
  )
@@ -29,7 +28,7 @@ from classiq.interface.helpers.backward_compatibility import zip_strict
29
28
  def infer_classical_type(val: Any, classical_type: ClassicalType) -> ClassicalType:
30
29
  if isinstance(classical_type, TypeName):
31
30
  return _infer_classical_struct_type(val, classical_type)
32
- if isinstance(classical_type, (ClassicalArray, ClassicalList, ClassicalTuple)):
31
+ if isinstance(classical_type, (ClassicalArray, ClassicalTuple)):
33
32
  return _infer_classical_array_type(val, classical_type)
34
33
  return classical_type
35
34
 
@@ -58,7 +57,7 @@ def _infer_classical_struct_type(val: Any, classical_type: TypeName) -> Classica
58
57
 
59
58
 
60
59
  def _infer_classical_array_type(
61
- val: Any, classical_type: Union[ClassicalArray, ClassicalList, ClassicalTuple]
60
+ val: Any, classical_type: Union[ClassicalArray, ClassicalTuple]
62
61
  ) -> ClassicalType:
63
62
  if isinstance(val, ClassicalSequenceProxy):
64
63
  val_length = val.length
@@ -102,7 +101,7 @@ def _infer_inner_array_types(
102
101
  ),
103
102
  )
104
103
  if TYPE_CHECKING:
105
- assert isinstance(classical_type, (ClassicalList, ClassicalArray))
104
+ assert isinstance(classical_type, ClassicalArray)
106
105
  if _is_int(val_length) and val_length != 0:
107
106
  return ClassicalTuple(
108
107
  element_types=(
@@ -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