classiq 0.61.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 (83) 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 +13 -2
  7. classiq/applications/combinatorial_helpers/pyomo_utils.py +143 -13
  8. classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +1 -1
  9. classiq/applications/combinatorial_optimization/combinatorial_problem.py +58 -23
  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 +12 -10
  14. classiq/interface/_version.py +1 -1
  15. classiq/interface/backend/backend_preferences.py +26 -5
  16. classiq/interface/backend/pydantic_backend.py +1 -1
  17. classiq/interface/backend/quantum_backend_providers.py +3 -1
  18. classiq/interface/chemistry/operator.py +0 -204
  19. classiq/interface/execution/primitives.py +1 -0
  20. classiq/interface/generator/compiler_keywords.py +4 -0
  21. classiq/interface/generator/copy.py +47 -0
  22. classiq/interface/generator/function_param_list_without_self_reference.py +2 -0
  23. classiq/interface/generator/functions/type_name.py +6 -0
  24. classiq/interface/generator/generated_circuit_data.py +22 -7
  25. classiq/interface/generator/model/model.py +3 -0
  26. classiq/interface/generator/model/preferences/preferences.py +14 -1
  27. classiq/interface/generator/quantum_function_call.py +4 -2
  28. classiq/interface/generator/types/compilation_metadata.py +2 -1
  29. classiq/interface/model/handle_binding.py +50 -5
  30. classiq/interface/model/quantum_type.py +16 -0
  31. classiq/interface/server/routes.py +1 -3
  32. classiq/model_expansions/capturing/captured_vars.py +114 -28
  33. classiq/model_expansions/closure.py +25 -65
  34. classiq/model_expansions/function_builder.py +19 -9
  35. classiq/model_expansions/generative_functions.py +16 -2
  36. classiq/model_expansions/interpreter.py +110 -66
  37. classiq/model_expansions/model_tables.py +4 -0
  38. classiq/model_expansions/quantum_operations/call_emitter.py +83 -20
  39. classiq/model_expansions/quantum_operations/classicalif.py +1 -1
  40. classiq/model_expansions/quantum_operations/control.py +3 -10
  41. classiq/model_expansions/quantum_operations/emitter.py +3 -4
  42. classiq/model_expansions/quantum_operations/quantum_assignment_operation.py +1 -2
  43. classiq/model_expansions/quantum_operations/quantum_function_call.py +1 -1
  44. classiq/model_expansions/quantum_operations/repeat.py +4 -3
  45. classiq/model_expansions/quantum_operations/shallow_emitter.py +9 -3
  46. classiq/model_expansions/scope.py +9 -13
  47. classiq/model_expansions/scope_initialization.py +34 -25
  48. classiq/model_expansions/transformers/var_splitter.py +57 -7
  49. classiq/open_library/__init__.py +4 -0
  50. classiq/open_library/functions/__init__.py +130 -0
  51. classiq/{qmod/builtins → open_library}/functions/amplitude_estimation.py +2 -2
  52. classiq/{qmod/builtins → open_library}/functions/discrete_sine_cosine_transform.py +6 -4
  53. classiq/{qmod/builtins → open_library}/functions/grover.py +2 -2
  54. classiq/{qmod/builtins → open_library}/functions/linear_pauli_rotation.py +1 -1
  55. classiq/{qmod/builtins → open_library}/functions/modular_exponentiation.py +2 -2
  56. classiq/{qmod/builtins → open_library}/functions/qpe.py +2 -2
  57. classiq/{qmod/builtins → open_library}/functions/state_preparation.py +6 -149
  58. classiq/{qmod/builtins → open_library}/functions/swap_test.py +1 -1
  59. classiq/open_library/functions/utility_functions.py +81 -0
  60. classiq/{qmod/builtins → open_library}/functions/variational.py +1 -1
  61. classiq/qmod/builtins/functions/__init__.py +4 -130
  62. classiq/qmod/builtins/functions/allocation.py +150 -0
  63. classiq/qmod/builtins/functions/arithmetic.py +0 -34
  64. classiq/qmod/builtins/functions/operators.py +0 -6
  65. classiq/qmod/builtins/operations.py +19 -80
  66. classiq/qmod/create_model_function.py +8 -162
  67. classiq/qmod/generative.py +0 -16
  68. classiq/qmod/model_state_container.py +7 -0
  69. classiq/qmod/native/pretty_printer.py +10 -11
  70. classiq/qmod/pretty_print/pretty_printer.py +1 -1
  71. classiq/qmod/python_classical_type.py +1 -5
  72. classiq/qmod/qfunc.py +11 -12
  73. classiq/qmod/qmod_variable.py +1 -3
  74. classiq/qmod/quantum_expandable.py +23 -1
  75. classiq/qmod/quantum_function.py +69 -7
  76. {classiq-0.61.0.dist-info → classiq-0.63.0.dist-info}/METADATA +2 -1
  77. {classiq-0.61.0.dist-info → classiq-0.63.0.dist-info}/RECORD +82 -78
  78. classiq/qmod/builtins/functions/utility_functions.py +0 -43
  79. /classiq/{qmod/builtins → open_library}/functions/hea.py +0 -0
  80. /classiq/{qmod/builtins → open_library}/functions/qaoa_penalty.py +0 -0
  81. /classiq/{qmod/builtins → open_library}/functions/qft_functions.py +0 -0
  82. /classiq/{qmod/builtins → open_library}/functions/qsvt.py +0 -0
  83. {classiq-0.61.0.dist-info → classiq-0.63.0.dist-info}/WHEEL +0 -0
