classiq 0.70.0__py3-none-any.whl → 0.72.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 (98) 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/debug_info/debug_info.py +4 -0
  12. classiq/interface/generator/expressions/expression_constants.py +0 -3
  13. classiq/interface/generator/expressions/expression_types.py +8 -3
  14. classiq/interface/generator/expressions/proxies/classical/any_classical_value.py +135 -0
  15. classiq/interface/generator/expressions/proxies/classical/classical_array_proxy.py +4 -0
  16. classiq/interface/generator/expressions/proxies/classical/classical_struct_proxy.py +5 -1
  17. classiq/interface/generator/expressions/proxies/classical/utils.py +34 -0
  18. classiq/interface/generator/functions/builtins/internal_operators.py +1 -0
  19. classiq/interface/generator/functions/classical_type.py +1 -1
  20. classiq/interface/generator/functions/type_name.py +16 -0
  21. classiq/interface/generator/functions/type_qualifier.py +7 -0
  22. classiq/interface/generator/generated_circuit_data.py +14 -1
  23. classiq/interface/generator/hardware/hardware_data.py +3 -1
  24. classiq/interface/generator/quantum_function_call.py +8 -1
  25. classiq/interface/generator/synthesis_execution_parameter.py +1 -0
  26. classiq/interface/generator/transpiler_basis_gates.py +3 -1
  27. classiq/interface/generator/types/compilation_metadata.py +1 -0
  28. classiq/interface/hardware.py +1 -0
  29. classiq/interface/ide/visual_model.py +1 -0
  30. classiq/interface/interface_version.py +1 -1
  31. classiq/interface/model/allocate.py +7 -0
  32. classiq/interface/model/block.py +12 -0
  33. classiq/interface/model/classical_if.py +4 -0
  34. classiq/interface/model/handle_binding.py +21 -0
  35. classiq/interface/model/inplace_binary_operation.py +4 -0
  36. classiq/interface/model/model.py +3 -1
  37. classiq/interface/model/phase_operation.py +4 -0
  38. classiq/interface/model/port_declaration.py +3 -0
  39. classiq/interface/model/power.py +4 -0
  40. classiq/interface/model/quantum_expressions/quantum_expression.py +4 -0
  41. classiq/interface/model/quantum_function_call.py +4 -0
  42. classiq/interface/model/quantum_function_declaration.py +1 -1
  43. classiq/interface/model/quantum_statement.py +5 -0
  44. classiq/interface/model/quantum_type.py +22 -0
  45. classiq/interface/model/repeat.py +4 -0
  46. classiq/interface/model/statement_block.py +3 -0
  47. classiq/interface/model/variable_declaration_statement.py +5 -0
  48. classiq/interface/server/routes.py +0 -2
  49. classiq/model_expansions/atomic_expression_functions_defs.py +35 -13
  50. classiq/model_expansions/capturing/captured_vars.py +156 -34
  51. classiq/model_expansions/closure.py +0 -9
  52. classiq/model_expansions/evaluators/classical_type_inference.py +70 -0
  53. classiq/model_expansions/evaluators/parameter_types.py +20 -10
  54. classiq/model_expansions/expression_evaluator.py +0 -11
  55. classiq/model_expansions/function_builder.py +2 -8
  56. classiq/model_expansions/generative_functions.py +7 -30
  57. classiq/model_expansions/interpreters/base_interpreter.py +7 -8
  58. classiq/model_expansions/interpreters/generative_interpreter.py +33 -5
  59. classiq/model_expansions/quantum_operations/__init__.py +0 -2
  60. classiq/model_expansions/quantum_operations/block_evaluator.py +16 -2
  61. classiq/model_expansions/quantum_operations/call_emitter.py +49 -6
  62. classiq/model_expansions/quantum_operations/emitter.py +64 -6
  63. classiq/model_expansions/quantum_operations/expression_evaluator.py +4 -0
  64. classiq/model_expansions/quantum_operations/handle_evaluator.py +1 -1
  65. classiq/model_expansions/quantum_operations/quantum_function_call.py +49 -0
  66. classiq/model_expansions/quantum_operations/repeat_block_evaluator.py +34 -0
  67. classiq/model_expansions/scope.py +33 -21
  68. classiq/model_expansions/scope_initialization.py +28 -32
  69. classiq/model_expansions/transformers/model_renamer.py +69 -63
  70. classiq/model_expansions/utils/sympy_utils.py +24 -0
  71. classiq/model_expansions/visitors/variable_references.py +1 -0
  72. classiq/qmod/__init__.py +3 -1
  73. classiq/qmod/builtins/functions/__init__.py +8 -0
  74. classiq/qmod/builtins/functions/allocation.py +36 -0
  75. classiq/qmod/builtins/functions/arithmetic.py +10 -5
  76. classiq/qmod/builtins/functions/mid_circuit_measurement.py +3 -0
  77. classiq/qmod/builtins/operations.py +2 -2
  78. classiq/qmod/declaration_inferrer.py +52 -24
  79. classiq/qmod/model_state_container.py +9 -0
  80. classiq/qmod/native/pretty_printer.py +25 -3
  81. classiq/qmod/pretty_print/pretty_printer.py +31 -14
  82. classiq/qmod/python_classical_type.py +12 -1
  83. classiq/qmod/qfunc.py +33 -8
  84. classiq/qmod/qmod_variable.py +188 -147
  85. classiq/qmod/quantum_function.py +3 -4
  86. classiq/qmod/semantics/validation/type_hints.py +19 -10
  87. classiq/qmod/symbolic.py +16 -3
  88. {classiq-0.70.0.dist-info → classiq-0.72.0.dist-info}/METADATA +1 -1
  89. {classiq-0.70.0.dist-info → classiq-0.72.0.dist-info}/RECORD +90 -91
  90. classiq/applications/finance/finance_model_constructor.py +0 -137
  91. classiq/applications/grover/__init__.py +0 -9
  92. classiq/applications/grover/grover_model_constructor.py +0 -167
  93. classiq/applications/libraries/__init__.py +0 -0
  94. classiq/applications/libraries/qmci_library.py +0 -22
  95. classiq/applications/qsvm/qsvm_model_constructor.py +0 -131
  96. classiq/model_expansions/quantum_operations/classicalif.py +0 -57
  97. classiq/model_expansions/quantum_operations/repeat.py +0 -62
  98. {classiq-0.70.0.dist-info → classiq-0.72.0.dist-info}/WHEEL +0 -0
