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
@@ -32,7 +32,7 @@ from classiq.interface.model.handle_binding import (
32
32
  )
33
33
  from classiq.interface.model.inplace_binary_operation import InplaceBinaryOperation
34
34
  from classiq.interface.model.invert import Invert
35
- from classiq.interface.model.model import Model
35
+ from classiq.interface.model.model import MAIN_FUNCTION_NAME, Model
36
36
  from classiq.interface.model.native_function_definition import NativeFunctionDefinition
37
37
  from classiq.interface.model.phase_operation import PhaseOperation
38
38
  from classiq.interface.model.power import Power
@@ -93,11 +93,14 @@ from classiq.model_expansions.scope import Evaluated, QuantumSymbol, Scope
93
93
  from classiq.model_expansions.scope_initialization import (
94
94
  add_constants_to_scope,
95
95
  add_entry_point_params_to_scope,
96
+ add_functions_to_scope,
97
+ add_generative_functions_to_scope,
96
98
  get_main_renamer,
97
99
  init_top_level_scope,
98
100
  )
99
101
  from classiq.model_expansions.utils.counted_name_allocator import CountedNameAllocator
100
102
  from classiq.qmod.builtins.functions import permute
103
+ from classiq.qmod.model_state_container import QMODULE, ModelStateContainer
101
104
  from classiq.qmod.quantum_function import GenerativeQFunc
102
105
  from classiq.qmod.semantics.error_manager import ErrorManager
103
106
 
@@ -107,22 +110,28 @@ class Interpreter:
107
110
  self,
108
111
  model: Model,
109
112
  generative_functions: Optional[list[GenerativeQFunc]] = None,
110
- is_frontend: bool = False,
113
+ is_normalizer: bool = False,
114
+ is_shallow: bool = False,
111
115
  ) -> None:
112
- self._is_frontend = is_frontend
116
+ self._is_normalizer = is_normalizer
117
+ self._is_shallow = is_shallow
113
118
  self._model = model
114
119
  self._top_level_scope = Scope()
115
- self._builder = OperationBuilder(self._top_level_scope)
120
+ self._counted_name_allocator = CountedNameAllocator()
121
+ self._builder = OperationBuilder(
122
+ self._top_level_scope, self._counted_name_allocator
123
+ )
116
124
  self._expanded_functions: dict[str, NativeFunctionDefinition] = {}
117
125
 
126
+ init_top_level_scope(model, generative_functions or [], self._top_level_scope)
118
127
  self._main_renamer: Optional[ExpressionRenamer] = (
119
- get_main_renamer(self._model.functions) if self._is_frontend else None
128
+ get_main_renamer(self._get_function_declarations())
129
+ if self._is_normalizer
130
+ else None
131
+ )
132
+ self._functions_compilation_metadata: dict[str, CompilationMetadata] = dict(
133
+ self._model.functions_compilation_metadata
120
134
  )
121
-
122
- if generative_functions is None:
123
- generative_functions = []
124
- self._generative_functions = generative_functions
125
- init_top_level_scope(model, generative_functions, self._top_level_scope)
126
135
  self._expanded_functions_compilation_metadata: dict[
127
136
  str, CompilationMetadata
128
137
  ] = defaultdict(CompilationMetadata)
@@ -130,13 +139,23 @@ class Interpreter:
130
139
  self._error_manager: ErrorManager = ErrorManager()
131
140
 
132
141
  def _expand_main_func(self) -> None:
133
- main_closure = FunctionClosure.create(
134
- name=self._model.main_func.name,
135
- positional_arg_declarations=self._model.main_func.positional_arg_declarations,
136
- body=self._model.main_func.body,
142
+ main_func = self._top_level_scope[MAIN_FUNCTION_NAME].value
143
+ closure_constructor: Any
144
+ if isinstance(main_func, GenerativeFunctionClosure):
145
+ closure_constructor = GenerativeFunctionClosure
146
+ extra_args = {
147
+ "generative_blocks": {"body": main_func.generative_blocks["body"]}
148
+ }
149
+ else:
150
+ closure_constructor = FunctionClosure
151
+ extra_args = {"body": main_func.body}
152
+ main_closure = closure_constructor.create(
153
+ name=main_func.name,
154
+ positional_arg_declarations=main_func.positional_arg_declarations,
137
155
  scope=Scope(parent=self._top_level_scope),
138
156
  expr_renamer=self._main_renamer,
139
157
  _depth=0,
158
+ **extra_args,
140
159
  )
141
160
 
142
161
  add_entry_point_params_to_scope(
@@ -170,9 +189,9 @@ class Interpreter:
170
189
  execution_preferences=self._model.execution_preferences,
171
190
  functions=list(self._expanded_functions.values()),
172
191
  constants=self._model.constants,
173
- enums=self._model.enums,
174
- types=self._model.types,
175
- qstructs=self._model.qstructs,
192
+ enums=list(QMODULE.enum_decls.values()),
193
+ types=list(QMODULE.type_decls.values()),
194
+ qstructs=list(QMODULE.qstruct_decls.values()),
176
195
  debug_info=self._model.debug_info,
177
196
  functions_compilation_metadata=self._expanded_functions_compilation_metadata,
178
197
  )
@@ -196,7 +215,9 @@ class Interpreter:
196
215
  for idx, param in enumerate(function.func_decl.positional_arg_declarations)
197
216
  ]
198
217
  func_decl = NamedParamsQuantumFunctionDeclaration(
199
- name=function.func_decl.name or "<lambda>",
218
+ name=self._counted_name_allocator.allocate(
219
+ function.func_decl.name or "<lambda>"
220
+ ),
200
221
  positional_arg_declarations=renamed_params,
201
222
  )
202
223
 
@@ -275,7 +296,7 @@ class Interpreter:
275
296
 
276
297
  @emit.register
277
298
  def emit_quantum_assignment_operation(self, op: QuantumAssignmentOperation) -> None:
278
- if self._is_frontend:
299
+ if self._is_normalizer:
279
300
  ShallowEmitter(
280
301
  self, "assignment_operation", components=["expression", "result_var"]
281
302
  ).emit(op)
@@ -284,7 +305,7 @@ class Interpreter:
284
305
 
285
306
  @emit.register
286
307
  def emit_inplace_binary_operation(self, op: InplaceBinaryOperation) -> None:
287
- if self._is_frontend:
308
+ if self._is_normalizer:
288
309
  ShallowEmitter(
289
310
  self, "inplace_binary_operation", components=["target", "value"]
290
311
  ).emit(op)
@@ -303,7 +324,7 @@ class Interpreter:
303
324
 
304
325
  @emit.register
305
326
  def emit_within_apply(self, within_apply: WithinApply) -> None:
306
- if self._is_frontend:
327
+ if self._is_normalizer:
307
328
  ShallowEmitter(
308
329
  self,
309
330
  WITHIN_APPLY_NAME,
@@ -314,7 +335,7 @@ class Interpreter:
314
335
 
315
336
  @emit.register
316
337
  def emit_invert(self, invert: Invert) -> None:
317
- if self._is_frontend:
338
+ if self._is_normalizer:
318
339
  ShallowEmitter(self, INVERT_OPERATOR_NAME, components=["body"]).emit(invert)
319
340
  else:
320
341
  InvertEmitter(self).emit(invert)
@@ -325,7 +346,7 @@ class Interpreter:
325
346
 
326
347
  @emit.register
327
348
  def emit_control(self, control: Control) -> None:
328
- if self._is_frontend:
349
+ if self._is_normalizer:
329
350
  ShallowEmitter(
330
351
  self,
331
352
  CONTROL_OPERATOR_NAME,
@@ -336,7 +357,7 @@ class Interpreter:
336
357
 
337
358
  @emit.register
338
359
  def emit_power(self, power: Power) -> None:
339
- if self._is_frontend:
360
+ if self._is_normalizer:
340
361
  ShallowEmitter(
341
362
  self, CONTROL_OPERATOR_NAME, components=["power", "body"]
342
363
  ).emit(power)
@@ -345,7 +366,7 @@ class Interpreter:
345
366
 
346
367
  @emit.register
347
368
  def emit_phase(self, phase: PhaseOperation) -> None:
348
- if self._is_frontend:
369
+ if self._is_normalizer:
349
370
  ShallowEmitter(self, "phase", components=["expression", "theta"]).emit(
350
371
  phase
351
372
  )
@@ -370,9 +391,11 @@ class Interpreter:
370
391
  def _expand_operation(self, operation: Closure) -> OperationContext:
371
392
  with self._builder.operation_context(operation) as context:
372
393
  if isinstance(operation, FunctionClosure) and (
373
- self._expanded_functions.get(operation.closure_id) is not None
394
+ (func_def := self._expanded_functions.get(operation.closure_id))
395
+ is not None
374
396
  ):
375
- pass
397
+ captured_vars = self._top_level_scope[func_def.name].value.captured_vars
398
+ operation.captured_vars.update(captured_vars)
376
399
  elif isinstance(operation, FunctionClosure) and operation.name == "permute":
377
400
  # special expansion since permute is generative
378
401
  self._expand_permute()
@@ -403,11 +426,50 @@ class Interpreter:
403
426
  self._expand_block(calls, "body")
404
427
 
405
428
  def _get_function_declarations(self) -> Sequence[QuantumFunctionDeclaration]:
406
- return (
407
- self._model.functions
408
- + [gen_func.func_decl for gen_func in self._generative_functions]
409
- + list(self._expanded_functions.values())
410
- )
429
+ return [
430
+ QuantumFunctionDeclaration(
431
+ name=func_closure.name,
432
+ positional_arg_declarations=func_closure.positional_arg_declarations,
433
+ )
434
+ for func in self._top_level_scope.values()
435
+ if isinstance(func_closure := func.value, FunctionClosure)
436
+ ]
411
437
 
412
438
  def add_constant(self, constant: Constant) -> None:
413
439
  add_constants_to_scope([constant], self._top_level_scope)
440
+
441
+ def update_declarative_functions(
442
+ self,
443
+ functions: dict[str, NativeFunctionDefinition],
444
+ qmodule: ModelStateContainer,
445
+ ) -> None:
446
+ add_functions_to_scope(list(functions.values()), self._top_level_scope)
447
+ for dec_func_name in functions:
448
+ if dec_func_name in qmodule.functions_compilation_metadata:
449
+ self._functions_compilation_metadata[dec_func_name] = (
450
+ qmodule.functions_compilation_metadata[dec_func_name]
451
+ )
452
+
453
+ def update_generative_functions(
454
+ self, generative_functions: dict[str, GenerativeQFunc]
455
+ ) -> None:
456
+ add_generative_functions_to_scope(
457
+ list(generative_functions.values()), self._top_level_scope
458
+ )
459
+ for name, gen_func in generative_functions.items():
460
+ if gen_func.compilation_metadata is not None:
461
+ self._functions_compilation_metadata[name] = (
462
+ gen_func.compilation_metadata
463
+ )
464
+
465
+ def add_purely_declarative_function(self, function: FunctionClosure) -> None:
466
+ functions_to_add = [function.name] + QMODULE.function_dependencies[
467
+ function.name
468
+ ]
469
+ for func in functions_to_add:
470
+ if func not in self._expanded_functions and func in QMODULE.native_defs:
471
+ self._expanded_functions[func] = QMODULE.native_defs[func]
472
+ if func in QMODULE.functions_compilation_metadata:
473
+ self._expanded_functions_compilation_metadata[func] = (
474
+ QMODULE.functions_compilation_metadata[func]
475
+ )
@@ -78,10 +78,14 @@ class SymbolTable:
78
78
  @classmethod
79
79
  def init_user_enums(cls, user_enums: list[EnumDeclaration]) -> None:
80
80
  cls.enum_table = EnumTable(user_enums)
81
+ QMODULE.enum_decls = {enum_decl.name: enum_decl for enum_decl in user_enums}
81
82
 
82
83
  @classmethod
83
84
  def init_user_types(cls, user_types: list[StructDeclaration]) -> None:
84
85
  cls.type_table = StructTable(user_types)
86
+ QMODULE.type_decls = {
87
+ struct_decl.name: struct_decl for struct_decl in user_types
88
+ }
85
89
 
86
90
  @classmethod
87
91
  def init_user_qstructs(cls, user_qstructs: list[QStructDeclaration]) -> None:
@@ -8,6 +8,9 @@ from typing import (
8
8
  )
9
9
 
10
10
  from classiq.interface.debug_info.debug_info import FunctionDebugInfo
11
+ from classiq.interface.generator.functions.port_declaration import (
12
+ PortDeclarationDirection,
13
+ )
11
14
  from classiq.interface.generator.generated_circuit_data import OperationLevel
12
15
  from classiq.interface.model.classical_parameter_declaration import (
13
16
  ClassicalParameterDeclaration,
@@ -20,6 +23,10 @@ from classiq.interface.model.quantum_function_declaration import (
20
23
  NamedParamsQuantumFunctionDeclaration,
21
24
  PositionalArg,
22
25
  )
26
+ from classiq.interface.model.quantum_lambda_function import (
27
+ OperandIdentifier,
28
+ QuantumLambdaFunction,
29
+ )
23
30
  from classiq.interface.model.quantum_statement import QuantumStatement
24
31
  from classiq.interface.model.variable_declaration_statement import (
25
32
  VariableDeclarationStatement,
@@ -28,7 +35,7 @@ from classiq.interface.model.variable_declaration_statement import (
28
35
  from classiq.model_expansions.capturing.captured_vars import (
29
36
  validate_args_are_not_propagated,
30
37
  )
31
- from classiq.model_expansions.closure import FunctionClosure
38
+ from classiq.model_expansions.closure import FunctionClosure, GenerativeClosure
32
39
  from classiq.model_expansions.evaluators.argument_types import (
33
40
  add_information_from_output_arguments,
34
41
  )
@@ -45,6 +52,7 @@ from classiq.model_expansions.quantum_operations.emitter import (
45
52
  from classiq.model_expansions.scope import Evaluated, QuantumSymbol, Scope
46
53
  from classiq.model_expansions.transformers.var_splitter import VarSplitter
47
54
  from classiq.qmod.builtins.functions import allocate, free
55
+ from classiq.qmod.model_state_container import QMODULE
48
56
 
49
57
  if TYPE_CHECKING:
50
58
  from classiq.model_expansions.interpreter import Interpreter
@@ -65,7 +73,7 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
65
73
  self, name: str, body: Sequence[QuantumStatement]
66
74
  ) -> QuantumFunctionCall:
67
75
  wrapping_function = FunctionClosure.create(
68
- name=name,
76
+ name=self._counted_name_allocator.allocate(name),
69
77
  body=body,
70
78
  scope=Scope(parent=self._current_scope),
71
79
  is_lambda=True,
@@ -98,6 +106,13 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
98
106
  )
99
107
  new_positional_arg_decls = new_declaration.positional_arg_declarations
100
108
  is_atomic = function.is_atomic
109
+ if (
110
+ self._interpreter._is_shallow
111
+ and self._is_function_purely_declarative(function)
112
+ and self._are_args_purely_declarative(args)
113
+ ):
114
+ is_atomic = True
115
+ self._interpreter.add_purely_declarative_function(function)
101
116
  new_function_name = function.name
102
117
  if not is_atomic: # perform monomorphization per interpreted parameters set
103
118
  self._add_params_to_scope(
@@ -253,3 +268,47 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
253
268
  evaluated_args,
254
269
  ),
255
270
  )
271
+
272
+ def _is_function_purely_declarative(self, function: FunctionClosure) -> bool:
273
+ if function.name not in QMODULE.native_defs:
274
+ return False
275
+
276
+ if isinstance(function, GenerativeClosure):
277
+ return False
278
+
279
+ if any(
280
+ not param.quantum_type.is_instantiated
281
+ for param in function.positional_arg_declarations
282
+ if isinstance(param, PortDeclaration)
283
+ and param.direction == PortDeclarationDirection.Output
284
+ ):
285
+ return False
286
+
287
+ dependencies = QMODULE.function_dependencies[function.name]
288
+ return self._are_identifiers_purely_declarative(dependencies)
289
+
290
+ def _are_args_purely_declarative(self, args: list[ArgValue]) -> bool:
291
+ if any(
292
+ isinstance(arg, (OperandIdentifier, QuantumLambdaFunction))
293
+ or (
294
+ isinstance(arg, list)
295
+ and any(
296
+ isinstance(item, (OperandIdentifier, QuantumLambdaFunction))
297
+ for item in arg
298
+ )
299
+ )
300
+ for arg in args
301
+ ):
302
+ return False
303
+
304
+ dependencies = [arg for arg in args if isinstance(arg, str)]
305
+ dependencies += [
306
+ cast(str, item) for arg in args if isinstance(arg, list) for item in arg
307
+ ]
308
+ return self._are_identifiers_purely_declarative(dependencies)
309
+
310
+ def _are_identifiers_purely_declarative(self, dependencies: list[str]) -> bool:
311
+ return not any(
312
+ isinstance(self._current_scope[dep].value, GenerativeClosure)
313
+ for dep in dependencies
314
+ )
@@ -45,7 +45,7 @@ class ClassicalIfEmitter(CallEmitter[ClassicalIf]):
45
45
  return
46
46
 
47
47
  then_else_func = FunctionClosure.create(
48
- name=op_name,
48
+ name=self._counted_name_allocator.allocate("then" if condition else "else"),
49
49
  body=body,
50
50
  scope=Scope(parent=self._current_scope),
51
51
  is_lambda=True,
@@ -1,5 +1,3 @@
1
- from typing import cast
2
-
3
1
  from sympy import Equality
4
2
  from sympy.logic.boolalg import Boolean
5
3
  from typing_extensions import TypeGuard
@@ -55,6 +53,9 @@ from classiq.qmod.builtins.functions.standard_gates import X
55
53
 
56
54
  class ControlEmitter(ExpressionOperationEmitter[Control]):
57
55
  def emit(self, control: Control, /) -> None:
56
+ validate_args_are_not_propagated(
57
+ extract_handles(control.expression), extract_handles(control.body)
58
+ )
58
59
  condition = control.expression
59
60
  arrays_with_subscript = self.split_symbols(
60
61
  condition, self._counted_name_allocator.allocate
@@ -117,10 +118,6 @@ class ControlEmitter(ExpressionOperationEmitter[Control]):
117
118
  scope=Scope(parent=self._current_scope),
118
119
  )
119
120
  context = self._expand_operation(control_operation)
120
- validate_args_are_not_propagated(
121
- control.var_handles,
122
- extract_handles([block.statements for block in context.blocks.values()]),
123
- )
124
121
  self._update_control_state(control)
125
122
  self.emit_statement(
126
123
  control.model_copy(update=dict(body=context.statements("body")))
@@ -130,10 +127,6 @@ class ControlEmitter(ExpressionOperationEmitter[Control]):
130
127
  wrapping_function = self._create_expanded_wrapping_function(
131
128
  CONTROL_OPERATOR_NAME, control.body
132
129
  )
133
- validate_args_are_not_propagated(
134
- control.var_handles,
135
- cast(list[HandleBinding], wrapping_function.positional_args),
136
- )
137
130
  self._update_control_state(control)
138
131
  self.emit_statement(control.model_copy(update=dict(body=[wrapping_function])))
139
132
 
@@ -54,7 +54,7 @@ class Emitter(Generic[QuantumStatementT]):
54
54
  self._interpreter._expanded_functions_compilation_metadata
55
55
  )
56
56
  self._functions_compilation_metadata = (
57
- self._interpreter._model.functions_compilation_metadata
57
+ self._interpreter._functions_compilation_metadata
58
58
  )
59
59
 
60
60
  @abstractmethod
@@ -123,8 +123,7 @@ class QuantumAssignmentOperationEmitter(
123
123
  self, op: ArithmeticOperation
124
124
  ) -> tuple[ArithmeticOperation, Expression, bool]:
125
125
  if (
126
- self._interpreter._is_frontend
127
- or op.operation_kind
126
+ op.operation_kind
128
127
  not in (
129
128
  ArithmeticOperationKind.Assignment,
130
129
  ArithmeticOperationKind.InplaceXor,
@@ -22,10 +22,11 @@ class RepeatEmitter(CallEmitter[Repeat]):
22
22
  raise ClassiqExpansionError(
23
23
  f"repeat count must be non-negative, got {count}"
24
24
  )
25
+ op_name = self._counted_name_allocator.allocate(REPEAT_OPERATOR_NAME)
25
26
  for i in range(count):
26
- self._emit_iteration(repeat, i)
27
+ self._emit_iteration(repeat, i, op_name)
27
28
 
28
- def _emit_iteration(self, repeat: Repeat, i: int) -> None:
29
+ def _emit_iteration(self, repeat: Repeat, i: int, op_name: str) -> None:
29
30
  closure_constructor: type[FunctionClosure]
30
31
  extra_args: dict
31
32
  if repeat.is_generative():
@@ -41,7 +42,7 @@ class RepeatEmitter(CallEmitter[Repeat]):
41
42
  closure_constructor = FunctionClosure
42
43
  extra_args = {}
43
44
  iteration_function = closure_constructor.create(
44
- name=REPEAT_OPERATOR_NAME,
45
+ name=op_name,
45
46
  positional_arg_declarations=[
46
47
  ClassicalParameterDeclaration(
47
48
  name=repeat.iter_var, classical_type=Integer()
@@ -163,8 +163,14 @@ class Evaluated: # FIXME: Merge with EvaluatedExpression if possible
163
163
  return int(self.value)
164
164
 
165
165
  def emit(self) -> ArgValue:
166
- if isinstance(self.value, QuantumSymbol):
166
+ from classiq.model_expansions.closure import FunctionClosure
167
+
168
+ if isinstance(self.value, (QuantumSymbol, FunctionClosure)):
167
169
  return self.value.emit()
170
+ if isinstance(self.value, list) and all(
171
+ isinstance(item, FunctionClosure) for item in self.value
172
+ ):
173
+ return [item.emit() for item in self.value]
168
174
 
169
175
  ret = Expression(expr=evaluated_to_str(self.value))
170
176
  ret._evaluated_expr = EvaluatedExpression(value=self.value)
@@ -1,4 +1,5 @@
1
1
  from collections.abc import Sequence
2
+ from typing import TYPE_CHECKING
2
3
 
3
4
  from classiq.interface.exceptions import ClassiqError
4
5
  from classiq.interface.generator.constant import Constant
@@ -12,7 +13,11 @@ from classiq.interface.model.handle_binding import HandleBinding
12
13
  from classiq.interface.model.model import MAIN_FUNCTION_NAME, Model
13
14
  from classiq.interface.model.native_function_definition import NativeFunctionDefinition
14
15
  from classiq.interface.model.port_declaration import PortDeclaration
15
- from classiq.interface.model.quantum_function_declaration import PositionalArg
16
+ from classiq.interface.model.quantum_function_declaration import (
17
+ NamedParamsQuantumFunctionDeclaration,
18
+ PositionalArg,
19
+ QuantumFunctionDeclaration,
20
+ )
16
21
 
17
22
  from classiq.model_expansions.closure import FunctionClosure, GenerativeFunctionClosure
18
23
  from classiq.model_expansions.evaluators.classical_expression import (
@@ -32,12 +37,14 @@ from classiq.qmod.quantum_function import GenerativeQFunc
32
37
 
33
38
 
34
39
  def get_main_renamer(
35
- functions: Sequence[NativeFunctionDefinition],
40
+ func_decls: Sequence[QuantumFunctionDeclaration],
36
41
  ) -> ExpressionRenamer:
37
- for func in functions:
38
- if func.name == MAIN_FUNCTION_NAME:
42
+ for func_decl in func_decls:
43
+ if func_decl.name == MAIN_FUNCTION_NAME:
44
+ if TYPE_CHECKING:
45
+ assert isinstance(func_decl, NamedParamsQuantumFunctionDeclaration)
39
46
  return ExpressionRenamer.from_positional_arg_declarations(
40
- func.positional_arg_declarations, CPARAM_EXECUTION_SUFFIX
47
+ func_decl.positional_arg_declarations, CPARAM_EXECUTION_SUFFIX
41
48
  )
42
49
  return ExpressionRenamer(var_mapping={})
43
50
 
@@ -49,32 +56,34 @@ def add_constants_to_scope(constants: list[Constant], scope: Scope) -> None:
49
56
  )
50
57
 
51
58
 
52
- def _add_functions_to_scope(
53
- functions: list[NativeFunctionDefinition], scope: Scope
59
+ def add_functions_to_scope(
60
+ functions: Sequence[NativeFunctionDefinition], scope: Scope
54
61
  ) -> None:
55
62
  for function in functions:
56
- scope[function.name] = Evaluated(
57
- value=FunctionClosure.create(
58
- name=function.name,
59
- positional_arg_declarations=function.positional_arg_declarations,
60
- body=function.body,
61
- scope=Scope(parent=scope),
63
+ if function.name not in scope:
64
+ scope[function.name] = Evaluated(
65
+ value=FunctionClosure.create(
66
+ name=function.name,
67
+ positional_arg_declarations=function.positional_arg_declarations,
68
+ body=function.body,
69
+ scope=Scope(parent=scope),
70
+ )
62
71
  )
63
- )
64
72
 
65
73
 
66
- def _add_generative_functions_to_scope(
67
- functions: list[GenerativeQFunc], scope: Scope
74
+ def add_generative_functions_to_scope(
75
+ functions: Sequence[GenerativeQFunc], scope: Scope
68
76
  ) -> None:
69
77
  for function in functions:
70
- scope[function.func_decl.name] = Evaluated(
71
- value=GenerativeFunctionClosure.create(
72
- name=function.func_decl.name,
73
- positional_arg_declarations=function.func_decl.positional_arg_declarations,
74
- scope=Scope(parent=scope),
75
- generative_blocks={"body": function},
78
+ if function.func_decl.name not in scope:
79
+ scope[function.func_decl.name] = Evaluated(
80
+ value=GenerativeFunctionClosure.create(
81
+ name=function.func_decl.name,
82
+ positional_arg_declarations=function.func_decl.positional_arg_declarations,
83
+ scope=Scope(parent=scope),
84
+ generative_blocks={"body": function},
85
+ )
76
86
  )
77
- )
78
87
 
79
88
 
80
89
  def _init_builtins_scope(scope: Scope) -> None:
@@ -129,7 +138,7 @@ def add_entry_point_params_to_scope(
129
138
  def init_top_level_scope(
130
139
  model: Model, generative_functions: list[GenerativeQFunc], scope: Scope
131
140
  ) -> None:
132
- _add_functions_to_scope(model.functions, scope)
133
- _add_generative_functions_to_scope(generative_functions, scope)
141
+ add_generative_functions_to_scope(generative_functions, scope)
142
+ add_functions_to_scope(model.functions, scope)
134
143
  add_constants_to_scope(model.constants, scope)
135
144
  _init_builtins_scope(scope)
@@ -1,7 +1,7 @@
1
1
  import ast
2
2
  from dataclasses import dataclass
3
3
  from itertools import chain
4
- from typing import TYPE_CHECKING, Callable, TypeVar, cast
4
+ from typing import TYPE_CHECKING, Callable, Optional, TypeVar, cast
5
5
 
6
6
  from classiq.interface.exceptions import (
7
7
  ClassiqExpansionError,
@@ -14,6 +14,7 @@ from classiq.interface.model.handle_binding import (
14
14
  HandleBinding,
15
15
  NestedHandleBinding,
16
16
  SlicedHandleBinding,
17
+ SubscriptHandleBinding,
17
18
  )
18
19
  from classiq.interface.model.quantum_expressions.quantum_expression import (
19
20
  QuantumExpressionOperation,
@@ -125,12 +126,7 @@ class VarSplitter:
125
126
  raise ClassiqExpansionError(
126
127
  f"Could not determine the length of quantum array " f"{symbol.handle}."
127
128
  )
128
- target_slices = {
129
- target_part.start.to_int_value(): target_part.end.to_int_value()
130
- for target_part in target_parts
131
- if isinstance(target_part, SlicedHandleBinding)
132
- and symbol.handle == target_part.base_handle
133
- }
129
+ target_slices = self._get_target_slices(symbol, target_parts)
134
130
 
135
131
  symbol_parts: list[QuantumSymbol] = []
136
132
  idx = 0
@@ -151,6 +147,60 @@ class VarSplitter:
151
147
 
152
148
  return symbol_parts
153
149
 
150
+ def _get_target_slices(
151
+ self, symbol: QuantumSymbol, target_parts: list[HandleBinding]
152
+ ) -> dict[int, int]:
153
+ if TYPE_CHECKING:
154
+ assert isinstance(symbol.quantum_type, QuantumBitvector)
155
+ target_items = {
156
+ idx
157
+ for idx in range(symbol.quantum_type.length_value)
158
+ for target_part in target_parts
159
+ if target_part
160
+ in SubscriptHandleBinding(
161
+ base_handle=symbol.handle, index=Expression(expr=str(idx))
162
+ )
163
+ }
164
+ target_slices = {
165
+ target_part.start.to_int_value(): target_part.end.to_int_value()
166
+ for target_part in target_parts
167
+ if isinstance(target_part, SlicedHandleBinding)
168
+ and symbol.handle == target_part.base_handle
169
+ }
170
+ self._add_unused_indices_as_slices(symbol, target_items, target_slices)
171
+ return target_slices
172
+
173
+ @staticmethod
174
+ def _add_unused_indices_as_slices(
175
+ symbol: QuantumSymbol, target_items: set[int], target_slices: dict[int, int]
176
+ ) -> None:
177
+ if TYPE_CHECKING:
178
+ assert isinstance(symbol.quantum_type, QuantumBitvector)
179
+ last_unused_idx: Optional[int] = None
180
+ array_length = symbol.quantum_type.length_value
181
+ idx = 0
182
+
183
+ while idx < array_length:
184
+ if (
185
+ idx in target_items or idx in target_slices
186
+ ) and last_unused_idx is not None:
187
+ target_slices[last_unused_idx] = idx
188
+ last_unused_idx = None
189
+
190
+ if idx in target_slices:
191
+ if target_slices[idx] <= idx:
192
+ raise ClassiqInternalExpansionError
193
+ idx = target_slices[idx]
194
+ continue
195
+
196
+ if idx not in target_items and last_unused_idx is None:
197
+ last_unused_idx = idx
198
+
199
+ idx += 1
200
+
201
+ if last_unused_idx is not None:
202
+ target_slices[last_unused_idx] = array_length
203
+
154
204
  def _get_struct_parts(
155
205
  self, symbol: QuantumSymbol, target_parts: list[HandleBinding]
156
206
  ) -> list[QuantumSymbol]:
@@ -0,0 +1,4 @@
1
+ from .functions import * # noqa: F403
2
+ from .functions import __all__ as _functions_all
3
+
4
+ __all__ = _functions_all