@@ -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
@@ -53,7 +61,7 @@ if TYPE_CHECKING:
53
61
  class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSplitter):
54
62
  def __init__(self, interpreter: "Interpreter") -> None:
55
63
  Emitter.__init__(self, interpreter)
56
- VarSplitter.__init__(self, interpreter._current_scope)
64
+ VarSplitter.__init__(self, interpreter._builder.current_scope)
57
65
 
58
66
  @staticmethod
59
67
  def _should_wrap(body: Sequence[QuantumStatement]) -> bool:
@@ -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,
@@ -90,14 +98,21 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
90
98
  args: list[ArgValue],
91
99
  propagated_debug_info: Optional[FunctionDebugInfo] = None,
92
100
  ) -> QuantumFunctionCall:
101
+ function = function.clone()
93
102
  function = function.set_depth(self._builder.current_function.depth + 1)
94
- function = function.copy_scope()
95
103
  evaluated_args = [self._interpreter.evaluate(arg) for arg in args]
96
104
  new_declaration = self._prepare_fully_typed_declaration(
97
105
  function, evaluated_args
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(
@@ -115,15 +130,20 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
115
130
  self._top_level_scope[function_def.name] = Evaluated(
116
131
  value=function_context.closure.with_new_declaration(function_def)
117
132
  )
133
+ compilation_metadata = self._functions_compilation_metadata.get(
134
+ function.name
135
+ )
136
+ if compilation_metadata is not None:
137
+ self._expanded_functions_compilation_metadata[function_def.name] = (
138
+ compilation_metadata
139
+ )
140
+ else:
141
+ self._expanded_functions_compilation_metadata[
142
+ function_def.name
143
+ ].occurrences_number += 1
144
+
118
145
  new_declaration = function_def
119
146
  new_function_name = function_def.name
120
- compilation_metadata = self._functions_compilation_metadata.get(
121
- function.name
122
- )
123
- if compilation_metadata is not None:
124
- self._expanded_functions_compilation_metadata[new_function_name] = (
125
- compilation_metadata
126
- )
127
147
 
128
148
  new_positional_args = self._get_new_positional_args(
129
149
  evaluated_args, is_atomic, new_positional_arg_decls
@@ -239,13 +259,56 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
239
259
  different from the call scope. For example, the former uses r,s and the latter
240
260
  uses p, q.
241
261
  """
242
- with self._scope_guard(Scope(parent=self._current_scope)):
243
- # The signature scope is passed as a separate argument to avoid contaminating the statement execution scope
244
- return NamedParamsQuantumFunctionDeclaration(
245
- name=function.name,
246
- positional_arg_declarations=evaluate_parameter_types_from_args(
247
- function,
248
- function.signature_scope,
249
- evaluated_args,
250
- ),
262
+ # The signature scope is passed as a separate argument to avoid contaminating the statement execution scope
263
+ return NamedParamsQuantumFunctionDeclaration(
264
+ name=function.name,
265
+ positional_arg_declarations=evaluate_parameter_types_from_args(
266
+ function,
267
+ function.signature_scope,
268
+ evaluated_args,
269
+ ),
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
+ )
251
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
 
@@ -49,13 +49,12 @@ class Emitter(Generic[QuantumStatementT]):
49
49
  def __init__(self, interpreter: "Interpreter") -> None:
50
50
  self._interpreter = interpreter
51
51
 
52
- self._scope_guard = self._interpreter._scope_guard
53
52
  self._machine_precision = self._interpreter._model.preferences.machine_precision
54
53
  self._expanded_functions_compilation_metadata = (
55
54
  self._interpreter._expanded_functions_compilation_metadata
56
55
  )
57
56
  self._functions_compilation_metadata = (
58
- self._interpreter._model.functions_compilation_metadata
57
+ self._interpreter._functions_compilation_metadata
59
58
  )
60
59
 
61
60
  @abstractmethod
@@ -71,7 +70,7 @@ class Emitter(Generic[QuantumStatementT]):
71
70
 
72
71
  @property
73
72
  def _current_scope(self) -> Scope:
74
- return self._interpreter._current_scope
73
+ return self._builder.current_scope
75
74
 
76
75
  @property
77
76
  def _top_level_scope(self) -> Scope:
@@ -104,7 +103,7 @@ class Emitter(Generic[QuantumStatementT]):
104
103
  )
105
104
  gen_closure = GenerativeClosure(
106
105
  name=func_decl.name,
107
- scope=Scope(parent=self._interpreter._current_scope),
106
+ scope=Scope(parent=self._interpreter._builder.current_scope),
108
107
  blocks={},
109
108
  generative_blocks={
110
109
  block_name: GenerativeQFunc(
@@ -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,
@@ -18,7 +18,7 @@ class QuantumFunctionCallEmitter(CallEmitter[QuantumFunctionCall]):
18
18
  def emit(self, call: QuantumFunctionCall, /) -> None:
19
19
  function = self._interpreter.evaluate(call.function).as_type(FunctionClosure)
20
20
  args = call.positional_args
21
- with ErrorManager().call(function.name), function.freeze():
21
+ with ErrorManager().call(function.name):
22
22
  self._emit_quantum_function_call(
23
23
  function, args, self._debug_info.get(call.uuid)
24
24
  )
@@ -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()
@@ -11,6 +11,9 @@ from classiq.interface.model.quantum_expressions.arithmetic_operation import (
11
11
  ArithmeticOperation,
12
12
  ArithmeticOperationKind,
13
13
  )
14
+ from classiq.interface.model.quantum_expressions.quantum_expression import (
15
+ QuantumAssignmentOperation,
16
+ )
14
17
  from classiq.interface.model.quantum_statement import QuantumOperation, QuantumStatement
15
18
 
16
19
  from classiq.model_expansions.closure import Closure
@@ -68,12 +71,15 @@ class ShallowEmitter(Emitter[QuantumOperation]):
68
71
  ).value.handle
69
72
 
70
73
  op = op.model_copy(update=expanded_components)
71
- if isinstance(op, ArithmeticOperation):
74
+ if isinstance(op, QuantumAssignmentOperation):
72
75
  self._post_process_assignment(op)
73
76
  self._builder.emit_statement(op)
74
77
 
75
- def _post_process_assignment(self, op: ArithmeticOperation) -> None:
76
- if op.operation_kind == ArithmeticOperationKind.Assignment:
78
+ def _post_process_assignment(self, op: QuantumAssignmentOperation) -> None:
79
+ if (
80
+ isinstance(op, ArithmeticOperation)
81
+ and op.operation_kind == ArithmeticOperationKind.Assignment
82
+ ):
77
83
  direction = PortDeclarationDirection.Output
78
84
  self._update_result_type(op)
79
85
  else:
@@ -2,7 +2,6 @@ import itertools
2
2
  import re
3
3
  from collections import UserDict
4
4
  from collections.abc import Iterator
5
- from contextlib import contextmanager
6
5
  from dataclasses import dataclass
7
6
  from functools import singledispatch
8
7
  from typing import TYPE_CHECKING, Any, Optional, TypeVar, Union
@@ -164,8 +163,14 @@ class Evaluated: # FIXME: Merge with EvaluatedExpression if possible
164
163
  return int(self.value)
165
164
 
166
165
  def emit(self) -> ArgValue:
167
- if isinstance(self.value, QuantumSymbol):
166
+ from classiq.model_expansions.closure import FunctionClosure
167
+
168
+ if isinstance(self.value, (QuantumSymbol, FunctionClosure)):
168
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]
169
174
 
170
175
  ret = Expression(expr=evaluated_to_str(self.value))
171
176
  ret._evaluated_expr = EvaluatedExpression(value=self.value)
@@ -231,14 +236,5 @@ class Scope(EvaluatedUserDict):
231
236
  parent=parent,
232
237
  )
233
238
 
234
- def _copy(self) -> "Scope":
235
- return Scope(
236
- self.data, parent=None if self._parent is None else self._parent.copy()
237
- )
238
-
239
- @contextmanager
240
- def freeze(self) -> Iterator[None]:
241
- previous = self._copy()
242
- yield
243
- self.data = previous.data
244
- self._parent = previous._parent
239
+ def clone(self) -> "Scope":
240
+ return Scope(self.data, parent=self._parent)
@@ -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