classiq 0.71.0__py3-none-any.whl → 0.72.1__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 (54) hide show
  1. classiq/__init__.py +0 -6
  2. classiq/_internals/client.py +11 -1
  3. classiq/applications/chemistry/chemistry_model_constructor.py +18 -16
  4. classiq/applications/combinatorial_helpers/optimization_model.py +9 -2
  5. classiq/applications/combinatorial_helpers/pyomo_utils.py +6 -1
  6. classiq/applications/finance/__init__.py +0 -3
  7. classiq/applications/qsvm/__init__.py +0 -2
  8. classiq/interface/_version.py +1 -1
  9. classiq/interface/backend/backend_preferences.py +22 -0
  10. classiq/interface/backend/quantum_backend_providers.py +2 -0
  11. classiq/interface/generator/expressions/expression_types.py +4 -1
  12. classiq/interface/generator/expressions/proxies/classical/any_classical_value.py +135 -0
  13. classiq/interface/generator/functions/builtins/internal_operators.py +1 -0
  14. classiq/interface/generator/generated_circuit_data.py +13 -0
  15. classiq/interface/generator/hardware/hardware_data.py +3 -1
  16. classiq/interface/generator/transpiler_basis_gates.py +3 -1
  17. classiq/interface/hardware.py +1 -0
  18. classiq/interface/model/handle_binding.py +21 -0
  19. classiq/interface/server/routes.py +0 -2
  20. classiq/model_expansions/atomic_expression_functions_defs.py +35 -13
  21. classiq/model_expansions/closure.py +0 -9
  22. classiq/model_expansions/evaluators/parameter_types.py +6 -10
  23. classiq/model_expansions/interpreters/base_interpreter.py +4 -4
  24. classiq/model_expansions/interpreters/generative_interpreter.py +33 -5
  25. classiq/model_expansions/quantum_operations/__init__.py +0 -2
  26. classiq/model_expansions/quantum_operations/block_evaluator.py +16 -2
  27. classiq/model_expansions/quantum_operations/call_emitter.py +4 -1
  28. classiq/model_expansions/quantum_operations/emitter.py +26 -9
  29. classiq/model_expansions/quantum_operations/handle_evaluator.py +1 -1
  30. classiq/model_expansions/quantum_operations/quantum_function_call.py +49 -0
  31. classiq/model_expansions/quantum_operations/repeat_block_evaluator.py +34 -0
  32. classiq/model_expansions/scope.py +34 -14
  33. classiq/model_expansions/scope_initialization.py +8 -4
  34. classiq/model_expansions/transformers/model_renamer.py +69 -63
  35. classiq/model_expansions/utils/sympy_utils.py +24 -0
  36. classiq/model_expansions/visitors/variable_references.py +1 -0
  37. classiq/qmod/builtins/functions/__init__.py +8 -0
  38. classiq/qmod/builtins/functions/allocation.py +36 -0
  39. classiq/qmod/builtins/functions/arithmetic.py +10 -5
  40. classiq/qmod/builtins/functions/mid_circuit_measurement.py +3 -0
  41. classiq/qmod/builtins/operations.py +2 -2
  42. classiq/qmod/model_state_container.py +9 -0
  43. classiq/qmod/symbolic.py +16 -3
  44. {classiq-0.71.0.dist-info → classiq-0.72.1.dist-info}/METADATA +1 -1
  45. {classiq-0.71.0.dist-info → classiq-0.72.1.dist-info}/RECORD +46 -51
  46. classiq/applications/finance/finance_model_constructor.py +0 -137
  47. classiq/applications/grover/__init__.py +0 -9
  48. classiq/applications/grover/grover_model_constructor.py +0 -167
  49. classiq/applications/libraries/__init__.py +0 -0
  50. classiq/applications/libraries/qmci_library.py +0 -22
  51. classiq/applications/qsvm/qsvm_model_constructor.py +0 -131
  52. classiq/model_expansions/quantum_operations/classicalif.py +0 -57
  53. classiq/model_expansions/quantum_operations/repeat.py +0 -62
  54. {classiq-0.71.0.dist-info → classiq-0.72.1.dist-info}/WHEEL +0 -0
