classiq 0.60.1__py3-none-any.whl → 0.62.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 (108) hide show
  1. classiq/__init__.py +2 -0
  2. classiq/_internals/client.py +6 -1
  3. classiq/applications/__init__.py +1 -1
  4. classiq/applications/chemistry/__init__.py +7 -7
  5. classiq/applications/chemistry/chemistry_model_constructor.py +17 -6
  6. classiq/applications/combinatorial_helpers/optimization_model.py +9 -2
  7. classiq/applications/combinatorial_helpers/pyomo_utils.py +60 -9
  8. classiq/applications/combinatorial_optimization/__init__.py +7 -1
  9. classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +2 -0
  10. classiq/applications/combinatorial_optimization/combinatorial_problem.py +230 -0
  11. classiq/applications/finance/finance_model_constructor.py +6 -6
  12. classiq/applications/grover/grover_model_constructor.py +3 -0
  13. classiq/applications/libraries/qmci_library.py +1 -10
  14. classiq/applications/qnn/__init__.py +1 -1
  15. classiq/applications/qnn/datasets/__init__.py +8 -8
  16. classiq/applications/qsvm/qsvm.py +1 -1
  17. classiq/execution/__init__.py +0 -2
  18. classiq/execution/execution_session.py +6 -0
  19. classiq/execution/jobs.py +9 -1
  20. classiq/executor.py +1 -1
  21. classiq/interface/_version.py +1 -1
  22. classiq/interface/backend/backend_preferences.py +33 -15
  23. classiq/interface/backend/quantum_backend_providers.py +3 -1
  24. classiq/interface/executor/execution_preferences.py +1 -1
  25. classiq/interface/generator/application_apis/chemistry_declarations.py +1 -1
  26. classiq/interface/generator/application_apis/finance_declarations.py +2 -2
  27. classiq/interface/generator/arith/arithmetic.py +16 -1
  28. classiq/interface/generator/arith/arithmetic_expression_validator.py +4 -3
  29. classiq/interface/generator/copy.py +47 -0
  30. classiq/interface/generator/expressions/expression_constants.py +3 -0
  31. classiq/interface/generator/function_param_list_without_self_reference.py +2 -0
  32. classiq/interface/generator/generated_circuit_data.py +58 -20
  33. classiq/interface/generator/model/__init__.py +1 -1
  34. classiq/interface/generator/model/preferences/preferences.py +1 -1
  35. classiq/interface/generator/model/quantum_register.py +3 -3
  36. classiq/interface/generator/standard_gates/controlled_standard_gates.py +20 -32
  37. classiq/interface/generator/types/compilation_metadata.py +2 -1
  38. classiq/interface/ide/visual_model.py +1 -0
  39. classiq/interface/interface_version.py +1 -1
  40. classiq/interface/model/model.py +2 -3
  41. classiq/interface/model/quantum_function_call.py +4 -7
  42. classiq/interface/model/quantum_function_declaration.py +7 -0
  43. classiq/interface/model/quantum_lambda_function.py +10 -1
  44. classiq/interface/model/quantum_type.py +3 -1
  45. classiq/model_expansions/atomic_expression_functions_defs.py +3 -1
  46. classiq/model_expansions/capturing/captured_vars.py +36 -17
  47. classiq/model_expansions/capturing/mangling_utils.py +23 -15
  48. classiq/model_expansions/closure.py +6 -9
  49. classiq/model_expansions/evaluators/arg_type_match.py +7 -7
  50. classiq/model_expansions/expression_evaluator.py +5 -2
  51. classiq/model_expansions/function_builder.py +26 -4
  52. classiq/model_expansions/generative_functions.py +13 -91
  53. classiq/model_expansions/interpreter.py +75 -46
  54. classiq/model_expansions/quantum_operations/call_emitter.py +42 -29
  55. classiq/model_expansions/quantum_operations/classicalif.py +1 -1
  56. classiq/model_expansions/quantum_operations/control.py +5 -31
  57. classiq/model_expansions/quantum_operations/emitter.py +29 -17
  58. classiq/model_expansions/quantum_operations/expression_operation.py +3 -5
  59. classiq/model_expansions/quantum_operations/inplace_binary_operation.py +57 -15
  60. classiq/model_expansions/quantum_operations/invert.py +1 -6
  61. classiq/model_expansions/quantum_operations/phase.py +2 -5
  62. classiq/model_expansions/quantum_operations/power.py +0 -4
  63. classiq/model_expansions/quantum_operations/quantum_assignment_operation.py +19 -30
  64. classiq/model_expansions/quantum_operations/quantum_function_call.py +4 -2
  65. classiq/model_expansions/quantum_operations/shallow_emitter.py +161 -0
  66. classiq/model_expansions/quantum_operations/within_apply.py +0 -14
  67. classiq/model_expansions/scope.py +12 -16
  68. classiq/model_expansions/scope_initialization.py +0 -11
  69. classiq/model_expansions/sympy_conversion/expression_to_sympy.py +7 -0
  70. classiq/model_expansions/sympy_conversion/sympy_to_python.py +12 -2
  71. classiq/model_expansions/transformers/ast_renamer.py +26 -0
  72. classiq/model_expansions/transformers/var_splitter.py +11 -12
  73. classiq/model_expansions/visitors/variable_references.py +20 -12
  74. classiq/qmod/builtins/classical_execution_primitives.py +6 -6
  75. classiq/qmod/builtins/classical_functions.py +10 -10
  76. classiq/qmod/builtins/functions/__init__.py +89 -103
  77. classiq/qmod/builtins/functions/amplitude_estimation.py +1 -1
  78. classiq/qmod/builtins/functions/arithmetic.py +1 -1
  79. classiq/qmod/builtins/functions/discrete_sine_cosine_transform.py +6 -6
  80. classiq/qmod/builtins/functions/grover.py +5 -5
  81. classiq/qmod/builtins/functions/hea.py +1 -1
  82. classiq/qmod/builtins/functions/linear_pauli_rotation.py +2 -2
  83. classiq/qmod/builtins/functions/modular_exponentiation.py +8 -8
  84. classiq/qmod/builtins/functions/operators.py +1 -1
  85. classiq/qmod/builtins/functions/qaoa_penalty.py +5 -5
  86. classiq/qmod/builtins/functions/qft_functions.py +2 -2
  87. classiq/qmod/builtins/functions/qpe.py +9 -12
  88. classiq/qmod/builtins/functions/qsvt.py +177 -15
  89. classiq/qmod/builtins/functions/state_preparation.py +9 -9
  90. classiq/qmod/builtins/functions/swap_test.py +1 -1
  91. classiq/qmod/builtins/functions/utility_functions.py +2 -2
  92. classiq/qmod/builtins/functions/variational.py +2 -2
  93. classiq/qmod/builtins/operations.py +22 -83
  94. classiq/qmod/builtins/structs.py +9 -9
  95. classiq/qmod/native/pretty_printer.py +17 -19
  96. classiq/qmod/pretty_print/pretty_printer.py +9 -6
  97. classiq/qmod/python_classical_type.py +1 -5
  98. classiq/qmod/qmod_variable.py +2 -5
  99. classiq/qmod/quantum_expandable.py +20 -5
  100. classiq/qmod/quantum_function.py +23 -10
  101. classiq/qmod/semantics/static_semantics_visitor.py +34 -16
  102. classiq/qmod/semantics/validation/func_call_validation.py +9 -5
  103. classiq/qmod/semantics/validation/function_name_collisions_validation.py +23 -0
  104. classiq/qmod/symbolic.py +47 -47
  105. {classiq-0.60.1.dist-info → classiq-0.62.0.dist-info}/METADATA +2 -1
  106. {classiq-0.60.1.dist-info → classiq-0.62.0.dist-info}/RECORD +107 -103
  107. classiq/execution/qaoa.py +0 -86
  108. {classiq-0.60.1.dist-info → classiq-0.62.0.dist-info}/WHEEL +0 -0
@@ -1,6 +1,6 @@
1
- import copy
2
- from collections.abc import Iterator, Sequence
3
- from contextlib import contextmanager, nullcontext
1
+ from collections import defaultdict
2
+ from collections.abc import Sequence
3
+ from contextlib import nullcontext
4
4
  from functools import singledispatchmethod
5
5
  from typing import Any, Optional, cast
6
6
 
@@ -15,6 +15,11 @@ from classiq.interface.exceptions import (
15
15
  )
16
16
  from classiq.interface.generator.constant import Constant
17
17
  from classiq.interface.generator.expressions.expression import Expression
18
+ from classiq.interface.generator.functions.builtins.internal_operators import (
19
+ CONTROL_OPERATOR_NAME,
20
+ INVERT_OPERATOR_NAME,
21
+ WITHIN_APPLY_NAME,
22
+ )
18
23
  from classiq.interface.generator.types.compilation_metadata import CompilationMetadata
19
24
  from classiq.interface.model.bind_operation import BindOperation
20
25
  from classiq.interface.model.classical_if import ClassicalIf
@@ -35,14 +40,16 @@ from classiq.interface.model.quantum_expressions.quantum_expression import (
35
40
  QuantumAssignmentOperation,
36
41
  )
37
42
  from classiq.interface.model.quantum_function_call import (
38
- OperandIdentifier,
39
43
  QuantumFunctionCall,
40
44
  )
41
45
  from classiq.interface.model.quantum_function_declaration import (
42
46
  NamedParamsQuantumFunctionDeclaration,
43
47
  QuantumFunctionDeclaration,
44
48
  )
45
- from classiq.interface.model.quantum_lambda_function import QuantumLambdaFunction
49
+ from classiq.interface.model.quantum_lambda_function import (
50
+ OperandIdentifier,
51
+ QuantumLambdaFunction,
52
+ )
46
53
  from classiq.interface.model.quantum_statement import QuantumStatement
47
54
  from classiq.interface.model.repeat import Repeat
48
55
  from classiq.interface.model.variable_declaration_statement import (
@@ -81,6 +88,7 @@ from classiq.model_expansions.quantum_operations import (
81
88
  WithinApplyEmitter,
82
89
  )
83
90
  from classiq.model_expansions.quantum_operations.phase import PhaseEmitter
91
+ from classiq.model_expansions.quantum_operations.shallow_emitter import ShallowEmitter
84
92
  from classiq.model_expansions.scope import Evaluated, QuantumSymbol, Scope
85
93
  from classiq.model_expansions.scope_initialization import (
86
94
  add_constants_to_scope,
@@ -103,43 +111,30 @@ class Interpreter:
103
111
  ) -> None:
104
112
  self._is_frontend = is_frontend
105
113
  self._model = model
106
- self._current_scope = Scope()
107
- self._top_level_scope = self._current_scope
114
+ self._top_level_scope = Scope()
108
115
  self._builder = OperationBuilder(self._top_level_scope)
109
116
  self._expanded_functions: dict[str, NativeFunctionDefinition] = {}
110
117
 
111
- self._main_renamer: ExpressionRenamer = get_main_renamer(self._model.functions)
118
+ self._main_renamer: Optional[ExpressionRenamer] = (
119
+ get_main_renamer(self._model.functions) if self._is_frontend else None
120
+ )
112
121
 
113
122
  if generative_functions is None:
114
123
  generative_functions = []
115
124
  self._generative_functions = generative_functions
116
- init_top_level_scope(model, generative_functions, self._current_scope)
125
+ init_top_level_scope(model, generative_functions, self._top_level_scope)
117
126
  self._expanded_functions_compilation_metadata: dict[
118
127
  str, CompilationMetadata
119
- ] = dict()
128
+ ] = defaultdict(CompilationMetadata)
120
129
  self._counted_name_allocator = CountedNameAllocator()
121
130
  self._error_manager: ErrorManager = ErrorManager()
122
131
 
123
- @contextmanager
124
- def _scope_guard(self, scope: Scope) -> Iterator[None]:
125
- """This manager restores both `scope` and `self._current_scope` to their previous values."""
126
- scope_data = copy.copy(scope.data)
127
- prev_context_scope_data = copy.copy(self._current_scope.data)
128
-
129
- prev_context_scope = self._current_scope
130
- self._current_scope = scope
131
- yield
132
- prev_context_scope.data = prev_context_scope_data
133
- self._current_scope = prev_context_scope
134
-
135
- scope.data = scope_data
136
-
137
132
  def _expand_main_func(self) -> None:
138
133
  main_closure = FunctionClosure.create(
139
134
  name=self._model.main_func.name,
140
135
  positional_arg_declarations=self._model.main_func.positional_arg_declarations,
141
136
  body=self._model.main_func.body,
142
- scope=Scope(parent=self._current_scope),
137
+ scope=Scope(parent=self._top_level_scope),
143
138
  expr_renamer=self._main_renamer,
144
139
  _depth=0,
145
140
  )
@@ -188,11 +183,11 @@ class Interpreter:
188
183
 
189
184
  @evaluate.register
190
185
  def evaluate_classical_expression(self, expression: Expression) -> Evaluated:
191
- return evaluate_classical_expression(expression, self._current_scope)
186
+ return evaluate_classical_expression(expression, self._builder.current_scope)
192
187
 
193
188
  @evaluate.register