@@ -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)
@@ -2,17 +2,32 @@ from collections.abc import Sequence
2
2
  from itertools import chain, combinations
3
3
  from typing import (
4
4
  TYPE_CHECKING,
5
+ Any,
5
6
  Generic,
6
7
  cast,
7
8
  )
8
9
  from uuid import UUID
9
10
 
11
+ import sympy
12
+
10
13
  from classiq.interface.debug_info.debug_info import FunctionDebugInfo
11
14
  from classiq.interface.exceptions import ClassiqExpansionError
15
+ from classiq.interface.generator.expressions.proxies.classical.any_classical_value import (
16
+ AnyClassicalValue,
17
+ )
18
+ from classiq.interface.generator.expressions.proxies.classical.classical_proxy import (
19
+ ClassicalProxy,
20
+ )
21
+ from classiq.interface.generator.expressions.proxies.classical.qmod_struct_instance import (
22
+ QmodStructInstance,
23
+ )
12
24
  from classiq.interface.generator.functions.port_declaration import (
13
25
  PortDeclarationDirection,
14
26
  )
15
27
  from classiq.interface.generator.generated_circuit_data import OperationLevel
28
+ from classiq.interface.model.classical_parameter_declaration import (
29
+ ClassicalParameterDeclaration,
30
+ )
16
31
  from classiq.interface.model.handle_binding import HandleBinding
17
32
  from classiq.interface.model.native_function_definition import NativeFunctionDefinition
18
33
  from classiq.interface.model.port_declaration import PortDeclaration
@@ -75,6 +90,16 @@ def _validate_cloning(evaluated_args: list[Evaluated]) -> None:
75
90
  )
76
91
 
77
92
 
93
+ def _is_symbolic(arg: Any) -> bool:
94
+ if isinstance(arg, list):
95
+ return any(_is_symbolic(item) for item in arg)
96
+ if isinstance(arg, QmodStructInstance):
97
+ return any(_is_symbolic(item) for item in arg.fields.values())
98
+ if isinstance(arg, sympy.Basic):
99
+ return len(arg.free_symbols) > 0
100
+ return isinstance(arg, (ClassicalProxy, AnyClassicalValue))
101
+
102
+
78
103
  class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSplitter):
79
104
  def __init__(self, interpreter: "BaseInterpreter") -> None:
80
105
  Emitter.__init__(self, interpreter)
@@ -197,7 +222,7 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
197
222
  ].occurrences_number += 1
198
223
  return function_def
199
224
 
200
- function_def = self._create_function_definition(function_context)
225
+ function_def = self._create_function_definition(function_context, args)
201
226
  self._expanded_functions[closure_id] = function_def