@@ -79,11 +79,6 @@ def _update_scope(
79
79
  if parameter.direction is PortDeclarationDirection.Output:
80
80
  return
81
81
  quantum_symbol = argument.as_type(QuantumSymbol)
82
- if not quantum_symbol.quantum_type.has_size_in_bits:
83
- raise ClassiqExpansionError(
84
- f"Argument {str(quantum_symbol.emit())!r} to function {closure.name!r} "
85
- f"is not initialized"
86
- )
87
82
  casted_argument = _cast(
88
83
  parameter.quantum_type,
89
84
  quantum_symbol,
@@ -274,9 +269,10 @@ def evaluate_types_in_quantum_symbols(
274
269
  def _inject_quantum_arg_info_to_type(
275
270
  parameter_type: QuantumType, quantum_symbol: QuantumSymbol, param_name: str
276
271
  ) -> QuantumType:
277
- copy_type_information(
278
- quantum_symbol.quantum_type,
279
- parameter_type,
280
- param_name,
281
- )
272
+ if quantum_symbol.quantum_type.has_size_in_bits:
273
+ copy_type_information(
274
+ quantum_symbol.quantum_type,
275
+ parameter_type,
276
+ param_name,
277
+ )
282
278
  return parameter_type
@@ -158,7 +158,7 @@ class BaseInterpreter:
158
158
  expr = evaluate_classical_expression(expression, self._builder.current_scope)
159
159
  if not isinstance(expr.value, sympy.Basic):
160
160
  return expr
161
- vrc = VarRefCollector(ignore_duplicated_handles=True)
161
+ vrc = VarRefCollector(ignore_duplicated_handles=True, unevaluated=True)
162
162
  vrc.visit(ast.parse(str(expr.value)))
163
163
  for handle in vrc.var_handles:
164
164
  if handle.name in self._builder.current_scope and isinstance(
@@ -189,8 +189,8 @@ class BaseInterpreter:
189
189
  quantum_variable = self.evaluate(sliced_handle_binding.base_handle).as_type(
190
190
  QuantumSymbol
191
191
  )
192
- start = self.evaluate(sliced_handle_binding.start).as_type(int)
193
- end = self.evaluate(sliced_handle_binding.end).as_type(int)
192
+ start = self.evaluate(sliced_handle_binding.start).value
193
+ end = self.evaluate(sliced_handle_binding.end).value
194
194
  return Evaluated(value=quantum_variable[start:end])
195
195
 
196
196
  @evaluate.register
@@ -200,7 +200,7 @@ class BaseInterpreter:
200
200
  @evaluate.register
201
201
  def evaluate_subscript_handle(self, subscript: SubscriptHandleBinding) -> Evaluated:
202
202
  base_value = self.evaluate(subscript.base_handle)
203
- index_value = self.evaluate(subscript.index).as_type(int)
203
+ index_value = self.evaluate(subscript.index).value
204
204
  return Evaluated(value=base_value.value[index_value])
205
205
 
206
206
  @evaluate.register
@@ -7,8 +7,10 @@ from numpy.random import permutation
7
7
  from classiq.interface.generator.constant import Constant
8
8
  from classiq.interface.generator.expressions.expression import Expression
9
9
  from classiq.interface.generator.functions.builtins.internal_operators import (
10
+ CLASSICAL_IF_OPERATOR_NAME,
10
11
  CONTROL_OPERATOR_NAME,
11
12
  INVERT_OPERATOR_NAME,
13
+ REPEAT_OPERATOR_NAME,
12
14
  WITHIN_APPLY_NAME,
13
15
  )
14
16
  from classiq.interface.model.allocate import Allocate
@@ -52,9 +54,7 @@ from classiq.model_expansions.generative_functions import emit_generative_statem
52
54
  from classiq.model_expansions.interpreters.base_interpreter import BaseInterpreter
53
55
  from classiq.model_expansions.quantum_operations import (
54
56
  BindEmitter,
55
- ClassicalIfEmitter,
56
57
  QuantumFunctionCallEmitter,
57
- RepeatEmitter,
58
58
  VariableDeclarationStatementEmitter,
59
59
  )
60
60
  from classiq.model_expansions.quantum_operations.allocate import AllocateEmitter
@@ -69,6 +69,9 @@ from classiq.model_expansions.quantum_operations.expression_evaluator import (
69
69
  ExpressionEvaluator,
70
70
  )
71
71
  from classiq.model_expansions.quantum_operations.handle_evaluator import HandleEvaluator
72
+ from classiq.model_expansions.quantum_operations.repeat_block_evaluator import (
73
+ RepeatBlockEvaluator,
74
+ )
72
75
  from classiq.model_expansions.scope import Evaluated, Scope
73
76
  from classiq.model_expansions.scope_initialization import (
74
77
  add_constants_to_scope,
@@ -87,7 +90,9 @@ class GenerativeInterpreter(BaseInterpreter):
87
90
  generative_functions: list[GenerativeQFunc],
88
91
  ) -> None:
89
92
  super().__init__(model)
90
- add_generative_functions_to_scope(generative_functions, self._top_level_scope)
93
+ add_generative_functions_to_scope(
94
+ generative_functions, self._top_level_scope, override_atomic=True
95
+ )
91
96
 
92
97
  def evaluate_lambda(self, function: QuantumLambdaFunction) -> Evaluated:
93
98
  renamed_params = [
@@ -189,8 +194,22 @@ class GenerativeInterpreter(BaseInterpreter):
189
194
  VariableDeclarationStatementEmitter(self).emit(variable_declaration)
190
195
 
191
196
  @emit.register
197
+ def _emit_classical_if(self, classical_if: ClassicalIf) -> None:
198
+ self.emit_classical_if(classical_if)
199
+
192
200
  def emit_classical_if(self, classical_if: ClassicalIf) -> None:
193
- ClassicalIfEmitter(self).emit(classical_if)
201
+ CompositeEmitter[ClassicalIf](
202
+ self,
203
+ [
204
+ ExpressionEvaluator(self, "condition"),
205
+ BlockEvaluator(
206
+ self,
207
+ CLASSICAL_IF_OPERATOR_NAME,
208
+ "then",
209
+ "else_",
210
+ ),
211
+ ],
212
+ ).emit(classical_if)
194
213
 
195
214
  @emit.register
196
215
  def emit_within_apply(self, within_apply: WithinApply) -> None:
@@ -208,8 +227,17 @@ class GenerativeInterpreter(BaseInterpreter):
208
227
  BlockEvaluator(self, INVERT_OPERATOR_NAME, "body").emit(invert)
209
228
 
210
229
  @emit.register
230
+ def _emit_repeat(self, repeat: Repeat) -> None:
231
+ self.emit_repeat(repeat)
232
+
211
233
  def emit_repeat(self, repeat: Repeat) -> None:
212
- RepeatEmitter(self).emit(repeat)
234
+ CompositeEmitter[Repeat](
235
+ self,
236
+ [
237
+ ExpressionEvaluator(self, "count"),
238
+ RepeatBlockEvaluator(self, REPEAT_OPERATOR_NAME, "body"),
239
+ ],
240
+ ).emit(repeat)
213
241
 
214
242
  @emit.register
215
243
  def _emit_control(self, control: Control) -> None:
@@ -1,9 +1,7 @@
1
1
  from classiq.model_expansions.quantum_operations.bind import BindEmitter
2
- from classiq.model_expansions.quantum_operations.classicalif import ClassicalIfEmitter
3
2
  from classiq.model_expansions.quantum_operations.quantum_function_call import (
4
3
  QuantumFunctionCallEmitter,
5
4
  )
6
- from classiq.model_expansions.quantum_operations.repeat import RepeatEmitter
7
5
  from classiq.model_expansions.quantum_operations.variable_decleration import (
8
6
  VariableDeclarationStatementEmitter,
9
7
  )
@@ -1,6 +1,7 @@
1
1
  from collections.abc import Sequence
2
2
  from typing import TYPE_CHECKING
3
3
 
4
+ from classiq.interface.model.quantum_function_declaration import PositionalArg
4
5
  from classiq.interface.model.quantum_statement import QuantumOperation, QuantumStatement
5
6
 
6
7
  from classiq.model_expansions.closure import Closure
@@ -54,7 +55,8 @@ class BlockEvaluator(Emitter[QuantumOperation]):
54
55
  }
55
56
  block_closure = Closure(
56
57
  name=self._operation_name,
57
- scope=Scope(parent=self._current_scope),
58
+ scope=self.get_scope(op),
59
+ positional_arg_declarations=self.get_params(op),
58
60
  blocks=blocks,
59
61
  )
60
62
  context = self._expand_operation(block_closure)
@@ -69,8 +71,20 @@ class BlockEvaluator(Emitter[QuantumOperation]):
69
71
  blocks = [
70
72
  block for block in self._block_names if op.has_generative_block(block)
71
73
  ]
72
- context = self._expand_generative_context(op, self._operation_name, blocks)
74
+ context = self._expand_generative_context(
75
+ op,
76
+ self._operation_name,
77
+ blocks,
78
+ self.get_params(op),
79
+ self.get_scope(op),
80
+ )
73
81
  return {
74
82
  _REVERSE_BLOCK_RENAMES.get(block, block): context.statements(block)
75
83
  for block in blocks
76
84
  }
85
+
86
+ def get_params(self, op: QuantumOperation) -> Sequence[PositionalArg]:
87
+ return []
88
+
89
+ def get_scope(self, op: QuantumOperation) -> Scope:
90
+ return Scope(parent=self._current_scope)
@@ -12,6 +12,9 @@ import sympy
12
12
 
13
13
  from classiq.interface.debug_info.debug_info import FunctionDebugInfo
14
14
  from classiq.interface.exceptions import ClassiqExpansionError
15
+ from classiq.interface.generator.expressions.proxies.classical.any_classical_value import (
16
+ AnyClassicalValue,
17
+ )
15
18
  from classiq.interface.generator.expressions.proxies.classical.classical_proxy import (
16
19
  ClassicalProxy,
17
20
  )
@@ -94,7 +97,7 @@ def _is_symbolic(arg: Any) -> bool:
94
97
  return any(_is_symbolic(item) for item in arg.fields.values())
95
98
  if isinstance(arg, sympy.Basic):
96
99
  return len(arg.free_symbols) > 0
97
- return isinstance(arg, ClassicalProxy)
100
+ return isinstance(arg, (ClassicalProxy, AnyClassicalValue))
98
101
 
99
102
 
100
103
  class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSplitter):
@@ -1,5 +1,6 @@
1
1
  import ast
2
2
  from abc import ABC, abstractmethod
3
+ from collections.abc import Sequence
3
4
  from typing import (
4
5
  TYPE_CHECKING,
5
6
  Generic,
@@ -30,10 +31,11 @@ from classiq.interface.generator.functions.port_declaration import (
30
31
  PortDeclarationDirection,
31
32
  )
32
33
  from classiq.interface.helpers.pydantic_model_helpers import nameables_to_dict
33
- from classiq.interface.model.handle_binding import HandleBinding
34
+ from classiq.interface.model.handle_binding import HandleBinding, NestedHandleBinding
34
35
  from classiq.interface.model.native_function_definition import NativeFunctionDefinition
35
36
  from classiq.interface.model.quantum_function_declaration import (
36
37
  NamedParamsQuantumFunctionDeclaration,
38
+ PositionalArg,
37
39
  )
38
40
  from classiq.interface.model.quantum_statement import QuantumOperation, QuantumStatement
39
41
 
@@ -110,17 +112,20 @@ class Emitter(Generic[QuantumStatementT], ABC):
110
112
  op: QuantumOperation,
111
113
  context_name: str,
112
114
  block_names: Union[None, str, list[str]] = None,
113
- func_decl: Optional[NamedParamsQuantumFunctionDeclaration] = None,
115
+ params: Optional[Sequence[PositionalArg]] = None,
116
+ scope: Optional[Scope] = None,
114
117
  ) -> OperationContext:
115
118
  if isinstance(block_names, str):
116
119
  block_names = [block_names]
117
120
  block_names = block_names or ["body"]
118
- func_decl = func_decl or NamedParamsQuantumFunctionDeclaration(
119
- name=context_name
121
+ func_decl = NamedParamsQuantumFunctionDeclaration(
122
+ name=context_name,
123
+ positional_arg_declarations=[] if params is None else params,
120
124
  )
125
+ _scope = Scope(parent=self._current_scope) if scope is None else scope
121
126
  gen_closure = GenerativeClosure(
122
127
  name=func_decl.name,
123
- scope=Scope(parent=self._interpreter._builder.current_scope),
128
+ scope=_scope,
124
129
  blocks={},
125
130
  generative_blocks={
126
131
  block_name: GenerativeQFunc(
@@ -128,6 +133,7 @@ class Emitter(Generic[QuantumStatementT], ABC):
128
133
  )
129
134
  for block_name in block_names
130
135
  },
136
+ positional_arg_declarations=func_decl.positional_arg_declarations,
131
137
  )
132
138
  context = self._interpreter._expand_operation(gen_closure)
133
139
  op.clear_generative_blocks()
@@ -163,8 +169,11 @@ class Emitter(Generic[QuantumStatementT], ABC):
163
169
 
164
170
  def _update_captured_classical_vars(self, stmt: QuantumStatement) -> None:
165
171
  for expr in stmt.expressions:
166
- for var_name, var_type in self._get_classical_vars_in_expression(expr):
167
- self._capture_classical_var(var_name, var_type)
172
+ self._update_captured_classical_vars_in_expression(expr)
173
+
174
+ def _update_captured_classical_vars_in_expression(self, expr: Expression) -> None:
175
+ for var_name, var_type in self._get_classical_vars_in_expression(expr):
176
+ self._capture_classical_var(var_name, var_type)
168
177
 
169
178
  def _update_captured_vars(self, op: QuantumOperation) -> None:
170
179
  handles = (
@@ -180,6 +189,12 @@ class Emitter(Generic[QuantumStatementT], ABC):
180
189
  ) -> None:
181
190
  if handle.name not in self._current_scope:
182
191
  return
192
+ if not handle.is_constant():
193
+ self._update_captured_classical_vars_in_expression(
194
+ Expression(expr=str(handle))
195
+ )
196
+ while isinstance(handle, NestedHandleBinding) and not handle.is_constant():
197
+ handle = handle.base_handle
183
198
  defining_function = self._current_scope[handle.name].defining_function
184
199
  if defining_function is None:
185
200
  raise ClassiqInternalExpansionError
@@ -204,7 +219,7 @@ class Emitter(Generic[QuantumStatementT], ABC):
204
219
  )
205
220
 
206
221
  def _get_symbols_in_expression(self, expr: Expression) -> list[QuantumSymbol]:
207
- vrc = VarRefCollector(ignore_duplicated_handles=True)
222
+ vrc = VarRefCollector(ignore_duplicated_handles=True, unevaluated=True)
208
223
  vrc.visit(ast.parse(expr.expr))
209
224
  handles = dict.fromkeys(
210
225
  handle
@@ -216,7 +231,9 @@ class Emitter(Generic[QuantumStatementT], ABC):
216
231
  def _get_classical_vars_in_expression(
217
232
  self, expr: Expression
218
233
  ) -> list[tuple[str, ClassicalType]]:
219
- vrc = VarRefCollector(ignore_duplicated_handles=True, ignore_sympy_symbols=True)
234
+ vrc = VarRefCollector(
235
+ ignore_duplicated_handles=True, ignore_sympy_symbols=True, unevaluated=True
236
+ )
220
237
  vrc.visit(ast.parse(expr.expr))
221
238
  return list(
222
239
  {
@@ -18,7 +18,7 @@ class HandleEvaluator(Emitter[QuantumOperation]):
18
18
  handle = getattr(op, self._handle_name)
19
19
  if not isinstance(handle, HandleBinding):
20
20
  return False
21
- evaluated_handle = self._interpreter.evaluate(handle).value.handle
21
+ evaluated_handle = self._interpreter.evaluate(handle).value.handle.collapse()
22
22
  if handle == evaluated_handle:
23
23
  return False
24
24
  op = op.model_copy(
@@ -1,6 +1,14 @@
1
1
  from typing import TYPE_CHECKING
2
2
 
3
+ from classiq.interface.exceptions import ClassiqInternalExpansionError
4
+ from classiq.interface.generator.expressions.expression import Expression
5
+ from classiq.interface.generator.expressions.proxies.classical.classical_scalar_proxy import (
6
+ ClassicalScalarProxy,
7
+ )
8
+ from classiq.interface.model.classical_if import ClassicalIf
3
9
  from classiq.interface.model.quantum_function_call import QuantumFunctionCall
10
+ from classiq.interface.model.quantum_lambda_function import OperandIdentifier
11
+ from classiq.interface.model.quantum_statement import QuantumStatement
4
12
 
5
13
  from classiq.model_expansions.closure import FunctionClosure
6
14
  from classiq.model_expansions.quantum_operations.call_emitter import CallEmitter
@@ -19,6 +27,10 @@ class QuantumFunctionCallEmitter(CallEmitter[QuantumFunctionCall]):
19
27
  self._model = self._interpreter._model
20
28
 
21
29
  def emit(self, call: QuantumFunctionCall, /) -> bool:
30
+ if isinstance(call.function, OperandIdentifier):
31
+ index_val = self._interpreter.evaluate(call.function.index).value
32
+ if isinstance(index_val, ClassicalScalarProxy):
33
+ return self._emit_symbolic_lambda_list(call, index_val)
22
34
  function = self._interpreter.evaluate(call.function).as_type(FunctionClosure)
23
35
  args = call.positional_args
24
36
  with ErrorManager().call(function.name):
@@ -27,6 +39,43 @@ class QuantumFunctionCallEmitter(CallEmitter[QuantumFunctionCall]):
27
39
  )
28
40
  return True
29
41
 
42
+ def _emit_symbolic_lambda_list(
43
+ self, call: QuantumFunctionCall, index: ClassicalScalarProxy
44
+ ) -> bool:
45
+ if TYPE_CHECKING:
46
+ assert isinstance(call.function, OperandIdentifier)
47
+ funcs = self._interpreter.evaluate(call.function.name).value
48
+ if not isinstance(funcs, list):
49
+ raise ClassiqInternalExpansionError(
50
+ f"Unexpected lambda list type {type(funcs).__name__!r}"
51
+ )
52
+ self._interpreter.emit(self._create_recursive_if(call, index, len(funcs)))
53
+ return True
54
+
55
+ @staticmethod
56
+ def _create_recursive_if(
57
+ call: QuantumFunctionCall, index: ClassicalScalarProxy, num_funcs: int
58
+ ) -> QuantumStatement:
59
+ if TYPE_CHECKING:
60
+ assert isinstance(call.function, OperandIdentifier)
61
+ stmt: list[QuantumStatement] = []
62
+ for idx in reversed(range(num_funcs)):
63
+ stmt = [
64
+ ClassicalIf(
65
+ condition=Expression(expr=f"{index} == {idx}"),
66
+ then=[
67
+ QuantumFunctionCall(
68
+ function=OperandIdentifier(
69
+ name=call.function.name, index=Expression(expr=str(idx))
70
+ ),
71
+ positional_args=call.positional_args,
72
+ )
73
+ ],
74
+ else_=stmt,
75
+ )
76
+ ]
77
+ return stmt[0]
78
+
30
79
 
31
80
  class DeclarativeQuantumFunctionCallEmitter(
32
81
  QuantumFunctionCallEmitter, DeclarativeCallEmitter
@@ -0,0 +1,34 @@
1
+ from collections.abc import Sequence
2
+ from typing import TYPE_CHECKING
3
+
4
+ from classiq.interface.generator.expressions.proxies.classical.classical_scalar_proxy import (
5
+ ClassicalScalarProxy,
6
+ )
7
+ from classiq.interface.generator.functions.classical_type import Integer
8
+ from classiq.interface.model.handle_binding import HandleBinding
9
+ from classiq.interface.model.quantum_function_declaration import PositionalArg
10
+ from classiq.interface.model.quantum_statement import QuantumOperation
11
+ from classiq.interface.model.repeat import Repeat
12
+
13
+ from classiq import ClassicalParameterDeclaration
14
+ from classiq.model_expansions.quantum_operations.block_evaluator import BlockEvaluator
15
+ from classiq.model_expansions.scope import Evaluated, Scope
16
+
17
+
18
+ class RepeatBlockEvaluator(BlockEvaluator):
19
+ def get_params(self, op: QuantumOperation) -> Sequence[PositionalArg]:
20
+ if TYPE_CHECKING:
21
+ assert isinstance(op, Repeat)
22
+ return [
23
+ ClassicalParameterDeclaration(name=op.iter_var, classical_type=Integer())
24
+ ]
25
+
26
+ def get_scope(self, op: QuantumOperation) -> Scope:
27
+ if TYPE_CHECKING:
28
+ assert isinstance(op, Repeat)
29
+ scope = super().get_scope(op)
30
+ scope[op.iter_var] = Evaluated(
31
+ value=ClassicalScalarProxy(HandleBinding(name=op.iter_var), Integer()),
32
+ defining_function=self._builder.current_function,
33
+ )
34
+ return scope
@@ -13,6 +13,9 @@ from classiq.interface.generator.expressions.evaluated_expression import (
13
13
  EvaluatedExpression,
14
14
  )
15
15
  from classiq.interface.generator.expressions.expression import Expression
16
+ from classiq.interface.generator.expressions.proxies.classical.classical_scalar_proxy import (
17
+ ClassicalScalarProxy,
18
+ )
16
19
  from classiq.interface.generator.expressions.proxies.classical.qmod_struct_instance import (
17
20
  QmodStructInstance,
18
21
  )
@@ -47,28 +50,38 @@ class QuantumSymbol:
47
50
  def emit(self) -> HandleBinding:
48
51
  return self.handle
49
52
 
50
- def __getitem__(self, item: Union[slice, int]) -> "QuantumSymbol":
51
- if isinstance(item, int):
52
- return self._subscript(item)
53
-
54
- return self._slice(item.start, item.stop)
53
+ def __getitem__(
54
+ self, item: Union[slice, int, ClassicalScalarProxy]
55
+ ) -> "QuantumSymbol":
56
+ if isinstance(item, slice):
57
+ return self._slice(item.start, item.stop)
58
+ return self._subscript(item)
55
59
 
56
- def _slice(self, start: int, end: int) -> "QuantumSymbol":
60
+ def _slice(
61
+ self,
62
+ start: Union[int, ClassicalScalarProxy],
63
+ end: Union[int, ClassicalScalarProxy],
64
+ ) -> "QuantumSymbol":
57
65
  if not isinstance(self.quantum_type, QuantumBitvector):
58
66
  raise ClassiqExpansionError(
59
67
  f"{self.quantum_type.type_name} is not subscriptable"
60
68
  )
61
- if start >= end:
69
+ if TYPE_CHECKING:
70
+ assert self.quantum_type.length is not None
71
+ if isinstance(start, int) and isinstance(end, int) and start >= end:
62
72
  raise ClassiqExpansionError(
63
73
  f"{self.quantum_type.type_name} slice '{self.handle}[{start}:{end}]' "
64
74
  f"has non-positive length"
65
75
  )
66
- array_length = self.quantum_type.length_value
67
- if start < 0 or end > array_length:
76
+ if (isinstance(start, int) and start < 0) or (
77
+ isinstance(end, int)
78
+ and self.quantum_type.length.is_constant()
79
+ and end > self.quantum_type.length_value
80
+ ):
68
81
  raise ClassiqExpansionError(
69
82
  f"Slice [{start}:{end}] is out of bounds for "
70
83
  f"{self.quantum_type.type_name.lower()} {str(self.handle)!r} (of "
71
- f"length {array_length})"
84
+ f"length {self.quantum_type.length_value})"
72
85
  )
73
86
  return QuantumSymbol(
74
87
  handle=SlicedHandleBinding(
@@ -82,17 +95,24 @@ class QuantumSymbol:
82
95
  ),
83
96
  )
84
97
 
85
- def _subscript(self, index: int) -> "QuantumSymbol":
98
+ def _subscript(self, index: Union[int, ClassicalScalarProxy]) -> "QuantumSymbol":
86
99
  if not isinstance(self.quantum_type, QuantumBitvector):
87
100
  raise ClassiqExpansionError(
88
101
  f"{self.quantum_type.type_name} is not subscriptable"
89
102
  )
90
- array_length = self.quantum_type.length_value
91
- if index < 0 or index >= array_length:
103
+ if TYPE_CHECKING:
104
+ assert self.quantum_type.length is not None
105
+ if isinstance(index, int) and (
106
+ index < 0
107
+ or (
108
+ self.quantum_type.length.is_constant()
109
+ and index >= self.quantum_type.length_value
110
+ )
111
+ ):
92
112
  raise ClassiqExpansionError(
93
113
  f"Index {index} is out of bounds for "
94
114
  f"{self.quantum_type.type_name.lower()} {str(self.handle)!r} (of "
95
- f"length {array_length})"
115
+ f"length {self.quantum_type.length})"
96
116
  )
97
117
  return QuantumSymbol(
98
118
  handle=SubscriptHandleBinding(
@@ -58,14 +58,18 @@ def add_functions_to_scope(
58
58
 
59
59
 
60
60
  def add_generative_functions_to_scope(
61
- functions: Sequence[GenerativeQFunc], scope: Scope
61
+ functions: Sequence[GenerativeQFunc], scope: Scope, override_atomic: bool = False
62
62
  ) -> None:
63
63
  for function in functions:
64
64
  name = function.func_decl.name
65
- if name == MAIN_FUNCTION_NAME or name not in scope:
66
- scope[function.func_decl.name] = Evaluated(
65
+ if (
66
+ name == MAIN_FUNCTION_NAME
67
+ or name not in scope
68
+ or (override_atomic and scope[name].value.is_atomic)
69
+ ):
70
+ scope[name] = Evaluated(
67
71
  value=GenerativeFunctionClosure.create(
68
- name=function.func_decl.name,
72
+ name=name,
69
73
  positional_arg_declarations=function.func_decl.positional_arg_declarations,
70
74
  scope=Scope(parent=scope),
71
75
  generative_blocks={"body": function},