classiq 0.60.0__py3-none-any.whl → 0.61.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 +2 -0
  2. classiq/_internals/client.py +28 -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_optimization/__init__.py +7 -1
  7. classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +2 -0
  8. classiq/applications/combinatorial_optimization/combinatorial_problem.py +197 -0
  9. classiq/applications/finance/finance_model_constructor.py +6 -6
  10. classiq/applications/grover/grover_model_constructor.py +3 -0
  11. classiq/applications/libraries/qmci_library.py +1 -10
  12. classiq/applications/qnn/__init__.py +1 -1
  13. classiq/applications/qnn/datasets/__init__.py +8 -8
  14. classiq/applications/qsvm/qsvm.py +1 -1
  15. classiq/execution/__init__.py +0 -2
  16. classiq/execution/execution_session.py +6 -0
  17. classiq/executor.py +1 -1
  18. classiq/interface/_version.py +1 -1
  19. classiq/interface/backend/backend_preferences.py +12 -12
  20. classiq/interface/executor/execution_preferences.py +1 -1
  21. classiq/interface/generator/application_apis/chemistry_declarations.py +1 -1
  22. classiq/interface/generator/application_apis/finance_declarations.py +2 -2
  23. classiq/interface/generator/arith/arithmetic.py +16 -1
  24. classiq/interface/generator/arith/arithmetic_expression_validator.py +4 -3
  25. classiq/interface/generator/expressions/expression_constants.py +3 -0
  26. classiq/interface/generator/generated_circuit_data.py +58 -20
  27. classiq/interface/generator/model/__init__.py +1 -1
  28. classiq/interface/generator/model/quantum_register.py +3 -3
  29. classiq/interface/generator/standard_gates/controlled_standard_gates.py +20 -32
  30. classiq/interface/ide/visual_model.py +1 -0
  31. classiq/interface/interface_version.py +1 -1
  32. classiq/interface/model/model.py +2 -3
  33. classiq/interface/model/quantum_function_call.py +4 -7
  34. classiq/interface/model/quantum_function_declaration.py +7 -0
  35. classiq/interface/model/quantum_lambda_function.py +10 -1
  36. classiq/interface/model/quantum_type.py +3 -1
  37. classiq/model_expansions/atomic_expression_functions_defs.py +3 -1
  38. classiq/model_expansions/capturing/captured_vars.py +24 -8
  39. classiq/model_expansions/capturing/mangling_utils.py +23 -15
  40. classiq/model_expansions/evaluators/arg_type_match.py +7 -7
  41. classiq/model_expansions/expression_evaluator.py +5 -2
  42. classiq/model_expansions/function_builder.py +21 -4
  43. classiq/model_expansions/generative_functions.py +12 -90
  44. classiq/model_expansions/interpreter.py +58 -11
  45. classiq/model_expansions/quantum_operations/call_emitter.py +19 -10
  46. classiq/model_expansions/quantum_operations/classicalif.py +1 -1
  47. classiq/model_expansions/quantum_operations/control.py +5 -31
  48. classiq/model_expansions/quantum_operations/emitter.py +27 -14
  49. classiq/model_expansions/quantum_operations/expression_operation.py +3 -5
  50. classiq/model_expansions/quantum_operations/inplace_binary_operation.py +57 -15
  51. classiq/model_expansions/quantum_operations/invert.py +1 -6
  52. classiq/model_expansions/quantum_operations/phase.py +2 -5
  53. classiq/model_expansions/quantum_operations/power.py +0 -4
  54. classiq/model_expansions/quantum_operations/quantum_assignment_operation.py +19 -30
  55. classiq/model_expansions/quantum_operations/quantum_function_call.py +3 -1
  56. classiq/model_expansions/quantum_operations/shallow_emitter.py +155 -0
  57. classiq/model_expansions/quantum_operations/within_apply.py +0 -14
  58. classiq/model_expansions/scope.py +10 -4
  59. classiq/model_expansions/scope_initialization.py +0 -11
  60. classiq/model_expansions/sympy_conversion/expression_to_sympy.py +7 -0
  61. classiq/model_expansions/sympy_conversion/sympy_to_python.py +12 -2
  62. classiq/model_expansions/transformers/ast_renamer.py +26 -0
  63. classiq/model_expansions/transformers/var_splitter.py +11 -12
  64. classiq/model_expansions/visitors/variable_references.py +20 -12
  65. classiq/qmod/builtins/classical_execution_primitives.py +6 -6
  66. classiq/qmod/builtins/classical_functions.py +10 -10
  67. classiq/qmod/builtins/functions/__init__.py +89 -103
  68. classiq/qmod/builtins/functions/amplitude_estimation.py +1 -1
  69. classiq/qmod/builtins/functions/arithmetic.py +1 -1
  70. classiq/qmod/builtins/functions/discrete_sine_cosine_transform.py +6 -6
  71. classiq/qmod/builtins/functions/grover.py +5 -5
  72. classiq/qmod/builtins/functions/hea.py +1 -1
  73. classiq/qmod/builtins/functions/linear_pauli_rotation.py +2 -2
  74. classiq/qmod/builtins/functions/modular_exponentiation.py +8 -8
  75. classiq/qmod/builtins/functions/operators.py +1 -1
  76. classiq/qmod/builtins/functions/qaoa_penalty.py +5 -5
  77. classiq/qmod/builtins/functions/qft_functions.py +2 -2
  78. classiq/qmod/builtins/functions/qpe.py +9 -12
  79. classiq/qmod/builtins/functions/qsvt.py +177 -15
  80. classiq/qmod/builtins/functions/state_preparation.py +9 -9
  81. classiq/qmod/builtins/functions/swap_test.py +1 -1
  82. classiq/qmod/builtins/functions/utility_functions.py +2 -2
  83. classiq/qmod/builtins/functions/variational.py +2 -2
  84. classiq/qmod/builtins/operations.py +3 -3
  85. classiq/qmod/builtins/structs.py +9 -9
  86. classiq/qmod/native/pretty_printer.py +17 -19
  87. classiq/qmod/pretty_print/pretty_printer.py +9 -6
  88. classiq/qmod/qmod_variable.py +2 -5
  89. classiq/qmod/quantum_expandable.py +18 -4
  90. classiq/qmod/quantum_function.py +19 -6
  91. classiq/qmod/semantics/static_semantics_visitor.py +34 -16
  92. classiq/qmod/semantics/validation/func_call_validation.py +9 -5
  93. classiq/qmod/semantics/validation/function_name_collisions_validation.py +23 -0
  94. classiq/qmod/symbolic.py +47 -47
  95. {classiq-0.60.0.dist-info → classiq-0.61.0.dist-info}/METADATA +1 -1
  96. {classiq-0.60.0.dist-info → classiq-0.61.0.dist-info}/RECORD +97 -94
  97. classiq/execution/qaoa.py +0 -86
  98. {classiq-0.60.0.dist-info → classiq-0.61.0.dist-info}/WHEEL +0 -0
@@ -1,4 +1,5 @@
1
1
  import ast
2
+ import re
2
3
  from collections.abc import Mapping
3
4
  from enum import EnumMeta
4
5
  from typing import Any, Optional
@@ -12,7 +13,7 @@ from classiq.interface.generator.expressions.evaluated_expression import (
12
13
  )
13
14
  from classiq.interface.generator.expressions.expression import Expression
14
15
  from classiq.interface.generator.expressions.expression_constants import (
15
- CPARAM_EXECUTION_SUFFIX,
16
+ CPARAM_EXECUTION_SUFFIX_PATTERN,
16
17
  )
17
18
  from classiq.interface.generator.expressions.expression_types import ExpressionValue
18
19
  from classiq.interface.generator.expressions.sympy_supported_expressions import (
@@ -99,7 +100,9 @@ def _validate_undefined_vars(
99
100
 
100
101
  # Ignore expanded execution parameters
101
102
  undefined_vars = {
102
- var for var in undefined_vars if not var.endswith(CPARAM_EXECUTION_SUFFIX)
103
+ var
104
+ for var in undefined_vars
105
+ if not re.search(CPARAM_EXECUTION_SUFFIX_PATTERN, var)
103
106
  }
104
107
 
105
108
  if len(undefined_vars) == 1:
@@ -20,6 +20,9 @@ from classiq.interface.model.quantum_function_declaration import (
20
20
  PositionalArg,
21
21
  )
22
22
  from classiq.interface.model.quantum_statement import QuantumStatement
23
+ from classiq.interface.model.variable_declaration_statement import (
24
+ VariableDeclarationStatement,
25
+ )
23
26
  from classiq.interface.source_reference import SourceReference
24
27
 
25
28
  from classiq.model_expansions.capturing.captured_vars import (
@@ -37,6 +40,14 @@ class Block:
37
40
  statements: list[QuantumStatement] = field(default_factory=list)
38
41
  captured_vars: CapturedVars = field(default_factory=CapturedVars)
39
42
 
43
+ @property
44
+ def variable_declarations(self) -> list[VariableDeclarationStatement]:
45
+ return [
46
+ stmt
47
+ for stmt in self.statements
48
+ if isinstance(stmt, VariableDeclarationStatement)
49
+ ]
50
+
40
51
 
41
52
  @dataclass
42
53
  class OperationContext(Generic[ClosureType]):
@@ -123,9 +134,13 @@ class OperationBuilder:
123
134
  not isinstance(self.current_operation, FunctionClosure)
124
135
  and self.current_operation.name != WITHIN_APPLY_NAME
125
136
  ):
126
- validate_captured_directions(captured_vars)
137
+ validate_captured_directions(
138
+ captured_vars.filter_vars(
139
+ self.current_function, self.current_block.variable_declarations
140
+ )
141
+ )
127
142
  self.current_operation.captured_vars.update(
128
- captured_vars.filter(self.current_function)
143
+ captured_vars.filter_vars(self.current_function)
129
144
  )
130
145
  self._blocks.pop()
131
146
 
@@ -149,7 +164,7 @@ class OperationBuilder:
149
164
  return
150
165
  within_captured_vars = self._operations[-1].blocks["within"].captured_vars
151
166
  self.current_operation.captured_vars.update(
152
- within_captured_vars.filter(self.current_function).negate()
167
+ within_captured_vars.filter_vars(self.current_function).negate()
153
168
  )
154
169
 
155
170
  def _propagate_captured_vars(self) -> None:
@@ -160,7 +175,9 @@ class OperationBuilder:
160
175
  if len(self._operations) < 2:
161
176
  return
162
177
  parent_block = self._operations[-2].blocks[self._blocks[-1]]
163
- parent_block.captured_vars.update(captured_vars.filter(self.parent_function))
178
+ parent_block.captured_vars.update(
179
+ captured_vars.filter_vars(self.parent_function)
180
+ )
164
181
 
165
182
  @contextmanager
166
183
  def source_ref_context(
@@ -1,31 +1,23 @@
1
1
  from collections.abc import Mapping
2
2
  from typing import TYPE_CHECKING, Any
3
3
 
4
- from classiq.interface.exceptions import ClassiqInternalExpansionError
5
4
  from classiq.interface.generator.expressions.qmod_struct_instance import (
6
5
  QmodStructInstance,
7
6
  )
8
7
  from classiq.interface.generator.functions.type_name import Struct
9
- from classiq.interface.generator.visitor import Visitor
10
8
  from classiq.interface.helpers.pydantic_model_helpers import nameables_to_dict
11
9
  from classiq.interface.model.native_function_definition import NativeFunctionDefinition
12
10
  from classiq.interface.model.port_declaration import PortDeclaration
13
- from classiq.interface.model.quantum_function_call import ArgValue, QuantumFunctionCall
14
11
  from classiq.interface.model.quantum_function_declaration import (
15
12
  PositionalArg,
16
13
  QuantumFunctionDeclaration,
17
14
  QuantumOperandDeclaration,
18
15
  )
19
- from classiq.interface.model.quantum_lambda_function import (
20
- QuantumCallable,
21
- QuantumLambdaFunction,
22
- )
23
16
  from classiq.interface.model.quantum_statement import QuantumStatement
24
17
 
25
18
  from classiq.model_expansions.closure import (
26
19
  FunctionClosure,
27
20
  GenerativeClosure,
28
- GenerativeFunctionClosure,
29
21
  )
30
22
  from classiq.model_expansions.scope import Evaluated, QuantumSymbol
31
23
  from classiq.qmod.generative import generative_mode_context, set_frontend_interpreter
@@ -35,7 +27,6 @@ from classiq.qmod.qmod_variable import QNum, _create_qvar_for_qtype
35
27
  from classiq.qmod.quantum_callable import QCallable
36
28
  from classiq.qmod.quantum_expandable import (
37
29
  QExpandable,
38
- QLambdaFunction,
39
30
  QTerminalCallable,
40
31
  )
41
32
  from classiq.qmod.quantum_function import QFunc
@@ -71,12 +62,18 @@ def translate_ast_arg_to_python_qmod(param: PositionalArg, evaluated: Evaluated)
71
62
  )
72
63
  if isinstance(param, QuantumOperandDeclaration):
73
64
  if param.is_list:
74
- return QTerminalCallable(
75
- QuantumOperandDeclaration(
76
- name=param.name,
77
- positional_arg_declarations=param.positional_arg_declarations,
78
- is_list=True,
79
- ),
65
+ func_list: list[FunctionClosure] = evaluated.as_type(list)
66
+ return LenList(
67
+ [
68
+ QTerminalCallable(
69
+ QuantumOperandDeclaration(
70
+ name=param.name,
71
+ positional_arg_declarations=param.positional_arg_declarations,
72
+ ),
73
+ index_=idx,
74
+ )
75
+ for idx in range(len(func_list))
76
+ ]
80
77
  )
81
78
  else:
82
79
  func = evaluated.as_type(FunctionClosure)
@@ -143,78 +140,3 @@ def emit_generative_statements(
143
140
  True
144
141
  ):
145
142
  generative_function._py_callable(*python_qmod_args)
146
-
147
-
148
- def emit_operands_as_declarative(
149
- interpreter: "Interpreter", param: PositionalArg, arg: Evaluated
150
- ) -> ArgValue:
151
- if not isinstance(param, QuantumOperandDeclaration):
152
- return arg.emit()
153
- value = arg.value
154
- if isinstance(value, list):
155
- return [
156
- _expand_operand_as_declarative(interpreter, param, item) for item in value
157
- ]
158
- if isinstance(value, GenerativeFunctionClosure):
159
- return _expand_operand_as_declarative(interpreter, param, value)
160
- if isinstance(value, FunctionClosure):
161
- if value.is_lambda:
162
- raise ClassiqInternalExpansionError
163
- _register_declarative_function(interpreter, value.name)
164
- return value.name
165
- raise ClassiqInternalExpansionError
166
-
167
-
168
- def _expand_operand_as_declarative(
169
- interpreter: "Interpreter",
170
- param: QuantumOperandDeclaration,
171
- arg: GenerativeFunctionClosure,
172
- ) -> QuantumCallable:
173
- if not arg.is_lambda:
174
- _register_declarative_function(interpreter, arg.name)
175
- return arg.name
176
- val = QLambdaFunction(param, arg.generative_blocks["body"]._py_callable)
177
- with generative_mode_context(False):
178
- val.expand()
179
- _DecFuncVisitor(interpreter).visit(val.body)
180
- qlambda = QuantumLambdaFunction(
181
- pos_rename_params=val.infer_rename_params(),
182
- body=val.body,
183
- )
184
- qlambda.set_op_decl(param)
185
- return qlambda
186
-
187
-
188
- def _register_declarative_function(interpreter: "Interpreter", func_name: str) -> None:
189
- if func_name in nameables_to_dict(list(interpreter._expanded_functions.values())):
190
- return
191
-
192
- for user_gen_func in interpreter._generative_functions:
193
- if user_gen_func.func_decl.name == func_name:
194
- break
195
- else:
196
- return
197
-
198
- with generative_mode_context(False):
199
- dec_func = QFunc(user_gen_func._py_callable)
200
- dec_func.expand()
201
- dec_func_def = QMODULE.native_defs[func_name]
202
- interpreter._expanded_functions[func_name] = dec_func_def
203
- _DecFuncVisitor(interpreter).visit(dec_func_def)
204
-
205
-
206
- class _DecFuncVisitor(Visitor):
207
- def __init__(self, interpreter: "Interpreter"):
208
- self._interpreter = interpreter
209
-
210
- def visit_QuantumFunctionCall(self, call: QuantumFunctionCall) -> None:
211
- _register_declarative_function(self._interpreter, call.func_name)
212
- for arg in call.positional_args:
213
- if isinstance(arg, str):
214
- arg = [arg]
215
- if isinstance(arg, list):
216
- for possible_func_name in arg:
217
- if isinstance(possible_func_name, str):
218
- _register_declarative_function(
219
- self._interpreter, possible_func_name
220
- )
@@ -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,
@@ -108,7 +116,9 @@ class Interpreter:
108
116
  self._builder = OperationBuilder(self._top_level_scope)
109
117
  self._expanded_functions: dict[str, NativeFunctionDefinition] = {}
110
118
 
111
- self._main_renamer: ExpressionRenamer = get_main_renamer(self._model.functions)
119
+ self._main_renamer: Optional[ExpressionRenamer] = (
120
+ get_main_renamer(self._model.functions) if self._is_frontend else None
121
+ )
112
122
 
113
123
  if generative_functions is None:
114
124
  generative_functions = []
@@ -280,11 +290,21 @@ class Interpreter:
280
290
 
281
291
  @emit.register
282
292
  def emit_quantum_assignment_operation(self, op: QuantumAssignmentOperation) -> None:
283
- QuantumAssignmentOperationEmitter(self).emit(op)
293
+ if self._is_frontend:
294
+ ShallowEmitter(
295
+ self, "assignment_operation", components=["expression", "result_var"]
296
+ ).emit(op)
297
+ else:
298
+ QuantumAssignmentOperationEmitter(self).emit(op)
284
299
 
285
300
  @emit.register
286
- def emit_inplace_binary_operation(self, operation: InplaceBinaryOperation) -> None:
287
- InplaceBinaryOperationEmitter(self).emit(operation)
301
+ def emit_inplace_binary_operation(self, op: InplaceBinaryOperation) -> None:
302
+ if self._is_frontend:
303
+ ShallowEmitter(
304
+ self, "inplace_binary_operation", components=["target", "value"]
305
+ ).emit(op)
306
+ else:
307
+ InplaceBinaryOperationEmitter(self).emit(op)
288
308
 
289
309
  @emit.register
290
310
  def emit_variable_declaration(
@@ -298,11 +318,21 @@ class Interpreter:
298
318
 
299
319
  @emit.register
300
320
  def emit_within_apply(self, within_apply: WithinApply) -> None:
301
- WithinApplyEmitter(self).emit(within_apply)
321
+ if self._is_frontend:
322
+ ShallowEmitter(
323
+ self,
324
+ WITHIN_APPLY_NAME,
325
+ components=["within", "apply", "compute", "action"],
326
+ ).emit(within_apply)
327
+ else:
328
+ WithinApplyEmitter(self).emit(within_apply)
302
329
 
303
330
  @emit.register
304
331
  def emit_invert(self, invert: Invert) -> None:
305
- InvertEmitter(self).emit(invert)
332
+ if self._is_frontend:
333
+ ShallowEmitter(self, INVERT_OPERATOR_NAME, components=["body"]).emit(invert)
334
+ else:
335
+ InvertEmitter(self).emit(invert)
306
336
 
307
337
  @emit.register
308
338
  def emit_repeat(self, repeat: Repeat) -> None:
@@ -310,15 +340,32 @@ class Interpreter:
310
340
 
311
341
  @emit.register
312
342
  def emit_control(self, control: Control) -> None:
313
- ControlEmitter(self).emit(control)
343
+ if self._is_frontend:
344
+ ShallowEmitter(
345
+ self,
346
+ CONTROL_OPERATOR_NAME,
347
+ components=["expression", "body", "else_block"],
348
+ ).emit(control)
349
+ else:
350
+ ControlEmitter(self).emit(control)
314
351
 
315
352
  @emit.register
316
353
  def emit_power(self, power: Power) -> None:
317
- PowerEmitter(self).emit(power)
354
+ if self._is_frontend:
355
+ ShallowEmitter(
356
+ self, CONTROL_OPERATOR_NAME, components=["power", "body"]
357
+ ).emit(power)
358
+ else:
359
+ PowerEmitter(self).emit(power)
318
360
 
319
361
  @emit.register
320
362
  def emit_phase(self, phase: PhaseOperation) -> None:
321
- PhaseEmitter(self).emit(phase)
363
+ if self._is_frontend:
364
+ ShallowEmitter(self, "phase", components=["expression", "theta"]).emit(
365
+ phase
366
+ )
367
+ else:
368
+ PhaseEmitter(self).emit(phase)
322
369
 
323
370
  def _expand_block(self, block: Sequence[QuantumStatement], block_name: str) -> None:
324
371
  with self._builder.block_context(block_name):
@@ -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,
@@ -73,14 +73,22 @@ 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:
85
93
  function = function.set_depth(self._builder.current_function.depth + 1)
86
94
  function = function.copy_scope()
@@ -144,10 +152,14 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
144
152
  for arg_decl, evaluated_arg in zip(new_positional_arg_decls, evaluated_args)
145
153
  if isinstance(arg_decl, PortDeclaration)
146
154
  }
147
- self._interpreter._model.debug_info[new_call.uuid] = FunctionDebugInfo(
155
+ self._debug_info[new_call.uuid] = FunctionDebugInfo(
148
156
  name=new_call.func_name,
149
157
  level=OperationLevel.QMOD_FUNCTION_CALL,
150
- parameters=parameters,
158
+ parameters=(
159
+ parameters
160
+ if propagated_debug_info is None
161
+ else propagated_debug_info.parameters
162
+ ),
151
163
  is_allocate_or_free=is_allocate_or_free,
152
164
  port_to_passed_variable_map=port_to_passed_variable_map,
153
165
  )
@@ -203,10 +215,7 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
203
215
  new_positional_arg_decls, evaluated_args
204
216
  )
205
217
  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
- ]
218
+ return [arg.emit() for arg in evaluated_args]
210
219
 
211
220
  positional_args = [
212
221
  arg.emit() for arg in evaluated_args if isinstance(arg.value, QuantumSymbol)
@@ -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:
@@ -55,20 +57,12 @@ class Emitter(Generic[QuantumStatementT]):
55
57
  self._functions_compilation_metadata = (
56
58
  self._interpreter._model.functions_compilation_metadata
57
59
  )
58
- self._generative_contexts: dict[str, OperationContext] = {}
59
60
 
60
61
  @abstractmethod
61
62
  def emit(self, statement: QuantumStatementT, /) -> None:
62
63
  pass
63
64
 
64
65
  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
66
  return self._interpreter._expand_operation(closure)
73
67
 
74
68
  @property
@@ -91,7 +85,11 @@ class Emitter(Generic[QuantumStatementT]):
91
85
  def _counted_name_allocator(self) -> CountedNameAllocator:
92
86
  return self._interpreter._counted_name_allocator
93
87
 
94
- def _register_generative_context(
88
+ @property
89
+ def _debug_info(self) -> DebugInfoCollection:
90
+ return self._interpreter._model.debug_info
91
+
92
+ def _expand_generative_context(
95
93
  self,
96
94
  op: QuantumOperation,
97
95
  context_name: str,
@@ -116,15 +114,19 @@ class Emitter(Generic[QuantumStatementT]):
116
114
  },
117
115
  )
118
116
  context = self._interpreter._expand_operation(gen_closure)
119
- self._generative_contexts[context_name] = context
120
117
  op.clear_generative_blocks()
121
118
  return context
122
119
 
123
- def _evaluate_expression(self, expression: Expression) -> Expression:
120
+ def _evaluate_expression(
121
+ self, expression: Expression, preserve_bool_ops: bool = False
122
+ ) -> Expression:
124
123
  evaluated_expression = self._interpreter.evaluate(expression)
125
124
  if isinstance(evaluated_expression.value, sympy.Basic):
126
125
  new_expression = Expression(
127
- expr=translate_sympy_quantum_expression(evaluated_expression.value)
126
+ expr=translate_sympy_quantum_expression(
127
+ evaluated_expression.value,
128
+ preserve_bool_ops=preserve_bool_ops,
129
+ )
128
130
  )
129
131
  else:
130
132
  new_expression = Expression(expr=str(evaluated_expression.value))
@@ -162,3 +164,14 @@ class Emitter(Generic[QuantumStatementT]):
162
164
  defining_function=defining_function,
163
165
  direction=direction,
164
166
  )
167
+
168
+ def capture_handles_in_expression(self, expr: Expression) -> None:
169
+ vrc = VarRefCollector(ignore_duplicated_handles=True)
170
+ vrc.visit(ast.parse(expr.expr))
171
+ handles = dict.fromkeys(
172
+ handle
173
+ for handle in vrc.var_handles
174
+ if isinstance(self._current_scope[handle.name].value, QuantumSymbol)
175
+ )
176
+ for handle in handles:
177
+ 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