194
189
  def evaluate_identifier(self, identifier: str) -> Evaluated:
195
- return self._current_scope[identifier]
190
+ return self._builder.current_scope[identifier]
196
191
 
197
192
  @evaluate.register
198
193
  def evaluate_lambda(self, function: QuantumLambdaFunction) -> Evaluated:
@@ -223,7 +218,7 @@ class Interpreter:
223
218
  name=func_decl.name,
224
219
  positional_arg_declarations=func_decl.positional_arg_declarations,
225
220
  body=function.body,
226
- scope=Scope(parent=self._current_scope),
221
+ scope=Scope(parent=self._builder.current_scope),
227
222
  is_lambda=True,
228
223
  **extra_args,
229
224
  ),
@@ -280,11 +275,21 @@ class Interpreter:
280
275
 
281
276
  @emit.register
282
277
  def emit_quantum_assignment_operation(self, op: QuantumAssignmentOperation) -> None:
283
- QuantumAssignmentOperationEmitter(self).emit(op)
278
+ if self._is_frontend:
279
+ ShallowEmitter(
280
+ self, "assignment_operation", components=["expression", "result_var"]
281
+ ).emit(op)
282
+ else:
283
+ QuantumAssignmentOperationEmitter(self).emit(op)
284
284
 
285
285
  @emit.register
286
- def emit_inplace_binary_operation(self, operation: InplaceBinaryOperation) -> None:
287
- InplaceBinaryOperationEmitter(self).emit(operation)
286
+ def emit_inplace_binary_operation(self, op: InplaceBinaryOperation) -> None:
287
+ if self._is_frontend:
288
+ ShallowEmitter(
289
+ self, "inplace_binary_operation", components=["target", "value"]
290
+ ).emit(op)
291
+ else:
292
+ InplaceBinaryOperationEmitter(self).emit(op)
288
293
 
289
294
  @emit.register
