classiq 0.73.0__py3-none-any.whl → 0.75.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 (49) hide show
  1. classiq/_internals/client.py +9 -10
  2. classiq/analyzer/show_interactive_hack.py +1 -1
  3. classiq/applications/qnn/qlayer.py +9 -0
  4. classiq/interface/_version.py +1 -1
  5. classiq/interface/ast_node.py +5 -2
  6. classiq/interface/compression_utils.py +31 -0
  7. classiq/interface/debug_info/debug_info.py +2 -11
  8. classiq/interface/generator/expressions/proxies/classical/classical_array_proxy.py +5 -5
  9. classiq/interface/generator/expressions/proxies/classical/utils.py +2 -2
  10. classiq/interface/generator/functions/classical_type.py +30 -0
  11. classiq/interface/generator/functions/type_name.py +25 -3
  12. classiq/interface/generator/generated_circuit_data.py +11 -25
  13. classiq/interface/generator/quantum_program.py +14 -0
  14. classiq/interface/generator/synthesis_metadata/synthesis_execution_data.py +10 -13
  15. classiq/interface/helpers/versioned_model.py +12 -0
  16. classiq/interface/ide/visual_model.py +4 -2
  17. classiq/interface/interface_version.py +1 -1
  18. classiq/interface/model/handle_binding.py +12 -0
  19. classiq/interface/model/quantum_lambda_function.py +14 -0
  20. classiq/interface/model/statement_block.py +9 -1
  21. classiq/interface/model/within_apply_operation.py +12 -0
  22. classiq/model_expansions/atomic_expression_functions_defs.py +24 -8
  23. classiq/model_expansions/capturing/captured_vars.py +28 -6
  24. classiq/model_expansions/closure.py +13 -0
  25. classiq/model_expansions/evaluators/argument_types.py +6 -5
  26. classiq/model_expansions/evaluators/type_type_match.py +2 -1
  27. classiq/model_expansions/generative_functions.py +14 -8
  28. classiq/model_expansions/interpreters/base_interpreter.py +10 -13
  29. classiq/model_expansions/interpreters/frontend_generative_interpreter.py +21 -0
  30. classiq/model_expansions/interpreters/generative_interpreter.py +13 -5
  31. classiq/model_expansions/quantum_operations/allocate.py +22 -11
  32. classiq/model_expansions/quantum_operations/assignment_result_processor.py +2 -0
  33. classiq/model_expansions/quantum_operations/call_emitter.py +5 -10
  34. classiq/model_expansions/quantum_operations/emitter.py +1 -5
  35. classiq/model_expansions/quantum_operations/expression_evaluator.py +1 -0
  36. classiq/model_expansions/quantum_operations/handle_evaluator.py +1 -0
  37. classiq/model_expansions/transformers/model_renamer.py +3 -1
  38. classiq/model_expansions/visitors/symbolic_param_inference.py +197 -0
  39. classiq/open_library/functions/__init__.py +2 -0
  40. classiq/open_library/functions/amplitude_amplification.py +5 -3
  41. classiq/open_library/functions/state_preparation.py +95 -2
  42. classiq/qmod/model_state_container.py +11 -8
  43. classiq/qmod/qmod_variable.py +23 -1
  44. classiq/qmod/quantum_function.py +1 -9
  45. classiq/qmod/symbolic_expr.py +8 -2
  46. classiq/qmod/write_qmod.py +5 -1
  47. {classiq-0.73.0.dist-info → classiq-0.75.0.dist-info}/METADATA +2 -1
  48. {classiq-0.73.0.dist-info → classiq-0.75.0.dist-info}/RECORD +49 -47
  49. {classiq-0.73.0.dist-info → classiq-0.75.0.dist-info}/WHEEL +1 -1
