classiq 0.62.0__py3-none-any.whl → 0.63.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 (75) hide show
  1. classiq/__init__.py +3 -0
  2. classiq/_internals/api_wrapper.py +6 -26
  3. classiq/_internals/client.py +1 -9
  4. classiq/applications/chemistry/chemistry_model_constructor.py +1 -1
  5. classiq/applications/combinatorial_helpers/combinatorial_problem_utils.py +26 -8
  6. classiq/applications/combinatorial_helpers/optimization_model.py +5 -1
  7. classiq/applications/combinatorial_helpers/pyomo_utils.py +106 -27
  8. classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +1 -1
  9. classiq/applications/combinatorial_optimization/combinatorial_problem.py +4 -2
  10. classiq/applications/grover/grover_model_constructor.py +1 -1
  11. classiq/applications/libraries/qmci_library.py +2 -1
  12. classiq/execution/execution_session.py +66 -96
  13. classiq/execution/jobs.py +3 -9
  14. classiq/interface/_version.py +1 -1
  15. classiq/interface/backend/backend_preferences.py +8 -5
  16. classiq/interface/backend/pydantic_backend.py +1 -1
  17. classiq/interface/chemistry/operator.py +0 -204
  18. classiq/interface/execution/primitives.py +1 -0
  19. classiq/interface/generator/compiler_keywords.py +4 -0
  20. classiq/interface/generator/functions/type_name.py +6 -0
  21. classiq/interface/generator/generated_circuit_data.py +22 -7
  22. classiq/interface/generator/model/model.py +3 -0
  23. classiq/interface/generator/model/preferences/preferences.py +13 -0
  24. classiq/interface/generator/quantum_function_call.py +4 -2
  25. classiq/interface/model/handle_binding.py +50 -5
  26. classiq/interface/model/quantum_type.py +16 -0
  27. classiq/interface/server/routes.py +1 -3
  28. classiq/model_expansions/capturing/captured_vars.py +102 -19
  29. classiq/model_expansions/closure.py +19 -56
  30. classiq/model_expansions/function_builder.py +13 -8
  31. classiq/model_expansions/generative_functions.py +15 -1
  32. classiq/model_expansions/interpreter.py +94 -32
  33. classiq/model_expansions/model_tables.py +4 -0
  34. classiq/model_expansions/quantum_operations/call_emitter.py +61 -2
  35. classiq/model_expansions/quantum_operations/classicalif.py +1 -1
  36. classiq/model_expansions/quantum_operations/control.py +3 -10
  37. classiq/model_expansions/quantum_operations/emitter.py +1 -1
  38. classiq/model_expansions/quantum_operations/quantum_assignment_operation.py +1 -2
  39. classiq/model_expansions/quantum_operations/repeat.py +4 -3
  40. classiq/model_expansions/scope.py +7 -1
  41. classiq/model_expansions/scope_initialization.py +34 -25
  42. classiq/model_expansions/transformers/var_splitter.py +57 -7
  43. classiq/open_library/__init__.py +4 -0
  44. classiq/open_library/functions/__init__.py +130 -0
  45. classiq/{qmod/builtins → open_library}/functions/amplitude_estimation.py +2 -2
  46. classiq/{qmod/builtins → open_library}/functions/discrete_sine_cosine_transform.py +6 -4
  47. classiq/{qmod/builtins → open_library}/functions/grover.py +2 -2
  48. classiq/{qmod/builtins → open_library}/functions/linear_pauli_rotation.py +1 -1
  49. classiq/{qmod/builtins → open_library}/functions/modular_exponentiation.py +2 -2
  50. classiq/{qmod/builtins → open_library}/functions/qpe.py +2 -2
  51. classiq/{qmod/builtins → open_library}/functions/state_preparation.py +6 -149
  52. classiq/{qmod/builtins → open_library}/functions/swap_test.py +1 -1
  53. classiq/open_library/functions/utility_functions.py +81 -0
  54. classiq/{qmod/builtins → open_library}/functions/variational.py +1 -1
  55. classiq/qmod/builtins/functions/__init__.py +4 -130
  56. classiq/qmod/builtins/functions/allocation.py +150 -0
  57. classiq/qmod/builtins/functions/arithmetic.py +0 -34
  58. classiq/qmod/builtins/functions/operators.py +0 -6
  59. classiq/qmod/create_model_function.py +8 -162
  60. classiq/qmod/generative.py +0 -16
  61. classiq/qmod/model_state_container.py +7 -0
  62. classiq/qmod/native/pretty_printer.py +10 -11
  63. classiq/qmod/pretty_print/pretty_printer.py +1 -1
  64. classiq/qmod/qfunc.py +11 -12
  65. classiq/qmod/qmod_variable.py +1 -3
  66. classiq/qmod/quantum_expandable.py +21 -0
  67. classiq/qmod/quantum_function.py +65 -3
  68. {classiq-0.62.0.dist-info → classiq-0.63.0.dist-info}/METADATA +1 -1
  69. {classiq-0.62.0.dist-info → classiq-0.63.0.dist-info}/RECORD +74 -71
  70. classiq/qmod/builtins/functions/utility_functions.py +0 -43
  71. /classiq/{qmod/builtins → open_library}/functions/hea.py +0 -0
  72. /classiq/{qmod/builtins → open_library}/functions/qaoa_penalty.py +0 -0
  73. /classiq/{qmod/builtins → open_library}/functions/qft_functions.py +0 -0
  74. /classiq/{qmod/builtins → open_library}/functions/qsvt.py +0 -0
  75. {classiq-0.62.0.dist-info → classiq-0.63.0.dist-info}/WHEEL +0 -0
@@ -1,4 +1,5 @@
1
1
  from collections.abc import Sequence
2
+ from enum import IntEnum
2
3
  from typing import TYPE_CHECKING, Annotated, Any, Optional, Union
3
4
 
4
5
  import pydantic
@@ -64,6 +65,13 @@ else:
64
65
  ]
65
66
 
66
67
 
68
+ class OptimizationLevel(IntEnum):
69
+ NONE = 0
70
+ LIGHT = 1
71
+ MEDIUM = 2
72
+ HIGH = 3
73
+
74
+
67
75
  class TranspilationOption(StrEnum):
68
76
  NONE = "none"
69
77
  DECOMPOSE = "decompose"
@@ -157,6 +165,11 @@ class Preferences(pydantic.BaseModel, extra="forbid"):
157
165
  synthesize_all_separately: bool = pydantic.Field(
158
166
  default=False,
159
167
  description="If true, a heuristic is used to determine if a function should be synthesized separately",
168
+ deprecated=True,
169
+ )
170
+ optimization_level: OptimizationLevel = pydantic.Field(
171
+ default=OptimizationLevel.HIGH,
172
+ description="The optimization level used during synthesis; determines the trade-off between synthesis speed and the quality of the results",
160
173
  )
161
174
  output_format: PydanticConstrainedQuantumFormatList = pydantic.Field(
162
175
  default=[QuantumFormat.QASM],
@@ -22,7 +22,9 @@ from pydantic_core.core_schema import ValidationInfo
22
22
  from classiq.interface.exceptions import ClassiqControlError, ClassiqValueError
23
23
  from classiq.interface.generator import function_param_list, function_params as f_params
24
24
  from classiq.interface.generator.arith.arithmetic import Arithmetic
25
- from classiq.interface.generator.compiler_keywords import EXPANDED_KEYWORD
25
+ from classiq.interface.generator.compiler_keywords import (
26
+ generate_original_function_name,
27
+ )
26
28
  from classiq.interface.generator.control_state import ControlState
27
29
  from classiq.interface.generator.function_params import (
28
30
  NAME_REGEX,
@@ -237,7 +239,7 @@ class SynthesisQuantumFunctionCall(BaseModel):
237
239
  suffix = f"{SUFFIX_MARKER}_{randomize_suffix()}"
238
240
  if not function or params is None:
239
241
  return name if name else suffix
240
- return f"{function.split(f'_{EXPANDED_KEYWORD}')[0]}_{suffix}"
242
+ return f"{generate_original_function_name(function)}_{suffix}"
241
243
 
242
244
  @pydantic.model_validator(mode="before")
243
245
  @classmethod
@@ -149,8 +149,7 @@ class SubscriptHandleBinding(NestedHandleBinding):
149
149
  if (
150
150
  isinstance(other_handle, SlicedHandleBinding)
151
151
  and self.index.is_evaluated()
152
- and other_handle.start.is_evaluated()
153
- and other_handle.end.is_evaluated()
152
+ and other_handle._is_evaluated()
154
153
  ):
155
154
  return (
156
155
  other_handle.start.to_int_value()
@@ -159,6 +158,26 @@ class SubscriptHandleBinding(NestedHandleBinding):
159
158
  )
160
159
  return False
161
160
 
161
+ def replace_prefix(
162
+ self, prefix: HandleBinding, replacement: HandleBinding
163
+ ) -> HandleBinding:
164
+ if (
165
+ isinstance(prefix, SlicedHandleBinding)
166
+ and self.base_handle == prefix.base_handle
167
+ and self.index.is_evaluated()
168
+ and prefix._is_evaluated()
169
+ and prefix.start.to_int_value()
170
+ <= self.index.to_int_value()
171
+ < prefix.end.to_int_value()
172
+ ):
173
+ return SubscriptHandleBinding(
174
+ base_handle=replacement,
175
+ index=Expression(
176
+ expr=str(self.index.to_int_value() - prefix.start.to_int_value())
177
+ ),
178
+ )
179
+ return super().replace_prefix(prefix, replacement)
180
+
162
181
 
163
182
  class SlicedHandleBinding(NestedHandleBinding):
164
183
  start: Expression
@@ -192,7 +211,7 @@ class SlicedHandleBinding(NestedHandleBinding):
192
211
  )
193
212
 
194
213
  def _tail_overlaps(self, other_handle: "HandleBinding") -> bool:
195
- if not self.start.is_evaluated() or not self.end.is_evaluated():
214
+ if not self._is_evaluated():
196
215
  return False
197
216
  start = self.start.to_int_value()
198
217
  end = self.end.to_int_value()
@@ -203,8 +222,7 @@ class SlicedHandleBinding(NestedHandleBinding):
203
222
  return start <= other_handle.index.to_int_value() < end
204
223
  if (
205
224
  isinstance(other_handle, SlicedHandleBinding)
206
- and other_handle.start.is_evaluated()
207
- and other_handle.end.is_evaluated()
225
+ and other_handle._is_evaluated()
208
226
  ):
209
227
  other_start = other_handle.start.to_int_value()
210
228
  other_end = other_handle.end.to_int_value()
@@ -231,6 +249,33 @@ class SlicedHandleBinding(NestedHandleBinding):
231
249
  )
232
250
  return Expression(expr=f"({self.base_handle.end})-({self.end})")
233
251
 
252
+ def replace_prefix(
253
+ self, prefix: HandleBinding, replacement: HandleBinding
254
+ ) -> HandleBinding:
255
+ if (
256
+ isinstance(prefix, SlicedHandleBinding)
257
+ and self.base_handle == prefix.base_handle
258
+ and self._is_evaluated()
259
+ and prefix._is_evaluated()
260
+ ):
261
+ prefix_start = prefix.start.to_int_value()
262
+ prefix_end = prefix.end.to_int_value()
263
+ self_start = self.start.to_int_value()
264
+ self_end = self.end.to_int_value()
265
+ if (
266
+ prefix_start <= self_start < prefix_end
267
+ and prefix_start < self_end <= prefix_end
268
+ ):
269
+ return SlicedHandleBinding(
270
+ base_handle=replacement,
271
+ start=Expression(expr=str(self_start - prefix_start)),
272
+ end=Expression(expr=str(self_end - prefix_start)),
273
+ )
274
+ return super().replace_prefix(prefix, replacement)
275
+
276
+ def _is_evaluated(self) -> bool:
277
+ return self.start.is_evaluated() and self.end.is_evaluated()
278
+
234
279
 
235
280
  class FieldHandleBinding(NestedHandleBinding):
236
281
  field: str
@@ -59,6 +59,10 @@ class QuantumType(HashableASTNode):
59
59
  def type_name(self) -> str:
60
60
  raise NotImplementedError
61
61
 
62
+ @property
63
+ def is_instantiated(self) -> bool:
64
+ raise NotImplementedError
65
+
62
66
 
63
67
  class QuantumScalar(QuantumType):
64
68
  def get_proxy(self, handle: "HandleBinding") -> QmodQScalarProxy:
@@ -88,6 +92,10 @@ class QuantumBit(QuantumScalar):
88
92
  def type_name(self) -> str:
89
93
  return "Quantum bit"
90
94
 
95
+ @property
96
+ def is_instantiated(self) -> bool:
97
+ return True
98
+
91
99
 
92
100
  class QuantumBitvector(QuantumType):
93
101
  element_type: "ConcreteQuantumType" = Field(
@@ -142,6 +150,10 @@ class QuantumBitvector(QuantumType):
142
150
  def type_name(self) -> str:
143
151
  return "Quantum array"
144
152
 
153
+ @property
154
+ def is_instantiated(self) -> bool:
155
+ return self.length is not None and self.element_type.is_instantiated
156
+
145
157
 
146
158
  class QuantumNumeric(QuantumScalar):
147
159
  kind: Literal["qnum"]
@@ -211,6 +223,10 @@ class QuantumNumeric(QuantumScalar):
211
223
  def type_name(self) -> str:
212
224
  return "Quantum numeric"
213
225
 
226
+ @property
227
+ def is_instantiated(self) -> bool:
228
+ return self.size is not None
229
+
214
230
 
215
231
  class RegisterQuantumType(BaseModel):
216
232
  quantum_types: "ConcreteQuantumType"
@@ -64,9 +64,7 @@ GENERATE_RESOURCE_ESTIMATOR_REPORT = "/resource_estimator_report"
64
64
  TASKS_SOLVE_EXACT_SUFFIX = "/tasks/solve_exact"
65
65
 
66
66
  GENERATE_HAMILTONIAN_SUFFIX = "/generate_hamiltonian"
67
- GENERATE_HAMILTONIAN_FULL_PATH = (
68
- SYNTHESIS_NON_VERSIONED_PREFIX + GENERATE_HAMILTONIAN_SUFFIX
69
- )
67
+ GENERATE_HAMILTONIAN_FULL_PATH = CHEMISTRY_PREFIX + GENERATE_HAMILTONIAN_SUFFIX
70
68
 
71
69
  CONVERSION_GENERATED_CIRCUIT_TO_EXECUTION_INPUT_SUFFIX = "/execution_input"
72
70
  CONVERSION_GENERATED_CIRCUIT_TO_EXECUTION_INPUT_FULL = (
@@ -8,13 +8,18 @@ from classiq.interface.exceptions import (
8
8
  ClassiqExpansionError,
9
9
  ClassiqInternalExpansionError,
10
10
  )
11
+ from classiq.interface.generator.expressions.expression import Expression
11
12
  from classiq.interface.generator.functions.port_declaration import (
12
13
  PortDeclarationDirection,
13
14
  )
14
- from classiq.interface.model.handle_binding import HandleBinding, NestedHandleBinding
15
+ from classiq.interface.model.handle_binding import (
16
+ HandleBinding,
17
+ NestedHandleBinding,
18
+ SlicedHandleBinding,
19
+ )
15
20
  from classiq.interface.model.port_declaration import PortDeclaration
16
21
  from classiq.interface.model.quantum_function_call import ArgValue
17
- from classiq.interface.model.quantum_type import QuantumType
22
+ from classiq.interface.model.quantum_type import QuantumBitvector, QuantumType
18
23
  from classiq.interface.model.variable_declaration_statement import (
19
24
  VariableDeclarationStatement,
20
25
  )
@@ -104,6 +109,11 @@ class _CapturedHandle:
104
109
  return dataclasses.replace(self, is_propagated=False)
105
110
  return self
106
111
 
112
+ def set_symbol(
113
+ self, handle: HandleBinding, quantum_type: QuantumType
114
+ ) -> "_CapturedHandle":
115
+ return dataclasses.replace(self, handle=handle, quantum_type=quantum_type)
116
+
107
117
 
108
118
  @dataclass
109
119
  class CapturedVars:
@@ -145,23 +155,10 @@ class CapturedVars:
145
155
  captured_handle = self._conjugate_direction(
146
156
  existing_captured_handle, captured_handle
147
157
  )
148
- elif captured_handle.handle in existing_captured_handle.handle:
149
- if existing_captured_handle.direction in (
150
- PortDirection.Input,
151
- PortDirection.Outin,
152
- ):
153
- raise ClassiqInternalExpansionError(
154
- "Captured handle is already freed"
155
- )
156
- captured_handle = existing_captured_handle
157
- elif existing_captured_handle.handle in captured_handle.handle:
158
- if captured_handle.direction in (
159
- PortDirection.Output,
160
- PortDirection.Outin,
161
- ):
162
- raise ClassiqInternalExpansionError(
163
- "Captured handle is already allocated"
164
- )
158
+ elif captured_handle.handle.overlaps(existing_captured_handle.handle):
159
+ captured_handle = self._intersect_handles(
160
+ existing_captured_handle, captured_handle
161
+ )
165
162
  else:
166
163
  new_captured_handles.append(existing_captured_handle)
167
164
  new_captured_handles.append(captured_handle)
@@ -204,6 +201,92 @@ class CapturedVars:
204
201
  raise ClassiqInternalExpansionError("Captured handle is already freed")
205
202
  return captured_handle
206
203
 
204
+ def _intersect_handles(
205
+ self,
206
+ existing_captured_handle: _CapturedHandle,
207
+ captured_handle: _CapturedHandle,
208
+ ) -> _CapturedHandle:
209
+ if captured_handle.handle in existing_captured_handle.handle:
210
+ if existing_captured_handle.direction in (
211
+ PortDirection.Input,
212
+ PortDirection.Outin,
213
+ ):
214
+ raise ClassiqInternalExpansionError("Captured handle is already freed")
215
+ return existing_captured_handle
216
+
217
+ if existing_captured_handle.handle in captured_handle.handle:
218
+ if captured_handle.direction in (
219
+ PortDirection.Output,
220
+ PortDirection.Outin,
221
+ ):
222
+ raise ClassiqInternalExpansionError(
223
+ "Captured handle is already allocated"
224
+ )
225
+ return captured_handle
226
+
227
+ sliced_handle, quantum_type, other_handle = self._get_sliced_handle(
228
+ existing_captured_handle, captured_handle
229
+ )
230
+ if not isinstance(other_handle, SlicedHandleBinding):
231
+ return captured_handle.set_symbol(sliced_handle, quantum_type)
232
+
233
+ merged_handle, merged_quantum_type = self._merge_sliced_handles(
234
+ sliced_handle, other_handle, quantum_type
235
+ )
236
+ return captured_handle.set_symbol(merged_handle, merged_quantum_type)
237
+
238
+ @staticmethod
239
+ def _get_sliced_handle(
240
+ existing_captured_handle: _CapturedHandle,
241
+ captured_handle: _CapturedHandle,
242
+ ) -> tuple[SlicedHandleBinding, QuantumBitvector, HandleBinding]:
243
+ handle_1 = existing_captured_handle.handle
244
+ quantum_type_1 = existing_captured_handle.quantum_type
245
+ handle_2 = captured_handle.handle
246
+ quantum_type_2 = captured_handle.quantum_type
247
+ if isinstance(handle_1, SlicedHandleBinding):
248
+ sliced_handle = handle_1
249
+ other_handle = handle_2
250
+ quantum_type = quantum_type_1
251
+ elif isinstance(handle_2, SlicedHandleBinding):
252
+ sliced_handle = handle_2
253
+ other_handle = handle_1
254
+ quantum_type = quantum_type_2
255
+ else:
256
+ raise ClassiqInternalExpansionError(
257
+ f"Unexpected overlapping handles {handle_1} and {handle_2}"
258
+ )
259
+ if not isinstance(quantum_type, QuantumBitvector):
260
+ raise ClassiqInternalExpansionError
261
+ return sliced_handle, quantum_type, other_handle
262
+
263
+ @staticmethod
264
+ def _merge_sliced_handles(
265
+ handle_1: SlicedHandleBinding,
266
+ handle_2: SlicedHandleBinding,
267
+ quantum_type: QuantumBitvector,
268
+ ) -> tuple[HandleBinding, QuantumBitvector]:
269
+ if (
270
+ not handle_1.start.is_evaluated()
271
+ or not handle_1.end.is_evaluated()
272
+ or not handle_2.start.is_evaluated()
273
+ or not handle_2.end.is_evaluated()
274
+ ):
275
+ raise ClassiqInternalExpansionError
276
+
277
+ new_start = min(handle_1.start.to_int_value(), handle_2.start.to_int_value())
278
+ new_end = max(handle_1.end.to_int_value(), handle_2.end.to_int_value())
279
+ merged_handle = SlicedHandleBinding(
280
+ base_handle=handle_1.base_handle,
281
+ start=Expression(expr=str(new_start)),
282
+ end=Expression(expr=str(new_end)),
283
+ )
284
+ merged_quantum_type = QuantumBitvector(
285
+ element_type=quantum_type.element_type,
286
+ length=Expression(expr=str(new_end - new_start)),
287
+ )
288
+ return merged_handle, merged_quantum_type
289
+
207
290
  def update(self, other_captured_vars: "CapturedVars") -> None:
208
291
  for captured_handle in other_captured_vars._captured_handles:
209
292
  self._capture_handle(captured_handle)
@@ -1,27 +1,21 @@
1
1
  import dataclasses
2
2
  import json
3
- import uuid
4
3
  from collections.abc import Collection, Sequence
5
4
  from dataclasses import dataclass, field
6
5
  from functools import singledispatch
7
- from symtable import Symbol
8
6
  from typing import Any, Optional
9
7
 
10
8
  from typing_extensions import Self
11
9
 
12
10
  from classiq.interface.exceptions import ClassiqInternalExpansionError
13
- from classiq.interface.generator.functions.builtins.internal_operators import (
14
- All_BUILTINS_OPERATORS,
15
- )
16
11
  from classiq.interface.model.port_declaration import PortDeclaration
17
12
  from classiq.interface.model.quantum_function_declaration import (
18
13
  NamedParamsQuantumFunctionDeclaration,
19
14
  PositionalArg,
20
- QuantumOperandDeclaration,
21
15
  )
16
+ from classiq.interface.model.quantum_lambda_function import QuantumCallable
22
17
  from classiq.interface.model.quantum_statement import QuantumStatement
23
18
 
24
- from classiq import ClassicalParameterDeclaration
25
19
  from classiq.model_expansions.capturing.captured_vars import CapturedVars
26
20
  from classiq.model_expansions.expression_renamer import ExpressionRenamer
27
21
  from classiq.model_expansions.scope import (
@@ -73,15 +67,7 @@ class FunctionClosure(Closure):
73
67
  # The closure is changing across the interpreter flow so it's closure_id may change
74
68
  @property
75
69
  def closure_id(self) -> str:
76
- # builtins operators have side effects, so generate a unique id for than
77
- # may create a bugs. Therefore, with each call to closure_id a new id is
78
- # created
79
- if self.is_lambda or self.name in All_BUILTINS_OPERATORS:
80
- signature = str(uuid.uuid4())
81
- else:
82
- signature = _generate_closure_id(
83
- self.positional_arg_declarations, self.scope.data.values()
84
- )
70
+ signature = _generate_closure_id(self.scope.data.values())
85
71
  return f"{self.name}__{signature}"
86
72
 
87
73
  @property
@@ -144,65 +130,42 @@ class FunctionClosure(Closure):
144
130
  captured_vars=self.captured_vars.clone(),
145
131
  )
146
132
 
133
+ def emit(self) -> QuantumCallable:
134
+ return self.name
135
+
147
136
 
148
137
  @dataclass(frozen=True)
149
138
  class GenerativeFunctionClosure(GenerativeClosure, FunctionClosure):
150
139
  pass
151
140
 
152
141
 
153
- def _generate_closure_id(
154
- args_declaration: Sequence[PositionalArg], evaluated_args: Collection[Evaluated]
155
- ) -> str:
156
- args_signature: dict = {}
157
- for arg_declara, eval_arg in zip(args_declaration, evaluated_args):
158
- args_signature |= _generate_arg_id(arg_declara, eval_arg)
142
+ def _generate_closure_id(evaluated_args: Collection[Evaluated]) -> str:
143
+ args_signature = [
144
+ _evaluated_arg_to_str(eval_arg.value) for eval_arg in evaluated_args
145
+ ]
159
146
  return json.dumps(args_signature)
160
147
 
161
148
 
162
- def _generate_arg_id(
163
- arg_declaration: PositionalArg, evaluated_arg: Evaluated
164
- ) -> dict[str, str]:
165
- arg_value = evaluated_arg.value
166
- arg_name = arg_declaration.name
167
- if isinstance(arg_declaration, ClassicalParameterDeclaration):
168
- return {arg_name: evaluated_classical_param_to_str(arg_value)}
169
- if isinstance(arg_declaration, PortDeclaration):
170
- return {arg_name: _evaluated_port_to_str(arg_value)}
171
- if isinstance(arg_declaration, QuantumOperandDeclaration):
172
- return {arg_name: _evaluated_operand_to_str(arg_value)}
173
-
174
-
149
+ @singledispatch
175
150
  def _evaluated_arg_to_str(arg: Any) -> str:
176
151
  if isinstance(arg, str):
177
152
  return arg
178
- return str(uuid.uuid4())
179
-
180
-
181
- @singledispatch
182
- def _evaluated_port_to_str(arg: Any) -> str:
183
- return _evaluated_arg_to_str(arg)
153
+ if isinstance(arg, QuantumSymbol):
154
+ return _evaluated_quantum_symbol_to_str(arg)
155
+ if isinstance(arg, FunctionClosure):
156
+ return _evaluated_one_operand_to_str(arg)
157
+ if isinstance(arg, list) and arg and isinstance(arg[0], FunctionClosure):
158
+ return _evaluated_operands_list_to_str(arg)
159
+ return evaluated_classical_param_to_str(arg)
184
160
 
185
161
 
186
- @_evaluated_port_to_str.register
187
162
  def _evaluated_quantum_symbol_to_str(port: QuantumSymbol) -> str:
188
163
  return port.quantum_type.model_dump_json(exclude_none=True, exclude={"name"})
189
164
 
190
165
 
191
- @_evaluated_port_to_str.register
192
- def _evaluated_symbol_to_str(port: Symbol) -> str:
193
- return repr(port)
194
-
195
-
196
- @singledispatch
197
- def _evaluated_operand_to_str(arg: Any) -> str:
198
- return _evaluated_arg_to_str(arg)
199
-
200
-
201
- @_evaluated_operand_to_str.register
202
166
  def _evaluated_one_operand_to_str(operand: FunctionClosure) -> str:
203
167
  return operand.closure_id
204
168
 
205
169
 
206
- @_evaluated_operand_to_str.register
207
- def _evaluated_list_operands_to_str(operands: list) -> str:
208
- return json.dumps([_evaluated_operand_to_str(ope) for ope in operands])
170
+ def _evaluated_operands_list_to_str(arg: list[FunctionClosure]) -> str:
171
+ return json.dumps([_evaluated_one_operand_to_str(ope) for ope in arg])
@@ -31,6 +31,7 @@ from classiq.model_expansions.capturing.captured_vars import (
31
31
  )
32
32
  from classiq.model_expansions.closure import Closure, FunctionClosure
33
33
  from classiq.model_expansions.scope import Scope
34
+ from classiq.model_expansions.utils.counted_name_allocator import CountedNameAllocator
34
35
 
35
36
  ClosureType = TypeVar("ClosureType", bound=Closure)
36
37
 
@@ -82,11 +83,14 @@ class FunctionContext(OperationContext[FunctionClosure]):
82
83
 
83
84
 
84
85
  class OperationBuilder:
85
- def __init__(self, functions_scope: Scope) -> None:
86
+ def __init__(
87
+ self, functions_scope: Scope, counted_name_allocator: CountedNameAllocator
88
+ ) -> None:
86
89
  self._operations: list[OperationContext] = []
87
90
  self._blocks: list[str] = []
88
91
  self._functions_scope = functions_scope
89
92
  self._current_source_ref: Optional[SourceReference] = None
93
+ self._counted_name_allocator = counted_name_allocator
90
94
 
91
95
  @property
92
96
  def current_operation(self) -> Closure:
@@ -198,13 +202,14 @@ class OperationBuilder:
198
202
  ) -> NativeFunctionDefinition:
199
203
  name = function_context.name
200
204
  if name != MAIN_FUNCTION_NAME:
201
- idx = 0
202
- new_name = name
203
- while idx == 0 or new_name in self._functions_scope:
204
- new_name = f"{name}_{LAMBDA_KEYWORD + '_0_0_' if function_context.is_lambda else ''}{EXPANDED_KEYWORD}_{idx}"
205
- idx += 1
206
- name = new_name
207
-
205
+ for _ in self.current_scope:
206
+ name = self._counted_name_allocator.allocate(
207
+ f"{name}_{LAMBDA_KEYWORD + '_0_0_' if function_context.is_lambda else ''}{EXPANDED_KEYWORD}"
208
+ )
209
+ if name not in self.current_scope:
210
+ break
211
+ else:
212
+ raise ClassiqInternalExpansionError("Could not allocate function name")
208
213
  new_parameters: list[PortDeclaration] = [
209
214
  param
210
215
  for param in function_context.positional_arg_declarations
@@ -105,7 +105,21 @@ class _InterpreterExpandable(QFunc):
105
105
  positional_arg_declarations=current_operation.positional_arg_declarations,
106
106
  body=self._interpreter._builder._current_statements + [stmt],
107
107
  )
108
- resolve_function_calls(dummy_function, self._get_function_declarations())
108
+ declarative_functions = {
109
+ name: func
110
+ for name, func in self._qmodule.native_defs.items()
111
+ if name not in self._interpreter._top_level_scope
112
+ }
113
+ self._interpreter.update_declarative_functions(
114
+ declarative_functions, self._qmodule
115
+ )
116
+ self._interpreter.update_generative_functions(
117
+ self._qmodule.generative_functions
118
+ )
119
+ func_decls = self._get_function_declarations()
120
+ for dec_func in declarative_functions.values():
121
+ resolve_function_calls(dec_func, func_decls)
122
+ resolve_function_calls(dummy_function, func_decls)
109
123
  stmt = dummy_function.body[-1]
110
124
  with generative_mode_context(False):
111
125
  self._interpreter.emit_statement(stmt)