290
295
  def emit_variable_declaration(
@@ -298,11 +303,21 @@ class Interpreter:
298
303
 
299
304
  @emit.register
300
305
  def emit_within_apply(self, within_apply: WithinApply) -> None:
301
- WithinApplyEmitter(self).emit(within_apply)
306
+ if self._is_frontend:
307
+ ShallowEmitter(
308
+ self,
309
+ WITHIN_APPLY_NAME,
310
+ components=["within", "apply", "compute", "action"],
311
+ ).emit(within_apply)
312
+ else:
313
+ WithinApplyEmitter(self).emit(within_apply)
302
314
 
303
315
  @emit.register
304
316
  def emit_invert(self, invert: Invert) -> None:
305
- InvertEmitter(self).emit(invert)
317
+ if self._is_frontend:
318
+ ShallowEmitter(self, INVERT_OPERATOR_NAME, components=["body"]).emit(invert)
319
+ else:
320
+ InvertEmitter(self).emit(invert)
306
321
 
307
322
  @emit.register
308
323
  def emit_repeat(self, repeat: Repeat) -> None:
@@ -310,15 +325,32 @@ class Interpreter:
310
325
 
311
326
  @emit.register
312
327
  def emit_control(self, control: Control) -> None:
313
- ControlEmitter(self).emit(control)
328
+ if self._is_frontend:
329
+ ShallowEmitter(
330
+ self,
331
+ CONTROL_OPERATOR_NAME,
332
+ components=["expression", "body", "else_block"],
333
+ ).emit(control)
334
+ else:
335
+ ControlEmitter(self).emit(control)
314
336
 
315
337
  @emit.register
316
338
  def emit_power(self, power: Power) -> None:
317
- PowerEmitter(self).emit(power)
339
+ if self._is_frontend:
340
+ ShallowEmitter(
341
+ self, CONTROL_OPERATOR_NAME, components=["power", "body"]
342
+ ).emit(power)
343
+ else:
344
+ PowerEmitter(self).emit(power)
318
345
 
319
346
  @emit.register
320
347
  def emit_phase(self, phase: PhaseOperation) -> None:
321
- PhaseEmitter(self).emit(phase)
348
+ if self._is_frontend:
349
+ ShallowEmitter(self, "phase", components=["expression", "theta"]).emit(
350
+ phase
351
+ )
352
+ else:
353
+ PhaseEmitter(self).emit(phase)
322
354
 
323
355
  def _expand_block(self, block: Sequence[QuantumStatement], block_name: str) -> None:
324
356
  with self._builder.block_context(block_name):
@@ -343,19 +375,16 @@ class Interpreter:
343
375
  pass
344
376
  elif isinstance(operation, FunctionClosure) and operation.name == "permute":
345
377
  # special expansion since permute is generative
346
- with self._scope_guard(operation.scope):
347
- self._expand_permute()
378
+ self._expand_permute()
348
379
  elif isinstance(operation, GenerativeClosure):
349
- with self._scope_guard(operation.scope):
350
- args = [
351
- self.evaluate(param.name)
352
- for param in operation.positional_arg_declarations
353
- ]
354
- emit_generative_statements(self, operation, args)
380
+ args = [
381
+ self.evaluate(param.name)
382
+ for param in operation.positional_arg_declarations
383
+ ]
384
+ emit_generative_statements(self, operation, args)
355
385
  else:
356
386
  for block, block_body in operation.blocks.items():
357
- with self._scope_guard(operation.scope):
358
- self._expand_block(block_body, block)
387
+ self._expand_block(block_body, block)
359
388
 
360
389
  return context
361
390
 
@@ -3,6 +3,7 @@ from itertools import chain
3
3
  from typing import (
4
4
  TYPE_CHECKING,
5
5
  Generic,
6
+ Optional,
6
7
  cast,
7
8
  )
8
9
 
@@ -37,7 +38,6 @@ from classiq.model_expansions.evaluators.parameter_types import (
37
38
  from classiq.model_expansions.function_builder import (
38
39
  FunctionContext,
39
40
  )
40
- from classiq.model_expansions.generative_functions import emit_operands_as_declarative
41
41
  from classiq.model_expansions.quantum_operations.emitter import (
42
42
  Emitter,
43
43
  QuantumStatementT,
@@ -53,7 +53,7 @@ if TYPE_CHECKING:
53
53
  class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSplitter):
54
54
  def __init__(self, interpreter: "Interpreter") -> None:
55
55
  Emitter.__init__(self, interpreter)
56
- VarSplitter.__init__(self, interpreter._current_scope)
56
+ VarSplitter.__init__(self, interpreter._builder.current_scope)
57
57
 
58
58
  @staticmethod
59
59
  def _should_wrap(body: Sequence[QuantumStatement]) -> bool:
@@ -73,17 +73,25 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
73
73
  return self._create_quantum_function_call(wrapping_function, list())
74
74
 
75
75
  def _emit_quantum_function_call(
76
- self, function: FunctionClosure, args: list[ArgValue]
76
+ self,
77
+ function: FunctionClosure,
78
+ args: list[ArgValue],
79
+ propagated_debug_info: Optional[FunctionDebugInfo] = None,
77
80
  ) -> QuantumFunctionCall:
78
- call = self._create_quantum_function_call(function, args)
81
+ call = self._create_quantum_function_call(
82
+ function, args, propagated_debug_info=propagated_debug_info
83
+ )
79
84
  self.emit_statement(call)
80
85
  return call
81
86
 
82
87
  def _create_quantum_function_call(
83
- self, function: FunctionClosure, args: list[ArgValue]
88
+ self,
89
+ function: FunctionClosure,
90
+ args: list[ArgValue],
91
+ propagated_debug_info: Optional[FunctionDebugInfo] = None,
84
92
  ) -> QuantumFunctionCall:
93
+ function = function.clone()
85
94
  function = function.set_depth(self._builder.current_function.depth + 1)
86
- function = function.copy_scope()
87
95
  evaluated_args = [self._interpreter.evaluate(arg) for arg in args]
88
96
  new_declaration = self._prepare_fully_typed_declaration(
89
97
  function, evaluated_args
@@ -107,15 +115,20 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
107
115
  self._top_level_scope[function_def.name] = Evaluated(
108
116
  value=function_context.closure.with_new_declaration(function_def)
109
117
  )
118
+ compilation_metadata = self._functions_compilation_metadata.get(
119
+ function.name
120
+ )
121
+ if compilation_metadata is not None:
122
+ self._expanded_functions_compilation_metadata[function_def.name] = (
123
+ compilation_metadata
124
+ )
125
+ else:
126
+ self._expanded_functions_compilation_metadata[
127
+ function_def.name
128
+ ].occurrences_number += 1
129
+
110
130
  new_declaration = function_def
111
131
  new_function_name = function_def.name
112
- compilation_metadata = self._functions_compilation_metadata.get(
113
- function.name
114
- )
115
- if compilation_metadata is not None:
116
- self._expanded_functions_compilation_metadata[new_function_name] = (
117
- compilation_metadata
118
- )
119
132
 
120
133
  new_positional_args = self._get_new_positional_args(
121
134
  evaluated_args, is_atomic, new_positional_arg_decls
@@ -144,10 +157,14 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
144
157
  for arg_decl, evaluated_arg in zip(new_positional_arg_decls, evaluated_args)
145
158
  if isinstance(arg_decl, PortDeclaration)
146
159
  }
147
- self._interpreter._model.debug_info[new_call.uuid] = FunctionDebugInfo(
160
+ self._debug_info[new_call.uuid] = FunctionDebugInfo(
148
161
  name=new_call.func_name,
149
162
  level=OperationLevel.QMOD_FUNCTION_CALL,
150
- parameters=parameters,
163
+ parameters=(
164
+ parameters
165
+ if propagated_debug_info is None
166
+ else propagated_debug_info.parameters
167
+ ),
151
168
  is_allocate_or_free=is_allocate_or_free,
152
169
  port_to_passed_variable_map=port_to_passed_variable_map,
153
170
  )
@@ -203,10 +220,7 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
203
220
  new_positional_arg_decls, evaluated_args
204
221
  )
205
222
  if is_atomic:
206
- return [
207
- emit_operands_as_declarative(self._interpreter, param, arg)
208
- for param, arg in zip(new_positional_arg_decls, evaluated_args)
209
- ]
223
+ return [arg.emit() for arg in evaluated_args]
210
224
 
211
225
  positional_args = [
212
226
  arg.emit() for arg in evaluated_args if isinstance(arg.value, QuantumSymbol)
@@ -230,13 +244,12 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
230
244
  different from the call scope. For example, the former uses r,s and the latter
231
245
  uses p, q.
232
246
  """
233
- with self._scope_guard(Scope(parent=self._current_scope)):
234
- # The signature scope is passed as a separate argument to avoid contaminating the statement execution scope
235
- return NamedParamsQuantumFunctionDeclaration(
236
- name=function.name,
237
- positional_arg_declarations=evaluate_parameter_types_from_args(
238
- function,
239
- function.signature_scope,
240
- evaluated_args,
241
- ),
242
- )
247
+ # The signature scope is passed as a separate argument to avoid contaminating the statement execution scope
248
+ return NamedParamsQuantumFunctionDeclaration(
249
+ name=function.name,
250
+ positional_arg_declarations=evaluate_parameter_types_from_args(
251
+ function,
252
+ function.signature_scope,
253
+ evaluated_args,
254
+ ),
255
+ )
@@ -26,7 +26,7 @@ class ClassicalIfEmitter(CallEmitter[ClassicalIf]):
26
26
  if is_generative:
27
27
  if not classical_if.has_generative_block(op_name):
28
28
  return
29
- context = self._register_generative_context(classical_if, op_name, op_name)
29
+ context = self._expand_generative_context(classical_if, op_name, op_name)
30
30
  context.blocks["body"] = context.blocks[op_name]
31
31
  context.blocks.pop(op_name)
32
32
  body = context.statements("body")
@@ -55,19 +55,16 @@ from classiq.qmod.builtins.functions.standard_gates import X
55
55
 
56
56
  class ControlEmitter(ExpressionOperationEmitter[Control]):
57
57
  def emit(self, control: Control, /) -> None:
58
- condition = self._evaluate_op_expression(control)
59
-
58
+ condition = control.expression
60
59
  arrays_with_subscript = self.split_symbols(
61
60
  condition, self._counted_name_allocator.allocate
62
61
  )
63
62
  if len(arrays_with_subscript) > 0:
64
- if control.is_generative():
65
- control = self._expand_generative_control(control)
66
63
  self._emit_with_split(control, condition, arrays_with_subscript)
67
64
  return
68
65
 
69
- control = self._evaluate_types_in_expression(control, condition)
70
- condition_val = condition.value.value
66
+ control = self._evaluate_types_in_expression(control)
67
+ condition_val = control.expression.value.value
71
68
  if isinstance(condition_val, QmodSizedProxy):
72
69
  if control.else_block is None:
73
70
  self._validate_canonical_condition(condition_val)
@@ -96,25 +93,9 @@ class ControlEmitter(ExpressionOperationEmitter[Control]):
96
93
  )
97
94
  return Expression(expr=f"{lhs} == 1")
98
95
 
99
- def _expand_generative_control(self, control: Control) -> Control:
100
- block_names = ["body"]
101
- if control.has_generative_block("else_block"):
102
- block_names += ["else_block"]
103
- context = self._register_generative_context(
104
- control, CONTROL_OPERATOR_NAME, block_names
105
- )
106
- new_blocks = {"body": context.statements("body")}
107
- if "else_block" in block_names:
108
- new_blocks["else_block"] = context.statements("else_block")
109
- return control.model_copy(update=new_blocks)
110
-
111
96
  def _emit_canonical_control(self, control: Control) -> None:
112
97
  # canonical means control(q, body) where q is a single quantum variable
113
- is_generative = control.is_generative()
114
- if is_generative:
115
- control = self._expand_generative_control(control)
116
-
117
- if not is_generative and self._should_wrap_control(control):
98
+ if self._should_wrap_control(control):
118
99
  self._emit_wrapped(control)
119
100
  return
120
101
  self._emit_as_operation(control)
@@ -211,12 +192,6 @@ class ControlEmitter(ExpressionOperationEmitter[Control]):
211
192
  compute=[else_compute_call],
212
193
  action=[control_else_inner],
213
194
  )
214
- if control_else_inner.is_generative():
215
- control_then.remove_generative_block("else_block")
216
- control_else_inner.set_generative_block(
217
- "body", control_else_inner.get_generative_block("else_block")
218
- )
219
- control_else_inner.remove_generative_block("else_block")
220
195
  return [control_then, control_else]
221
196
 
222
197
  def _control_with_x_gates(
@@ -296,8 +271,7 @@ class ControlEmitter(ExpressionOperationEmitter[Control]):
296
271
  condition_value = control.expression.value.value
297
272
  if not (
298
273
  isinstance(condition_value, Boolean)
299
- or self._all_vars_boolean(control)
300
- and self._is_res_boolean(control)
274
+ or (self._all_vars_boolean(control) and self._is_res_boolean(control))
301
275
  ):
302
276
  raise ClassiqExpansionError(_condition_err_msg(condition_value))
303
277
 
@@ -1,3 +1,4 @@
1
+ import ast
1
2
  from abc import abstractmethod
2
3
  from typing import (
3
4
  TYPE_CHECKING,
@@ -9,6 +10,7 @@ from typing import (
9
10
 
10
11
  import sympy
11
12
 
13
+ from classiq.interface.debug_info.debug_info import DebugInfoCollection
12
14
  from classiq.interface.exceptions import ClassiqInternalExpansionError
13
15
  from classiq.interface.generator.expressions.evaluated_expression import (
14
16
  EvaluatedExpression,
@@ -24,9 +26,8 @@ from classiq.interface.model.quantum_function_declaration import (
24
26
  )
25
27
  from classiq.interface.model.quantum_statement import QuantumOperation, QuantumStatement
26
28
 
27
- from classiq.model_expansions.closure import Closure, FunctionClosure, GenerativeClosure
29
+ from classiq.model_expansions.closure import Closure, GenerativeClosure
28
30
  from classiq.model_expansions.function_builder import (
29
- FunctionContext,
30
31
  OperationBuilder,
31
32
  OperationContext,
32
33
  )
@@ -35,6 +36,7 @@ from classiq.model_expansions.sympy_conversion.sympy_to_python import (
35
36
  translate_sympy_quantum_expression,
36
37
  )
37
38
  from classiq.model_expansions.utils.counted_name_allocator import CountedNameAllocator
39
+ from classiq.model_expansions.visitors.variable_references import VarRefCollector
38
40
  from classiq.qmod.quantum_function import GenerativeQFunc
39
41
 
40
42
  if TYPE_CHECKING:
@@ -47,7 +49,6 @@ class Emitter(Generic[QuantumStatementT]):
47
49
  def __init__(self, interpreter: "Interpreter") -> None:
48
50
  self._interpreter = interpreter
49
51
 
50
- self._scope_guard = self._interpreter._scope_guard
51
52
  self._machine_precision = self._interpreter._model.preferences.machine_precision
52
53
  self._expanded_functions_compilation_metadata = (
53
54
  self._interpreter._expanded_functions_compilation_metadata
@@ -55,20 +56,12 @@ class Emitter(Generic[QuantumStatementT]):
55
56
  self._functions_compilation_metadata = (
56
57
  self._interpreter._model.functions_compilation_metadata
57
58
  )
58
- self._generative_contexts: dict[str, OperationContext] = {}
59
59
 
60
60
  @abstractmethod
61
61
  def emit(self, statement: QuantumStatementT, /) -> None:
62
62
  pass
63
63
 
64
64
  def _expand_operation(self, closure: Closure) -> OperationContext:
65
- if closure.name in self._generative_contexts:
66
- if isinstance(closure, FunctionClosure):
67
- return FunctionContext(
68
- closure=closure,
69
- blocks=self._generative_contexts[closure.name].blocks,
70
- )
71
- return self._generative_contexts[closure.name]
72
65
  return self._interpreter._expand_operation(closure)
73
66
 
74
67
  @property
@@ -77,7 +70,7 @@ class Emitter(Generic[QuantumStatementT]):
77
70
 
78
71
  @property
79
72
  def _current_scope(self) -> Scope:
80
- return self._interpreter._current_scope
73
+ return self._builder.current_scope
81
74
 
82
75
  @property
83
76
  def _top_level_scope(self) -> Scope:
@@ -91,7 +84,11 @@ class Emitter(Generic[QuantumStatementT]):
91
84
  def _counted_name_allocator(self) -> CountedNameAllocator:
92
85
  return self._interpreter._counted_name_allocator
93
86
 
94
- def _register_generative_context(
87
+ @property
88
+ def _debug_info(self) -> DebugInfoCollection:
89
+ return self._interpreter._model.debug_info
90
+
91
+ def _expand_generative_context(
95
92
  self,
96
93
  op: QuantumOperation,
97
94
  context_name: str,
@@ -106,7 +103,7 @@ class Emitter(Generic[QuantumStatementT]):
106
103
  )
107
104
  gen_closure = GenerativeClosure(
108
105
  name=func_decl.name,
109
- scope=Scope(parent=self._interpreter._current_scope),
106
+ scope=Scope(parent=self._interpreter._builder.current_scope),
110
107
  blocks={},
111
108
  generative_blocks={
112
109
  block_name: GenerativeQFunc(
@@ -116,15 +113,19 @@ class Emitter(Generic[QuantumStatementT]):
116
113
  },
117
114
  )
118
115
  context = self._interpreter._expand_operation(gen_closure)
119
- self._generative_contexts[context_name] = context
120
116
  op.clear_generative_blocks()
121
117
  return context
122
118
 
123
- def _evaluate_expression(self, expression: Expression) -> Expression:
119
+ def _evaluate_expression(
120
+ self, expression: Expression, preserve_bool_ops: bool = False
121
+ ) -> Expression:
124
122
  evaluated_expression = self._interpreter.evaluate(expression)
125
123
  if isinstance(evaluated_expression.value, sympy.Basic):
126
124
  new_expression = Expression(
127
- expr=translate_sympy_quantum_expression(evaluated_expression.value)
125
+ expr=translate_sympy_quantum_expression(
126
+ evaluated_expression.value,
127
+ preserve_bool_ops=preserve_bool_ops,
128
+ )
128
129
  )
129
130
  else:
130
131
  new_expression = Expression(expr=str(evaluated_expression.value))
@@ -162,3 +163,14 @@ class Emitter(Generic[QuantumStatementT]):
162
163
  defining_function=defining_function,
163
164
  direction=direction,
164
165
  )
166
+
167
+ def capture_handles_in_expression(self, expr: Expression) -> None:
168
+ vrc = VarRefCollector(ignore_duplicated_handles=True)
169
+ vrc.visit(ast.parse(expr.expr))
170
+ handles = dict.fromkeys(
171
+ handle
172
+ for handle in vrc.var_handles
173
+ if isinstance(self._current_scope[handle.name].value, QuantumSymbol)
174
+ )
175
+ for handle in handles:
176
+ self._capture_handle(handle, PortDeclarationDirection.Inout)
@@ -60,13 +60,11 @@ class ExpressionOperationEmitter(
60
60
  ) -> ExpressionOperationT:
61
61
  return op
62
62
 
63
- def _evaluate_op_expression(self, op: ExpressionOperationT) -> Expression:
64
- return self._evaluate_expression(op.expression)
65
-
66
63
  def _evaluate_types_in_expression(
67
- self, op: ExpressionOperationT, expression: Expression
64
+ self, op: ExpressionOperationT
68
65
  ) -> ExpressionOperationT:
69
- op_with_evaluated_types = op.model_copy(update={"expression": expression})
66
+ new_expression = self._evaluate_expression(op.expression)
67
+ op_with_evaluated_types = op.model_copy(update={"expression": new_expression})
70
68
  vrc = VarRefCollector()
71
69
  vrc.visit(ast.parse(op_with_evaluated_types.expression.expr))
72
70
  handles = vrc.var_handles