@@ -15,6 +15,9 @@ from classiq.interface.generator.expressions.expression_types import (
15
15
  from classiq.interface.generator.expressions.proxies.classical.any_classical_value import (
16
16
  AnyClassicalValue,
17
17
  )
18
+ from classiq.interface.generator.expressions.proxies.classical.classical_array_proxy import (
19
+ ClassicalArrayProxy,
20
+ )
18
21
  from classiq.interface.generator.expressions.proxies.classical.classical_proxy import (
19
22
  ClassicalProxy,
20
23
  )
@@ -170,7 +173,11 @@ def get_field(
170
173
  return AnyClassicalValue(f"({proxy}).{field}")
171
174
  if isinstance(proxy, type) and issubclass(proxy, Enum):
172
175
  return getattr(proxy, field)
173
- if isinstance(proxy, Symbol) and not isinstance(proxy, QmodSizedProxy):
176
+ if (
177
+ isinstance(proxy, Symbol)
178
+ and not isinstance(proxy, QmodSizedProxy)
179
+ and not isinstance(proxy, ClassicalProxy)
180
+ ):
174
181
  raise ClassiqExpansionError(
175
182
  f"Cannot evaluate '{proxy}.{field}': Variable {str(proxy)!r} is not "
176
183
  f"initialized"
@@ -229,7 +236,9 @@ def _is_qmod_value(val: Any) -> bool:
229
236
 
230
237
 
231
238
  def do_subscript(value: Any, index: Any) -> Any:
232
- if not isinstance(value, list) or not isinstance(index, QmodQNumProxy):
239
+ if not isinstance(value, (list, ClassicalArrayProxy)) or not isinstance(
240
+ index, QmodQNumProxy
241
+ ):
233
242
  if isinstance(index, (QmodSizedProxy, QmodStructInstance)):
234
243
  raise ClassiqExpansionError(
235
244
  f"Subscript {value}[{index}] is not supported. Supported subscripts "
@@ -251,15 +260,22 @@ def do_subscript(value: Any, index: Any) -> Any:
251
260
  "Quantum numeric subscript must be an unsigned integer (is_signed=False, "
252
261
  "fraction_digits=0)"
253
262
  )
254
- if len(value) != 2**index.size:
263
+ if isinstance(value, ClassicalArrayProxy):
264
+ length = value.length
265
+ else:
266
+ length = len(value)
267
+ if length != 2**index.size:
255
268
  raise ClassiqExpansionError(
256
269
  f"Quantum numeric subscript size mismatch: The quantum numeric has "
257
- f"{index.size} qubits but the list size is {len(value)} != 2**{index.size}"
270
+ f"{index.size} qubits but the list size is {length} != 2**{index.size}"
271
+ )
272
+ if isinstance(value, ClassicalArrayProxy):
273
+ return AnyClassicalValue(f"{value}[{index}]")
274
+ else:
275
+ return Piecewise(
276
+ *[(item, Eq(index, idx)) for idx, item in enumerate(value[:-1])],
277
+ (value[-1], True),
258
278
  )
259
- return Piecewise(
260
- *[(item, Eq(index, idx)) for idx, item in enumerate(value[:-1])],
261
- (value[-1], True),
262
- )
263
279
 
264
280
 
265
281
  def do_slice(value: Any, lower: Any, upper: Any) -> Any:
@@ -116,6 +116,16 @@ class _CapturedHandle(_Captured):
116
116
  self.defining_function.depth,
117
117
  )
118
118
 
119
+ @property
120
+ def mangled_handle(self) -> HandleBinding:
121
+ return self.handle.rename(
122
+ mangle_captured_var_name(
123
+ self.handle.name,
124
+ self.defining_function.name,
125
+ self.defining_function.depth,
126
+ )
127
+ )
128
+
119
129
  @property
120
130
  def port(self) -> PortDeclaration:
121
131
  return PortDeclaration(
@@ -485,15 +495,14 @@ class CapturedVars:
485
495
  (
486
496
  captured_handle.handle
487
497
  if _same_closure(current_function, captured_handle.defining_function)
488
- else HandleBinding(name=captured_handle.mangled_name)
498
+ else captured_handle.mangled_handle
489
499
  )
490
500
  for captured_handle in self._captured_handles
491
501
  ]
492
502
  return args
493
503
 
494
- def get_captured_mapping(self) -> SymbolRenaming:
495
- mapping: SymbolRenaming
496
- mapping = {
504
+ def get_immediate_captured_mapping(self) -> SymbolRenaming:
505
+ return {
497
506
  captured_handle.handle: [
498
507
  SymbolPart(
499
508
  source_handle=captured_handle.handle,
@@ -504,7 +513,21 @@ class CapturedVars:
504
513
  for captured_handle in self._captured_handles
505
514
  if not captured_handle.is_propagated
506
515
  }
507
- mapping |= {
516
+
517
+ def get_propagated_captured_mapping(self) -> SymbolRenaming:
518
+ return {
519
+ captured_handle.mangled_handle: [
520
+ SymbolPart(
521
+ source_handle=captured_handle.mangled_handle,
522
+ target_var_name=captured_handle.mangled_name,
523
+ target_var_type=captured_handle.quantum_type,
524
+ )
525
+ ]
526
+ for captured_handle in self._captured_handles
527
+ }
528
+
529
+ def get_classical_captured_mapping(self) -> SymbolRenaming:
530
+ return {
508
531
  (handle := HandleBinding(name=captured_classical_var.name)): [
509
532
  HandleRenaming(
510
533
  source_handle=handle,
@@ -514,7 +537,6 @@ class CapturedVars:
514
537
  for captured_classical_var in self._captured_classical_vars
515
538
  if not captured_classical_var.is_propagated
516
539
  }
517
- return mapping
518
540
 
519
541
  def init_var(self, var_name: str, defining_function: "FunctionClosure") -> None:
520
542
  self._handle_states.append((var_name, defining_function, False))
@@ -8,6 +8,15 @@ from typing import Any, Optional
8
8
  from typing_extensions import Self
9
9
 
10
10
  from classiq.interface.exceptions import ClassiqInternalExpansionError
11
+ from classiq.interface.generator.expressions.proxies.classical.classical_proxy import (
12
+ ClassicalProxy,
13
+ )
14
+ from classiq.interface.generator.expressions.proxies.classical.classical_struct_proxy import (
15
+ ClassicalStructProxy,
16
+ )
17
+ from classiq.interface.generator.expressions.proxies.classical.utils import (
18
+ get_proxy_type,
19
+ )
11
20
  from classiq.interface.model.quantum_function_declaration import (
12
21
  NamedParamsQuantumFunctionDeclaration,
13
22
  PositionalArg,
@@ -139,6 +148,10 @@ def _evaluated_arg_to_str(arg: Any) -> str:
139
148
  return _evaluated_one_operand_to_str(arg)
140
149
  if isinstance(arg, list) and arg and isinstance(arg[0], FunctionClosure):
141
150
  return _evaluated_operands_list_to_str(arg)
151
+ if isinstance(arg, ClassicalProxy):
152
+ if isinstance(arg, ClassicalStructProxy):
153
+ return repr(arg.struct_declaration)
154
+ return repr(get_proxy_type(arg))
142
155
  return evaluated_classical_param_to_str(arg)
143
156
 
144
157
 
@@ -34,9 +34,10 @@ def add_information_from_output_arguments(
34
34
  if parameter.direction != PortDeclarationDirection.Output:
35
35
  continue
36
36
 
37
- copy_type_information(
38
- parameter.quantum_type,
39
- argument_as_quantum_symbol.quantum_type,
40
- str(argument_as_quantum_symbol.handle),
41
- )
37
+ if parameter.quantum_type.is_evaluated:
38
+ copy_type_information(
39
+ parameter.quantum_type,
40
+ argument_as_quantum_symbol.quantum_type,
41
+ str(argument_as_quantum_symbol.handle),
42
+ )
42
43
  return args
@@ -85,6 +85,7 @@ def _check_classical_type_match(
85
85
  ) -> None:
86
86
  if (
87
87
  not isinstance(op_param, AnonClassicalParameterDeclaration)
88
- or decl_param.classical_type != op_param.classical_type
88
+ or decl_param.classical_type.clear_flags()
89
+ != op_param.classical_type.clear_flags()
89
90
  ):
90
91
  raise ClassiqExpansionError(error_message)
@@ -81,11 +81,14 @@ def translate_ast_arg_to_python_qmod(param: PositionalArg, evaluated: Evaluated)
81
81
  ]
82
82
  classical_value = evaluated.value
83
83
  if isinstance(classical_value, QmodStructInstance):
84
- return CParamStruct(
85
- expr=param.name,
86
- struct_type=Struct(name=classical_value.struct_declaration.name),
87
- qmodule=QMODULE,
88
- )
84
+ if param.classical_type.is_purely_declarative:
85
+ return CParamStruct(
86
+ expr=param.name,
87
+ struct_type=Struct(name=classical_value.struct_declaration.name),
88
+ qmodule=QMODULE,
89
+ )
90
+ else:
91
+ return get_sdk_compatible_python_object(dict(classical_value.fields))
89
92
  if isinstance(classical_value, ClassicalProxy):
90
93
  return create_param(
91
94
  str(classical_value.handle), get_proxy_type(classical_value), QMODULE
@@ -111,15 +114,18 @@ class _InterpreterExpandable(QFunc):
111
114
  for name, func in self._qmodule.native_defs.items()
112
115
  if name not in self._interpreter._top_level_scope
113
116
  }
117
+ generative_functions = self._qmodule.generative_functions
114
118
  self._interpreter.update_declarative_functions(
115
119
  declarative_functions, self._qmodule
116
120
  )
117
- self._interpreter.update_generative_functions(
118
- self._qmodule.generative_functions
119
- )
121
+ self._interpreter.update_generative_functions(generative_functions)
120
122
  func_decls = self._get_function_declarations()
121
123
  for dec_func in declarative_functions.values():
122
124
  resolve_function_calls(dec_func, func_decls)
125
+ self._interpreter.infer_symbolic_parameters(
126
+ list(declarative_functions.values()),
127
+ [func.func_decl for func in generative_functions.values()],
128
+ )
123
129
  resolve_function_calls(dummy_function, func_decls)
124
130
  stmt = dummy_function.body[-1]
125
131
  with generative_mode_context(False):
@@ -9,14 +9,15 @@ from typing import Any, cast
9
9
  import sympy
10
10
  from pydantic import ValidationError
11
11
 
12
- from classiq.interface.debug_info.debug_info import FunctionDebugInfo
12
+ from classiq.interface.debug_info.debug_info import (
13
+ new_function_debug_info_by_node,
14
+ )
13
15
  from classiq.interface.exceptions import (
14
16
  ClassiqError,
15
17
  ClassiqExpansionError,
16
18
  ClassiqInternalExpansionError,
17
19
  )
18
20
  from classiq.interface.generator.expressions.expression import Expression
19
- from classiq.interface.generator.generated_circuit_data import OperationLevel
20
21
  from classiq.interface.generator.types.compilation_metadata import CompilationMetadata
21
22
  from classiq.interface.model.handle_binding import (
22
23
  FieldHandleBinding,
@@ -237,20 +238,16 @@ class BaseInterpreter:
237
238
  if source_ref is not None
238
239
  else nullcontext()
239
240
  )
240
- if statement.uuid not in self._model.debug_info:
241
- self._model.debug_info[statement.uuid] = FunctionDebugInfo(
242
- name="",
243
- parameters={},
244
- level=OperationLevel.QMOD_STATEMENT,
245
- statement_type=None,
246
- is_allocate_or_free=False,
247
- is_inverse=False,
248
- port_to_passed_variable_map={},
249
- node=statement._as_back_ref(),
250
- )
241
+ self.add_to_debug_info(statement)
251
242
  with error_context, self._builder.source_ref_context(source_ref):
252
243
  self.emit(statement)
253
244
 
245
+ def add_to_debug_info(self, statement: QuantumStatement) -> None:
246
+ if statement.uuid not in self._model.debug_info:
247
+ self._model.debug_info[statement.uuid] = new_function_debug_info_by_node(
248
+ statement # type: ignore[arg-type]
249
+ )
250
+
254
251
  def _expand_operation(self, operation: Closure) -> OperationContext:
255
252
  with self._builder.operation_context(operation) as context:
256
253
  if isinstance(operation, FunctionClosure) and (
@@ -4,21 +4,42 @@ import os
4
4
  from pydantic import ValidationError
5
5
 
6
6
  from classiq.interface.exceptions import ClassiqError
7
+ from classiq.interface.model.allocate import Allocate
8
+ from classiq.interface.model.native_function_definition import NativeFunctionDefinition
7
9
  from classiq.interface.model.quantum_function_call import QuantumFunctionCall
10
+ from classiq.interface.model.quantum_function_declaration import (
11
+ NamedParamsQuantumFunctionDeclaration,
12
+ )
8
13
  from classiq.interface.source_reference import SourceReference
9
14
 
10
15
  from classiq.model_expansions.closure import FunctionClosure, GenerativeFunctionClosure
11
16
  from classiq.model_expansions.interpreters.generative_interpreter import (
12
17
  GenerativeInterpreter,
13
18
  )
19
+ from classiq.model_expansions.quantum_operations.allocate import AllocateEmitter
14
20
  from classiq.model_expansions.quantum_operations.quantum_function_call import (
15
21
  DeclarativeQuantumFunctionCallEmitter,
16
22
  )
17
23
  from classiq.model_expansions.scope import Scope
24
+ from classiq.model_expansions.visitors.symbolic_param_inference import (
25
+ SymbolicParamInference,
26
+ )
18
27
  from classiq.qmod.model_state_container import QMODULE
19
28
 
20
29
 
21
30
  class FrontendGenerativeInterpreter(GenerativeInterpreter):
31
+ def infer_symbolic_parameters(
32
+ self,
33
+ functions: list[NativeFunctionDefinition],
34
+ additional_signatures: (
35
+ list[NamedParamsQuantumFunctionDeclaration] | None
36
+ ) = None,
37
+ ) -> None:
38
+ SymbolicParamInference(functions, additional_signatures).infer()
39
+
40
+ def emit_allocate(self, allocate: Allocate) -> None:
41
+ AllocateEmitter(self, allow_symbolic_size=True).emit(allocate)
42
+
22
43
  def emit_quantum_function_call(self, call: QuantumFunctionCall) -> None:
23
44
  DeclarativeQuantumFunctionCallEmitter(self).emit(call)
24
45
 
@@ -93,17 +93,25 @@ class GenerativeInterpreter(BaseInterpreter):
93
93
  add_generative_functions_to_scope(
94
94
  generative_functions, self._top_level_scope, override_atomic=True
95
95
  )
96
+ self.infer_symbolic_parameters(
97
+ model.functions, [gen_func.func_decl for gen_func in generative_functions]
98
+ )
99
+
100
+ def infer_symbolic_parameters(
101
+ self,
102
+ functions: list[NativeFunctionDefinition],
103
+ additional_signatures: (
104
+ list[NamedParamsQuantumFunctionDeclaration] | None
105
+ ) = None,
106
+ ) -> None:
107
+ pass
96
108
 
97
109
  def evaluate_lambda(self, function: QuantumLambdaFunction) -> Evaluated:
98
- renamed_params = [
99
- param.rename(function.pos_rename_params[idx])
100
- for idx, param in enumerate(function.func_decl.positional_arg_declarations)
101
- ]
102
110
  func_decl = NamedParamsQuantumFunctionDeclaration(
103
111
  name=self._counted_name_allocator.allocate(
104
112
  function.func_decl.name or "<lambda>"
105
113
  ),
106
- positional_arg_declarations=renamed_params,
114
+ positional_arg_declarations=function.named_func_decl.positional_arg_declarations,
107
115
  )
108
116
 
109
117
  closure_class: type[FunctionClosure]
@@ -1,7 +1,10 @@
1
+ from typing import TYPE_CHECKING
2
+
3
+ import sympy
4
+
1
5
  from classiq.interface.debug_info.debug_info import FunctionDebugInfo
2
6
  from classiq.interface.exceptions import ClassiqValueError
3
7
  from classiq.interface.generator.expressions.expression import Expression
4
- from classiq.interface.generator.generated_circuit_data import OperationLevel
5
8
  from classiq.interface.model.allocate import Allocate
6
9
  from classiq.interface.model.handle_binding import NestedHandleBinding
7
10
  from classiq.interface.model.quantum_type import QuantumBitvector
@@ -10,8 +13,17 @@ from classiq.model_expansions.evaluators.quantum_type_utils import copy_type_inf
10
13
  from classiq.model_expansions.quantum_operations.emitter import Emitter
11
14
  from classiq.model_expansions.scope import QuantumSymbol
12
15
 
16
+ if TYPE_CHECKING:
17
+ from classiq.model_expansions.interpreters.base_interpreter import BaseInterpreter
18
+
13
19
 
14
20
  class AllocateEmitter(Emitter[Allocate]):
21
+ def __init__(
22
+ self, interpreter: "BaseInterpreter", allow_symbolic_size: bool = False
23
+ ) -> None:
24
+ super().__init__(interpreter)
25
+ self._allow_symbolic_size = allow_symbolic_size
26
+
15
27
  def emit(self, allocate: Allocate, /) -> bool:
16
28
  target: QuantumSymbol = self._interpreter.evaluate(allocate.target).as_type(
17
29
  QuantumSymbol
@@ -22,10 +34,10 @@ class AllocateEmitter(Emitter[Allocate]):
22
34
  f"Cannot allocate partial quantum variable {str(target.handle)!r}"
23
35
  )
24
36
 
25
- size = self._get_var_size(target, allocate.size)
37
+ size_expr = self._get_var_size(target, allocate.size)
26
38
  allocate = allocate.model_copy(
27
39
  update=dict(
28
- size=Expression(expr=str(size)),
40
+ size=Expression(expr=size_expr),
29
41
  target=target.handle,
30
42
  back_ref=allocate.uuid,
31
43
  )
@@ -34,27 +46,29 @@ class AllocateEmitter(Emitter[Allocate]):
34
46
  self.emit_statement(allocate)
35
47
  return True
36
48
 
37
- def _get_var_size(self, target: QuantumSymbol, size: Expression | None) -> int:
49
+ def _get_var_size(self, target: QuantumSymbol, size: Expression | None) -> str:
38
50
  if size is None:
39
51
  if not target.quantum_type.is_evaluated:
40
52
  raise ClassiqValueError(
41
53
  f"Could not infer the size of variable {str(target.handle)!r}"
42
54
  )
43
- return target.quantum_type.size_in_bits
55
+ return str(target.quantum_type.size_in_bits)
44
56
 
45
57
  size_value = self._interpreter.evaluate(size).value
58
+ if self._allow_symbolic_size and isinstance(size_value, sympy.Basic):
59
+ return str(size_value)
46
60
  if not isinstance(size_value, (int, float)):
47
61
  raise ClassiqValueError(
48
62
  f"The number of allocated qubits must be an integer. Got "
49
63
  f"{str(size_value)!r}"
50
64
  )
51
- size_value = int(size_value)
65
+ size_expr = str(size_value)
52
66
  copy_type_information(
53
- QuantumBitvector(length=Expression(expr=str(size_value))),
67
+ QuantumBitvector(length=Expression(expr=size_expr)),
54
68
  target.quantum_type,
55
69
  str(target.handle),
56
70
  )
57
- return size_value
71
+ return size_expr
58
72
 
59
73
  def _register_debug_info(self, allocate: Allocate) -> None:
60
74
  if (
@@ -67,9 +81,6 @@ class AllocateEmitter(Emitter[Allocate]):
67
81
  parameters["num_qubits"] = allocate.size.expr
68
82
  self._debug_info[allocate.uuid] = FunctionDebugInfo(
69
83
  name="allocate",
70
- parameters=parameters,
71
- level=OperationLevel.QMOD_STATEMENT,
72
- is_allocate_or_free=True,
73
84
  port_to_passed_variable_map={"ARG": str(allocate.target)},
74
85
  node=allocate._as_back_ref(),
75
86
  )
@@ -36,6 +36,8 @@ class AssignmentResultProcessor(Emitter[QuantumAssignmentOperation]):
36
36
 
37
37
  def _update_result_type(self, op: ArithmeticOperation) -> None:
38
38
  expr = self._evaluate_expression(op.expression)
39
+ if len(self._get_classical_vars_in_expression(expr)):
40
+ return
39
41
  symbols = self._get_symbols_in_expression(expr)
40
42
  expr_str = rename_variables(
41
43
  expr.expr,
@@ -24,7 +24,6 @@ from classiq.interface.generator.expressions.proxies.classical.qmod_struct_insta
24
24
  from classiq.interface.generator.functions.port_declaration import (
25
25
  PortDeclarationDirection,
26
26
  )
27
- from classiq.interface.generator.generated_circuit_data import OperationLevel
28
27
  from classiq.interface.model.classical_parameter_declaration import (
29
28
  ClassicalParameterDeclaration,
30
29
  )
@@ -63,7 +62,6 @@ from classiq.model_expansions.quantum_operations.emitter import (
63
62
  from classiq.model_expansions.scope import Evaluated, QuantumSymbol, Scope
64
63
  from classiq.model_expansions.transformers.var_splitter import VarSplitter
65
64
  from classiq.model_expansions.utils.text_utils import are, readable_list, s
66
- from classiq.qmod.builtins.functions import free
67
65
  from classiq.qmod.semantics.validation.signature_validation import (
68
66
  validate_function_signature,
69
67
  )
@@ -188,7 +186,6 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
188
186
  positional_args=new_positional_args,
189
187
  back_ref=self._get_back_ref(propagated_debug_info),
190
188
  )
191
- is_allocate_or_free = new_call.func_name == free.func_decl.name
192
189
 
193
190
  port_to_passed_variable_map = {
194
191
  arg_decl.name: str(evaluated_arg.value.handle)
@@ -197,8 +194,6 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
197
194
  }
198
195
  self._debug_info[new_call.uuid] = FunctionDebugInfo(
199
196
  name=new_call.func_name,
200
- level=OperationLevel.QMOD_FUNCTION_CALL,
201
- is_allocate_or_free=is_allocate_or_free,
202
197
  port_to_passed_variable_map=port_to_passed_variable_map,
203
198
  node=new_call._as_back_ref(),
204
199
  )
@@ -263,11 +258,11 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
263
258
  chain.from_iterable((func_def.positional_arg_declarations, captured_ports))
264
259
  )
265
260
 
266
- if not function_context.is_lambda:
267
- return func_def
268
- func_def.body = self.rewrite(
269
- func_def.body, captured_vars.get_captured_mapping()
270
- )
261
+ rewrite_mapping = dict(captured_vars.get_propagated_captured_mapping())
262
+ if function_context.is_lambda:
263
+ rewrite_mapping |= captured_vars.get_immediate_captured_mapping()
264
+ rewrite_mapping |= captured_vars.get_classical_captured_mapping()
265
+ func_def.body = self.rewrite(func_def.body, rewrite_mapping)
271
266
 
272
267
  return func_def
273
268
 
@@ -13,7 +13,6 @@ import sympy
13
13
 
14
14
  from classiq.interface.debug_info.debug_info import (
15
15
  DebugInfoCollection,
16
- new_function_debug_info_by_node,
17
16
  )
18
17
  from classiq.interface.exceptions import ClassiqInternalExpansionError
19
18
  from classiq.interface.generator.expressions.evaluated_expression import (
@@ -161,10 +160,7 @@ class Emitter(Generic[QuantumStatementT], ABC):
161
160
  self._update_captured_classical_vars(statement)
162
161
  if isinstance(statement, QuantumOperation):
163
162
  self._update_captured_vars(statement)
164
- if statement.uuid not in self._interpreter._model.debug_info:
165
- self._interpreter._model.debug_info[statement.uuid] = (
166
- new_function_debug_info_by_node(statement) # type:ignore[arg-type]
167
- )
163
+ self._interpreter.add_to_debug_info(statement)
168
164
  self._builder.emit_statement(statement)
169
165
 
170
166
  def _update_captured_classical_vars(self, stmt: QuantumStatement) -> None:
@@ -33,5 +33,6 @@ class ExpressionEvaluator(Emitter[QuantumOperation]):
33
33
  op = op.model_copy(
34
34
  update={self._expression_name: evaluated_expression, "back_ref": op.uuid}
35
35
  )
36
+ self._interpreter.add_to_debug_info(op)
36
37
  self._interpreter.emit(op)
37
38
  return True
@@ -24,5 +24,6 @@ class HandleEvaluator(Emitter[QuantumOperation]):
24
24
  op = op.model_copy(
25
25
  update={self._handle_name: evaluated_handle, "back_ref": op.uuid}
26
26
  )
27
+ self._interpreter.add_to_debug_info(op)
27
28
  self._interpreter.emit(op)
28
29
  return True
@@ -92,7 +92,9 @@ class _ReplaceSplitVarsExpressions(ModelTransformer):
92
92
  ) -> QuantumExpressionOperation:
93
93
  op = cast(QuantumExpressionOperation, self.generic_visit(op))
94
94
  previous_var_handles = list(op._var_handles)
95
- op._var_handles = self.visit(op._var_handles)
95
+ op._var_handles = _ReplaceSplitVarsHandles(self._symbol_mapping).visit(
96
+ op._var_handles
97
+ )
96
98
  op._var_types = {
97
99
  new_handle.name: op._var_types.get(
98
100
  new_handle.name, op._var_types[previous_handle.name]