202
227
  self._top_level_scope[function_def.name] = Evaluated(
203
228
  value=function_context.closure.with_new_declaration(function_def)
@@ -210,14 +235,23 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
210
235
  return function_def
211
236
 
212
237
  def _create_function_definition(
213
- self, function_context: FunctionContext
238
+ self, function_context: FunctionContext, args: list[Evaluated]
214
239
  ) -> NativeFunctionDefinition:
215
- func_def = self._builder.create_definition(function_context)
240
+ params = [
241
+ param
242
+ for arg, param in zip(args, function_context.positional_arg_declarations)
243
+ if isinstance(param, PortDeclaration)
244
+ or (
245
+ isinstance(param, ClassicalParameterDeclaration)
246
+ and _is_symbolic(arg.value)
247
+ )
248
+ ]
249
+ func_def = self._builder.create_definition(function_context, params)
216
250
 
217
251
  captured_vars = function_context.closure.captured_vars.filter_vars(
218
252
  function_context.closure
219
253
  )
220
- captured_ports = captured_vars.get_captured_ports()
254
+ captured_ports = captured_vars.get_captured_parameters()
221
255
  if len(captured_ports) == 0:
222
256
  return func_def
223
257
  func_def.positional_arg_declarations = list(
@@ -239,15 +273,22 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
239
273
  closure: FunctionClosure,
240
274
  ) -> None:
241
275
  for parameter, argument in zip(parameters, arguments):
276
+ param_handle = HandleBinding(name=parameter.name)
242
277
  if isinstance(argument.value, QuantumSymbol):
243
278
  assert isinstance(parameter, PortDeclaration)
244
279
  closure.scope[parameter.name] = Evaluated(
245
280
  QuantumSymbol(
246
- handle=HandleBinding(name=parameter.name),
281
+ handle=param_handle,
247
282
  quantum_type=parameter.quantum_type,
248
283
  ),
249
284
  defining_function=closure,
250
285
  )
286
+ elif _is_symbolic(argument.value):
287
+ assert isinstance(parameter, ClassicalParameterDeclaration)
288
+ closure.scope[parameter.name] = Evaluated(
289
+ value=parameter.classical_type.get_classical_proxy(param_handle),
290
+ defining_function=closure,
291
+ )
251
292
  else:
252
293
  closure.scope[parameter.name] = argument
253
294
 
@@ -264,7 +305,9 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
264
305
  return [arg.emit() for arg in evaluated_args]
265
306
 
266
307
  positional_args = [
267
- arg.emit() for arg in evaluated_args if isinstance(arg.value, QuantumSymbol)
308
+ arg.emit()
309
+ for arg in evaluated_args
310
+ if isinstance(arg.value, QuantumSymbol) or _is_symbolic(arg.value)
268
311
  ]
269
312
 
270
313
  return positional_args
@@ -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,
@@ -19,14 +20,22 @@ from classiq.interface.generator.expressions.evaluated_expression import (
19
20
  EvaluatedExpression,
20
21
  )
21
22
  from classiq.interface.generator.expressions.expression import Expression
23
+ from classiq.interface.generator.expressions.proxies.classical.classical_proxy import (
24
+ ClassicalProxy,
25
+ )
26
+ from classiq.interface.generator.expressions.proxies.classical.utils import (
27
+ get_proxy_type,
28
+ )
29
+ from classiq.interface.generator.functions.classical_type import ClassicalType
22
30
  from classiq.interface.generator.functions.port_declaration import (
23
31
  PortDeclarationDirection,
24
32
  )
25
33
  from classiq.interface.helpers.pydantic_model_helpers import nameables_to_dict
26
- from classiq.interface.model.handle_binding import HandleBinding
34
+ from classiq.interface.model.handle_binding import HandleBinding, NestedHandleBinding
27
35
  from classiq.interface.model.native_function_definition import NativeFunctionDefinition
28
36
  from classiq.interface.model.quantum_function_declaration import (
29
37
  NamedParamsQuantumFunctionDeclaration,
38
+ PositionalArg,
30
39
  )
31
40
  from classiq.interface.model.quantum_statement import QuantumOperation, QuantumStatement
32
41
 
@@ -103,17 +112,20 @@ class Emitter(Generic[QuantumStatementT], ABC):
103
112
  op: QuantumOperation,
104
113
  context_name: str,
105
114
  block_names: Union[None, str, list[str]] = None,
106
- func_decl: Optional[NamedParamsQuantumFunctionDeclaration] = None,
115
+ params: Optional[Sequence[PositionalArg]] = None,
116
+ scope: Optional[Scope] = None,
107
117
  ) -> OperationContext:
108
118
  if isinstance(block_names, str):
109
119
  block_names = [block_names]
110
120
  block_names = block_names or ["body"]
111
- func_decl = func_decl or NamedParamsQuantumFunctionDeclaration(
112
- name=context_name
121
+ func_decl = NamedParamsQuantumFunctionDeclaration(
122
+ name=context_name,
123
+ positional_arg_declarations=[] if params is None else params,
113
124
  )
125
+ _scope = Scope(parent=self._current_scope) if scope is None else scope
114
126
  gen_closure = GenerativeClosure(
115
127
  name=func_decl.name,
116
- scope=Scope(parent=self._interpreter._builder.current_scope),
128
+ scope=_scope,
117
129
  blocks={},
118
130
  generative_blocks={
119
131
  block_name: GenerativeQFunc(
@@ -121,6 +133,7 @@ class Emitter(Generic[QuantumStatementT], ABC):
121
133
  )
122
134
  for block_name in block_names
123
135
  },
136
+ positional_arg_declarations=func_decl.positional_arg_declarations,
124
137
  )
125
138
  context = self._interpreter._expand_operation(gen_closure)
126
139
  op.clear_generative_blocks()
@@ -145,6 +158,7 @@ class Emitter(Generic[QuantumStatementT], ABC):
145
158
  return new_expression
146
159
 
147
160
  def emit_statement(self, statement: QuantumStatement) -> None:
161
+ self._update_captured_classical_vars(statement)
148
162
  if isinstance(statement, QuantumOperation):
149
163
  self._update_captured_vars(statement)
150
164
  if statement.uuid not in self._interpreter._model.debug_info:
@@ -153,6 +167,14 @@ class Emitter(Generic[QuantumStatementT], ABC):
153
167
  )
154
168
  self._builder.emit_statement(statement)
155
169
 
170
+ def _update_captured_classical_vars(self, stmt: QuantumStatement) -> None:
171
+ for expr in stmt.expressions:
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)
177
+
156
178
  def _update_captured_vars(self, op: QuantumOperation) -> None:
157
179
  handles = (
158
180
  [(handle, PortDeclarationDirection.Input) for handle in op.inputs]
@@ -167,6 +189,12 @@ class Emitter(Generic[QuantumStatementT], ABC):
167
189
  ) -> None:
168
190
  if handle.name not in self._current_scope:
169
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
170
198
  defining_function = self._current_scope[handle.name].defining_function
171
199
  if defining_function is None:
172
200
  raise ClassiqInternalExpansionError
@@ -178,8 +206,20 @@ class Emitter(Generic[QuantumStatementT], ABC):
178
206
  direction=direction,
179
207
  )
180
208
 
209
+ def _capture_classical_var(self, var_name: str, var_type: ClassicalType) -> None:
210
+ if var_name not in self._current_scope:
211
+ return
212
+ defining_function = self._current_scope[var_name].defining_function
213
+ if defining_function is None:
214
+ raise ClassiqInternalExpansionError
215
+ self._builder.current_block.captured_vars.capture_classical_var(
216
+ var_name=var_name,
217
+ var_type=var_type,
218
+ defining_function=defining_function,
219
+ )
220
+
181
221
  def _get_symbols_in_expression(self, expr: Expression) -> list[QuantumSymbol]:
182
- vrc = VarRefCollector(ignore_duplicated_handles=True)
222
+ vrc = VarRefCollector(ignore_duplicated_handles=True, unevaluated=True)
183
223
  vrc.visit(ast.parse(expr.expr))
184
224
  handles = dict.fromkeys(
185
225
  handle
@@ -187,3 +227,21 @@ class Emitter(Generic[QuantumStatementT], ABC):
187
227
  if isinstance(self._current_scope[handle.name].value, QuantumSymbol)
188
228
  )
189
229
  return [self._interpreter.evaluate(handle).value for handle in handles]
230
+
231
+ def _get_classical_vars_in_expression(
232
+ self, expr: Expression
233
+ ) -> list[tuple[str, ClassicalType]]:
234
+ vrc = VarRefCollector(
235
+ ignore_duplicated_handles=True, ignore_sympy_symbols=True, unevaluated=True
236
+ )
237
+ vrc.visit(ast.parse(expr.expr))
238
+ return list(
239
+ {
240
+ handle.name: get_proxy_type(proxy)
241
+ for handle in vrc.var_handles
242
+ if handle.name in self._current_scope
243
+ and isinstance(
244
+ proxy := self._current_scope[handle.name].value, ClassicalProxy
245
+ )
246
+ }.items()
247
+ )
@@ -26,6 +26,10 @@ class ExpressionEvaluator(Emitter[QuantumOperation]):
26
26
  )
27
27
  for symbol in self._get_symbols_in_expression(evaluated_expression):
28
28
  self._capture_handle(symbol.handle, PortDeclarationDirection.Inout)
29
+ for var_name, var_type in self._get_classical_vars_in_expression(
30
+ evaluated_expression
31
+ ):
32
+ self._capture_classical_var(var_name, var_type)
29
33
  op = op.model_copy(
30
34
  update={self._expression_name: evaluated_expression, "back_ref": op.uuid}
31
35
  )
@@ -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
@@ -1,13 +1,10 @@
1
1
  import itertools
2
- import re
3
2
  from collections import UserDict
4
3
  from collections.abc import Iterator
5
4
  from dataclasses import dataclass
6
5
  from functools import singledispatch
7
6
  from typing import TYPE_CHECKING, Any, Optional, TypeVar, Union
8
7
 
9
- from sympy import Symbol
10
-
11
8
  from classiq.interface.exceptions import (
12
9
  ClassiqExpansionError,
13
10
  ClassiqInternalExpansionError,
@@ -16,8 +13,8 @@ from classiq.interface.generator.expressions.evaluated_expression import (
16
13
  EvaluatedExpression,
17
14
  )
18
15
  from classiq.interface.generator.expressions.expression import Expression
19
- from classiq.interface.generator.expressions.expression_constants import (
20
- CPARAM_EXECUTION_SUFFIX_PATTERN,
16
+ from classiq.interface.generator.expressions.proxies.classical.classical_scalar_proxy import (
17
+ ClassicalScalarProxy,
21
18
  )
22
19
  from classiq.interface.generator.expressions.proxies.classical.qmod_struct_instance import (
23
20
  QmodStructInstance,
@@ -53,28 +50,38 @@ class QuantumSymbol:
53
50
  def emit(self) -> HandleBinding:
54
51
  return self.handle
55
52
 
56
- def __getitem__(self, item: Union[slice, int]) -> "QuantumSymbol":
57
- if isinstance(item, int):
58
- return self._subscript(item)
59
-
60
- 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)
61
59
 
62
- 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":
63
65
  if not isinstance(self.quantum_type, QuantumBitvector):
64
66
  raise ClassiqExpansionError(
65
67
  f"{self.quantum_type.type_name} is not subscriptable"
66
68
  )
67
- 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:
68
72
  raise ClassiqExpansionError(
69
73
  f"{self.quantum_type.type_name} slice '{self.handle}[{start}:{end}]' "
70
74
  f"has non-positive length"
71
75
  )
72
- array_length = self.quantum_type.length_value
73
- 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
+ ):
74
81
  raise ClassiqExpansionError(
75
82
  f"Slice [{start}:{end}] is out of bounds for "
76
83
  f"{self.quantum_type.type_name.lower()} {str(self.handle)!r} (of "
77
- f"length {array_length})"
84
+ f"length {self.quantum_type.length_value})"
78
85
  )
79
86
  return QuantumSymbol(
80
87
  handle=SlicedHandleBinding(
@@ -88,17 +95,24 @@ class QuantumSymbol:
88
95
  ),
89
96
  )
90
97
 
91
- def _subscript(self, index: int) -> "QuantumSymbol":
98
+ def _subscript(self, index: Union[int, ClassicalScalarProxy]) -> "QuantumSymbol":
92
99
  if not isinstance(self.quantum_type, QuantumBitvector):
93
100
  raise ClassiqExpansionError(
94
101
  f"{self.quantum_type.type_name} is not subscriptable"
95
102
  )
96
- array_length = self.quantum_type.length_value
97
- 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
+ ):
98
112
  raise ClassiqExpansionError(
99
113
  f"Index {index} is out of bounds for "
100
114
  f"{self.quantum_type.type_name.lower()} {str(self.handle)!r} (of "
101
- f"length {array_length})"
115
+ f"length {self.quantum_type.length})"
102
116
  )
103
117
  return QuantumSymbol(
104
118
  handle=SubscriptHandleBinding(
@@ -204,8 +218,6 @@ class Scope(EvaluatedUserDict):
204
218
  return self.data[name]
205
219
  if self._parent is not None:
206
220
  return self._parent[name]
207
- if re.search(CPARAM_EXECUTION_SUFFIX_PATTERN, name):
208
- return Evaluated(value=Symbol(name))
209
221
  raise ClassiqExpansionError(f"Variable {name!r} is undefined")
210
222
 
211
223
  def __contains__(self, item: Any) -> bool: