classiq 0.66.1__py3-none-any.whl → 0.68.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 (89) hide show
  1. classiq/_internals/api_wrapper.py +5 -1
  2. classiq/_internals/async_utils.py +1 -1
  3. classiq/_internals/authentication/password_manager.py +1 -1
  4. classiq/_internals/client.py +1 -1
  5. classiq/applications/finance/finance_model_constructor.py +9 -0
  6. classiq/applications/grover/grover_model_constructor.py +10 -0
  7. classiq/applications/qnn/qlayer.py +8 -2
  8. classiq/applications/qsvm/qsvm_model_constructor.py +9 -0
  9. classiq/execution/execution_session.py +7 -3
  10. classiq/executor.py +7 -2
  11. classiq/interface/_version.py +1 -1
  12. classiq/interface/ast_node.py +1 -1
  13. classiq/interface/chemistry/operator.py +1 -1
  14. classiq/interface/debug_info/debug_info.py +27 -0
  15. classiq/interface/exceptions.py +2 -5
  16. classiq/interface/generator/arith/argument_utils.py +1 -1
  17. classiq/interface/generator/arith/arithmetic.py +99 -2
  18. classiq/interface/generator/arith/arithmetic_expression_parser.py +1 -1
  19. classiq/interface/generator/arith/binary_ops.py +3 -0
  20. classiq/interface/generator/function_param_list_without_self_reference.py +2 -0
  21. classiq/interface/generator/functions/type_name.py +2 -2
  22. classiq/interface/generator/generated_circuit_data.py +38 -3
  23. classiq/interface/generator/hardware_efficient_ansatz.py +1 -1
  24. classiq/interface/generator/hva.py +1 -1
  25. classiq/interface/generator/model/preferences/preferences.py +8 -1
  26. classiq/interface/generator/quantum_program.py +18 -1
  27. classiq/interface/generator/reset.py +14 -0
  28. classiq/interface/generator/types/enum_declaration.py +33 -2
  29. classiq/interface/generator/ucc.py +1 -1
  30. classiq/interface/interface_version.py +1 -1
  31. classiq/interface/model/classical_if.py +2 -2
  32. classiq/interface/model/control.py +2 -2
  33. classiq/interface/model/invert.py +2 -2
  34. classiq/interface/model/power.py +2 -2
  35. classiq/interface/model/quantum_function_call.py +4 -0
  36. classiq/interface/model/quantum_statement.py +13 -0
  37. classiq/interface/model/repeat.py +2 -2
  38. classiq/interface/model/statement_block.py +1 -1
  39. classiq/interface/model/within_apply_operation.py +2 -2
  40. classiq/model_expansions/atomic_expression_functions_defs.py +2 -1
  41. classiq/model_expansions/capturing/captured_vars.py +184 -54
  42. classiq/model_expansions/closure.py +6 -3
  43. classiq/model_expansions/evaluators/control.py +14 -38
  44. classiq/model_expansions/function_builder.py +19 -14
  45. classiq/model_expansions/generative_functions.py +7 -11
  46. classiq/model_expansions/interpreters/base_interpreter.py +14 -5
  47. classiq/model_expansions/interpreters/generative_interpreter.py +87 -26
  48. classiq/model_expansions/quantum_operations/allocate.py +9 -3
  49. classiq/model_expansions/quantum_operations/assignment_result_processor.py +52 -0
  50. classiq/model_expansions/quantum_operations/bind.py +67 -14
  51. classiq/model_expansions/quantum_operations/block_evaluator.py +76 -0
  52. classiq/model_expansions/quantum_operations/call_emitter.py +79 -10
  53. classiq/model_expansions/quantum_operations/classicalif.py +10 -6
  54. classiq/model_expansions/quantum_operations/composite_emitter.py +27 -0
  55. classiq/model_expansions/quantum_operations/emitter.py +24 -3
  56. classiq/model_expansions/quantum_operations/expression_evaluator.py +33 -0
  57. classiq/model_expansions/quantum_operations/handle_evaluator.py +28 -0
  58. classiq/model_expansions/quantum_operations/quantum_function_call.py +3 -2
  59. classiq/model_expansions/quantum_operations/repeat.py +9 -3
  60. classiq/model_expansions/quantum_operations/variable_decleration.py +13 -2
  61. classiq/model_expansions/sympy_conversion/expression_to_sympy.py +1 -1
  62. classiq/open_library/functions/__init__.py +1 -2
  63. classiq/open_library/functions/amplitude_amplification.py +12 -9
  64. classiq/open_library/functions/discrete_sine_cosine_transform.py +16 -13
  65. classiq/open_library/functions/grover.py +11 -15
  66. classiq/open_library/functions/modular_exponentiation.py +7 -13
  67. classiq/open_library/functions/state_preparation.py +16 -17
  68. classiq/open_library/functions/swap_test.py +1 -1
  69. classiq/open_library/functions/utility_functions.py +10 -2
  70. classiq/qmod/builtins/functions/__init__.py +3 -0
  71. classiq/qmod/builtins/functions/chemistry.py +6 -38
  72. classiq/qmod/builtins/functions/mid_circuit_measurement.py +15 -0
  73. classiq/qmod/quantum_expandable.py +30 -6
  74. classiq/qmod/quantum_function.py +4 -0
  75. classiq/qmod/semantics/annotation/call_annotation.py +8 -2
  76. classiq/qmod/semantics/annotation/model_annotation.py +9 -0
  77. classiq/qmod/semantics/error_manager.py +0 -6
  78. classiq/qmod/semantics/static_semantics_visitor.py +0 -347
  79. classiq/qmod/semantics/validation/types_validation.py +1 -1
  80. classiq/qmod/symbolic.py +2 -1
  81. classiq/qmod/utilities.py +2 -1
  82. classiq/qmod/write_qmod.py +10 -7
  83. classiq/synthesis.py +20 -7
  84. {classiq-0.66.1.dist-info → classiq-0.68.0.dist-info}/METADATA +1 -1
  85. {classiq-0.66.1.dist-info → classiq-0.68.0.dist-info}/RECORD +86 -81
  86. classiq/model_expansions/quantum_operations/shallow_emitter.py +0 -166
  87. classiq/qmod/semantics/validation/func_call_validation.py +0 -99
  88. classiq/qmod/semantics/validation/handle_validation.py +0 -85
  89. {classiq-0.66.1.dist-info → classiq-0.68.0.dist-info}/WHEEL +0 -0
@@ -4,6 +4,10 @@ from sympy import Equality
4
4
  from sympy.core.numbers import Number
5
5
 
6
6
  from classiq.interface.exceptions import ClassiqExpansionError
7
+ from classiq.interface.generator.arith.argument_utils import (
8
+ unsigned_integer_interpretation,
9
+ )
10
+ from classiq.interface.generator.arith.register_user_input import RegisterArithmeticInfo
7
11
  from classiq.interface.generator.expressions.qmod_qscalar_proxy import (
8
12
  QmodQNumProxy,
9
13
  QmodQScalarProxy,
@@ -31,14 +35,15 @@ def resolve_num_condition(condition: Equality) -> tuple[QmodSizedProxy, str]:
31
35
  def _calculate_ctrl_state(ctrl: QmodSizedProxy, ctrl_val: float) -> str:
32
36
  is_signed, fraction_places = _get_numeric_attributes(ctrl)
33
37
 
34
- integer_ctrl_val = _get_integer_ctrl_val(ctrl, ctrl_val, fraction_places)
35
-
36
- _validate_control_value_sign(ctrl, integer_ctrl_val, is_signed)
37
- _validate_control_var_qubits(
38
- ctrl, integer_ctrl_val, is_signed, fraction_places, ctrl_val
38
+ reg = RegisterArithmeticInfo(
39
+ size=ctrl.size, is_signed=is_signed, fraction_places=fraction_places
39
40
  )
41
+ uint_ctrl_val = unsigned_integer_interpretation(ctrl_val, reg)
40
42
 
41
- return _to_twos_complement(integer_ctrl_val, ctrl.size)
43
+ _validate_control_value_sign(ctrl, ctrl_val, is_signed)
44
+ _validate_control_var_qubits(ctrl, uint_ctrl_val, fraction_places, ctrl_val)
45
+
46
+ return _to_twos_complement(uint_ctrl_val, ctrl.size)
42
47
 
43
48
 
44
49
  def _get_numeric_attributes(ctrl: QmodSizedProxy) -> tuple[bool, int]:
@@ -49,20 +54,8 @@ def _get_numeric_attributes(ctrl: QmodSizedProxy) -> tuple[bool, int]:
49
54
  )
50
55
 
51
56
 
52
- def _get_integer_ctrl_val(
53
- ctrl: QmodSizedProxy, ctrl_val: float, fraction_places: int
54
- ) -> int:
55
- unfractioned_ctrl_val = ctrl_val * 2**fraction_places
56
- if unfractioned_ctrl_val != int(unfractioned_ctrl_val):
57
- raise ClassiqExpansionError(
58
- f"Variable {str(ctrl)!r} doesne't have enough fraction digits to "
59
- f"represent control value {ctrl_val}"
60
- )
61
- return int(unfractioned_ctrl_val)
62
-
63
-
64
57
  def _validate_control_value_sign(
65
- ctrl: QmodSizedProxy, ctrl_val: int, is_signed: bool
58
+ ctrl: QmodSizedProxy, ctrl_val: float, is_signed: bool
66
59
  ) -> None:
67
60
  if not is_signed and ctrl_val < 0:
68
61
  raise ClassiqExpansionError(
@@ -74,18 +67,17 @@ def _validate_control_value_sign(
74
67
  def _validate_control_var_qubits(
75
68
  ctrl: QmodSizedProxy,
76
69
  ctrl_val: int,
77
- is_signed: bool,
78
70
  fraction_places: int,
79
71
  orig_ctrl_val: float,
80
72
  ) -> None:
81
- required_qubits = _min_bit_length(ctrl_val, is_signed)
73
+ required_qubits = _min_unsigned_bit_length(ctrl_val)
82
74
  fraction_places_message = (
83
75
  f" with {fraction_places} fraction digits" if fraction_places else ""
84
76
  )
85
77
  if ctrl.size < required_qubits:
86
78
  raise ClassiqExpansionError(
87
79
  f"Variable {str(ctrl)!r} has {ctrl.size} qubits{fraction_places_message} but control value "
88
- f"{str(orig_ctrl_val if fraction_places else ctrl_val)!r} requires at least {required_qubits} qubits{fraction_places_message}"
80
+ f"{str(orig_ctrl_val if fraction_places else int(orig_ctrl_val))!r} requires at least {required_qubits} qubits{fraction_places_message}"
89
81
  )
90
82
 
91
83
 
@@ -116,22 +108,6 @@ def _min_unsigned_bit_length(number: int) -> int:
116
108
  raise e
117
109
 
118
110
 
119
- def _min_signed_bit_length(number: int) -> int:
120
- pos_val = abs(number)
121
- is_whole = pos_val & (pos_val - 1) == 0
122
- if number <= 0 and is_whole:
123
- return _min_unsigned_bit_length(pos_val)
124
- return _min_unsigned_bit_length(pos_val) + 1
125
-
126
-
127
- def _min_bit_length(number: int, is_signed: bool) -> int:
128
- return (
129
- _min_signed_bit_length(number)
130
- if is_signed
131
- else _min_unsigned_bit_length(number)
132
- )
133
-
134
-
135
111
  def _to_twos_complement(value: int, bits: int) -> str:
136
112
  if value >= 0:
137
113
  return bin(value)[2:].zfill(bits)[::-1]
@@ -28,6 +28,7 @@ from classiq.interface.source_reference import SourceReference
28
28
  from classiq.model_expansions.capturing.captured_vars import (
29
29
  CapturedVars,
30
30
  validate_captured_directions,
31
+ validate_end_state,
31
32
  )
32
33
  from classiq.model_expansions.closure import (
33
34
  Closure,
@@ -142,7 +143,9 @@ class OperationBuilder:
142
143
  @contextmanager
143
144
  def block_context(self, block_name: str) -> Iterator[None]:
144
145
  self._blocks.append(block_name)
145
- self._operations[-1].blocks[block_name] = Block()
146
+ block = Block()
147
+ block.captured_vars.set_parent(self.current_operation.captured_vars)
148
+ self._operations[-1].blocks[block_name] = block
146
149
  yield
147
150
  captured_vars = self.current_block.captured_vars
148
151
  if (
@@ -150,14 +153,12 @@ class OperationBuilder:
150
153
  and self.current_operation.name != WITHIN_APPLY_NAME
151
154
  ):
152
155
  validate_captured_directions(
153
- captured_vars.filter_vars(
154
- self.current_function, self.current_block.variable_declarations
156
+ captured_vars.filter_var_decls(
157
+ self.current_block.variable_declarations
155
158
  ),
156
159
  report_outin=False,
157
160
  )
158
- self.current_operation.captured_vars.update(
159
- captured_vars.filter_vars(self.current_function)
160
- )
161
+ self.current_operation.captured_vars.update(captured_vars)
161
162
  self._blocks.pop()
162
163
 
163
164
  @contextmanager
@@ -167,11 +168,17 @@ class OperationBuilder:
167
168
  context: OperationContext
168
169
  if isinstance(original_operation, FunctionClosure):
169
170
  context = FunctionContext.create(original_operation)
171
+ context.closure.captured_vars.init_params(original_operation)
170
172
  else:
171
173
  context = OperationContext(closure=original_operation)
174
+ context.closure.captured_vars.set_parent(self.current_block.captured_vars)
172
175
  self._operations.append(context)
173
176
  yield context
174
177
  self._finalize_within_apply()
178
+ if isinstance(self.current_operation, FunctionClosure):
179
+ validate_end_state(
180
+ self.current_operation, self.current_operation.captured_vars
181
+ )
175
182
  self._propagate_captured_vars()
176
183
  self._operations.pop()
177
184
 
@@ -179,21 +186,19 @@ class OperationBuilder:
179
186
  if self.current_operation.name != WITHIN_APPLY_NAME:
180
187
  return
181
188
  within_captured_vars = self._operations[-1].blocks["within"].captured_vars
182
- self.current_operation.captured_vars.update(
183
- within_captured_vars.filter_vars(self.current_function).negate()
184
- )
189
+ self.current_operation.captured_vars.update(within_captured_vars.negate())
185
190
 
186
191
  def _propagate_captured_vars(self) -> None:
187
192
  captured_vars = self.current_operation.captured_vars
188
193
  if isinstance(self.current_operation, FunctionClosure):
189
- captured_vars = captured_vars.set_propagated()
190
- validate_captured_directions(captured_vars)
194
+ captured_vars = captured_vars.filter_vars(
195
+ self.current_function
196
+ ).set_propagated()
197
+ validate_captured_directions(captured_vars)
191
198
  if len(self._operations) < 2:
192
199
  return
193
200
  parent_block = self._operations[-2].blocks[self._blocks[-1]]
194
- parent_block.captured_vars.update(
195
- captured_vars.filter_vars(self.parent_function)
196
- )
201
+ parent_block.captured_vars.update(captured_vars)
197
202
 
198
203
  @contextmanager
199
204
  def source_ref_context(
@@ -24,9 +24,7 @@ from classiq.qmod.generative import generative_mode_context, set_frontend_interp
24
24
  from classiq.qmod.model_state_container import QMODULE
25
25
  from classiq.qmod.qmod_parameter import CParamStruct
26
26
  from classiq.qmod.qmod_variable import QNum, _create_qvar_for_qtype
27
- from classiq.qmod.quantum_callable import QCallable
28
27
  from classiq.qmod.quantum_expandable import (
29
- QExpandable,
30
28
  QTerminalCallable,
31
29
  )
32
30
  from classiq.qmod.quantum_function import QFunc
@@ -147,12 +145,10 @@ def emit_generative_statements(
147
145
  translate_ast_arg_to_python_qmod(param, arg)
148
146
  for param, arg in zip(operation.positional_arg_declarations, args)
149
147
  ]
150
- interpreter_expandable = _InterpreterExpandable(interpreter)
151
- QExpandable.STACK.append(interpreter_expandable)
152
- QCallable.CURRENT_EXPANDABLE = interpreter_expandable
153
- set_frontend_interpreter(interpreter)
154
- for block_name, generative_function in operation.generative_blocks.items():
155
- with interpreter._builder.block_context(block_name), generative_mode_context(
156
- True
157
- ):
158
- generative_function._py_callable(*python_qmod_args)
148
+ with _InterpreterExpandable(interpreter):
149
+ set_frontend_interpreter(interpreter)
150
+ for block_name, generative_function in operation.generative_blocks.items():
151
+ with interpreter._builder.block_context(
152
+ block_name
153
+ ), generative_mode_context(True):
154
+ generative_function._py_callable(*python_qmod_args)
@@ -144,7 +144,7 @@ class BaseInterpreter:
144
144
 
145
145
  def process_exception(self, e: Exception) -> None:
146
146
  if not isinstance(e, (ClassiqError, ValidationError)):
147
- raise ClassiqInternalExpansionError(str(e)) from None
147
+ raise ClassiqInternalExpansionError(str(e)) from e
148
148
  prefix = ""
149
149
  if not isinstance(e, ClassiqExpansionError):
150
150
  prefix = f"{type(e).__name__}: "
@@ -212,8 +212,15 @@ class BaseInterpreter:
212
212
 
213
213
  @evaluate.register
214
214
  def evaluate_field_access(self, field_access: FieldHandleBinding) -> Evaluated:
215
- base_value = self.evaluate(field_access.base_handle)
216
- return Evaluated(value=base_value.value.fields[field_access.field])
215
+ base_value = self.evaluate(field_access.base_handle).as_type(QuantumSymbol)
216
+ fields = base_value.fields
217
+ field_name = field_access.field
218
+ if field_name not in fields:
219
+ raise ClassiqExpansionError(
220
+ f"Struct {base_value.quantum_type.type_name} has no field "
221
+ f"{field_name!r}. Available fields: {', '.join(fields.keys())}"
222
+ )
223
+ return Evaluated(value=fields[field_name])
217
224
 
218
225
  @abstractmethod
219
226
  def emit(self, statement: QuantumStatement) -> None:
@@ -251,8 +258,10 @@ class BaseInterpreter:
251
258
  (func_def := self._expanded_functions.get(operation.closure_id))
252
259
  is not None
253
260
  ):
254
- captured_vars = self._top_level_scope[func_def.name].value.captured_vars
255
- operation.captured_vars.update(captured_vars)
261
+ cached_closure = self._top_level_scope[func_def.name].value
262
+ operation.captured_vars.set(
263
+ cached_closure.captured_vars, cached_closure, operation
264
+ )
256
265
  else:
257
266
  self._expand_body(operation)
258
267
 
@@ -21,8 +21,11 @@ from classiq.interface.model.model import Model
21
21
  from classiq.interface.model.native_function_definition import NativeFunctionDefinition
22
22
  from classiq.interface.model.phase_operation import PhaseOperation
23
23
  from classiq.interface.model.power import Power
24
- from classiq.interface.model.quantum_expressions.quantum_expression import (
25
- QuantumAssignmentOperation,
24
+ from classiq.interface.model.quantum_expressions.amplitude_loading_operation import (
25
+ AmplitudeLoadingOperation,
26
+ )
27
+ from classiq.interface.model.quantum_expressions.arithmetic_operation import (
28
+ ArithmeticOperation,
26
29
  )
27
30
  from classiq.interface.model.quantum_function_call import QuantumFunctionCall
28
31
  from classiq.interface.model.quantum_function_declaration import (
@@ -55,7 +58,17 @@ from classiq.model_expansions.quantum_operations import (
55
58
  VariableDeclarationStatementEmitter,
56
59
  )
57
60
  from classiq.model_expansions.quantum_operations.allocate import AllocateEmitter
58
- from classiq.model_expansions.quantum_operations.shallow_emitter import ShallowEmitter
61
+ from classiq.model_expansions.quantum_operations.assignment_result_processor import (
62
+ AssignmentResultProcessor,
63
+ )
64
+ from classiq.model_expansions.quantum_operations.block_evaluator import BlockEvaluator
65
+ from classiq.model_expansions.quantum_operations.composite_emitter import (
66
+ CompositeEmitter,
67
+ )
68
+ from classiq.model_expansions.quantum_operations.expression_evaluator import (
69
+ ExpressionEvaluator,
70
+ )
71
+ from classiq.model_expansions.quantum_operations.handle_evaluator import HandleEvaluator
59
72
  from classiq.model_expansions.scope import Evaluated, Scope
60
73
  from classiq.model_expansions.scope_initialization import (
61
74
  add_constants_to_scope,
@@ -101,15 +114,16 @@ class GenerativeInterpreter(BaseInterpreter):
101
114
  closure_class = FunctionClosure
102
115
  extra_args = {}
103
116
 
117
+ closure = closure_class.create(
118
+ name=func_decl.name,
119
+ positional_arg_declarations=func_decl.positional_arg_declarations,
120
+ body=function.body,
121
+ scope=Scope(parent=self._builder.current_scope),
122
+ lambda_external_vars=self._builder.current_block.captured_vars,
123
+ **extra_args,
124
+ )
104
125
  return Evaluated(
105
- value=closure_class.create(
106
- name=func_decl.name,
107
- positional_arg_declarations=func_decl.positional_arg_declarations,
108
- body=function.body,
109
- scope=Scope(parent=self._builder.current_scope),
110
- is_lambda=True,
111
- **extra_args,
112
- ),
126
+ value=closure,
113
127
  defining_function=self._builder.current_function,
114
128
  )
115
129
 
@@ -133,15 +147,39 @@ class GenerativeInterpreter(BaseInterpreter):
133
147
  BindEmitter(self).emit(bind)
134
148
 
135
149
  @emit.register
136
- def emit_quantum_assignment_operation(self, op: QuantumAssignmentOperation) -> None:
137
- ShallowEmitter(
138
- self, "assignment_operation", components=["expression", "result_var"]
150
+ def emit_amplitude_loading_operation(self, op: AmplitudeLoadingOperation) -> None:
151
+ CompositeEmitter[AmplitudeLoadingOperation](
152
+ self,
153
+ [
154
+ HandleEvaluator(self, "result_var"),
155
+ ExpressionEvaluator(self, "expression"),
156
+ AssignmentResultProcessor(self),
157
+ ],
158
+ ).emit(op)
159
+
160
+ @emit.register
161
+ def _emit_arithmetic_operation(self, op: ArithmeticOperation) -> None:
162
+ self.emit_arithmetic_operation(op)
163
+
164
+ def emit_arithmetic_operation(self, op: ArithmeticOperation) -> None:
165
+ CompositeEmitter[ArithmeticOperation](
166
+ self,
167
+ [
168
+ HandleEvaluator(self, "result_var"),
169
+ ExpressionEvaluator(self, "expression"),
170
+ AssignmentResultProcessor(self),
171
+ ],
139
172
  ).emit(op)
140
173
 
141
174
  @emit.register
142
175
  def emit_inplace_binary_operation(self, op: InplaceBinaryOperation) -> None:
143
- ShallowEmitter(
144
- self, "inplace_binary_operation", components=["target", "value"]
176
+ CompositeEmitter[InplaceBinaryOperation](
177
+ self,
178
+ [
179
+ HandleEvaluator(self, "target"),
180
+ HandleEvaluator(self, "value"),
181
+ ExpressionEvaluator(self, "value"),
182
+ ],
145
183
  ).emit(op)
146
184
 
147
185
  @emit.register
@@ -156,37 +194,60 @@ class GenerativeInterpreter(BaseInterpreter):
156
194
 
157
195
  @emit.register
158
196
  def emit_within_apply(self, within_apply: WithinApply) -> None:
159
- ShallowEmitter(
197
+ BlockEvaluator(
160
198
  self,
161
199
  WITHIN_APPLY_NAME,
162
- components=["within", "apply", "compute", "action"],
200
+ "within",
201
+ "apply",
202
+ "compute",
203
+ "action",
163
204
  ).emit(within_apply)
164
205
 
165
206
  @emit.register
166
207
  def emit_invert(self, invert: Invert) -> None:
167
- ShallowEmitter(self, INVERT_OPERATOR_NAME, components=["body"]).emit(invert)
208
+ BlockEvaluator(self, INVERT_OPERATOR_NAME, "body").emit(invert)
168
209
 
169
210
  @emit.register
170
211
  def emit_repeat(self, repeat: Repeat) -> None:
171
212
  RepeatEmitter(self).emit(repeat)
172
213
 
173
214
  @emit.register
215
+ def _emit_control(self, control: Control) -> None:
216
+ self.emit_control(control)
217
+
174
218
  def emit_control(self, control: Control) -> None:
175
- ShallowEmitter(
219
+ CompositeEmitter[Control](
176
220
  self,
177
- CONTROL_OPERATOR_NAME,
178
- components=["expression", "body", "else_block"],
221
+ [
222
+ ExpressionEvaluator(self, "expression"),
223
+ BlockEvaluator(
224
+ self,
225
+ CONTROL_OPERATOR_NAME,
226
+ "body",
227
+ "else_block",
228
+ ),
229
+ ],
179
230
  ).emit(control)
180
231
 
181
232
  @emit.register
182
233
  def emit_power(self, power: Power) -> None:
183
- ShallowEmitter(self, CONTROL_OPERATOR_NAME, components=["power", "body"]).emit(
184
- power
185
- )
234
+ CompositeEmitter[Power](
235
+ self,
236
+ [
237
+ ExpressionEvaluator(self, "power"),
238
+ BlockEvaluator(self, CONTROL_OPERATOR_NAME, "body"),
239
+ ],
240
+ ).emit(power)
186
241
 
187
242
  @emit.register
188
243
  def emit_phase(self, phase: PhaseOperation) -> None:
189
- ShallowEmitter(self, "phase", components=["expression", "theta"]).emit(phase)
244
+ CompositeEmitter[PhaseOperation](
245
+ self,
246
+ [
247
+ ExpressionEvaluator(self, "expression"),
248
+ ExpressionEvaluator(self, "theta"),
249
+ ],
250
+ ).emit(phase)
190
251
 
191
252
  def _expand_body(self, operation: Closure) -> None:
192
253
  if isinstance(operation, FunctionClosure) and operation.name == "permute":
@@ -12,8 +12,7 @@ from classiq.model_expansions.scope import QuantumSymbol
12
12
 
13
13
 
14
14
  class AllocateEmitter(Emitter[Allocate]):
15
- def emit(self, allocate: Allocate, /) -> None:
16
- self._register_debug_info(allocate)
15
+ def emit(self, allocate: Allocate, /) -> bool:
17
16
  target: QuantumSymbol = self._interpreter.evaluate(allocate.target).as_type(
18
17
  QuantumSymbol
19
18
  )
@@ -25,9 +24,15 @@ class AllocateEmitter(Emitter[Allocate]):
25
24
 
26
25
  size = self._get_var_size(target, allocate.size)
27
26
  allocate = allocate.model_copy(
28
- update=dict(size=Expression(expr=str(size)), target=target.handle)
27
+ update=dict(
28
+ size=Expression(expr=str(size)),
29
+ target=target.handle,
30
+ back_ref=allocate.uuid,
31
+ )
29
32
  )
33
+ self._register_debug_info(allocate)
30
34
  self.emit_statement(allocate)
35
+ return True
31
36
 
32
37
  def _get_var_size(self, target: QuantumSymbol, size: Expression | None) -> int:
33
38
  if size is None:
@@ -66,4 +71,5 @@ class AllocateEmitter(Emitter[Allocate]):
66
71
  level=OperationLevel.QMOD_STATEMENT,
67
72
  is_allocate_or_free=True,
68
73
  port_to_passed_variable_map={"ARG": str(allocate.target)},
74
+ node=allocate._as_back_ref(),
69
75
  )
@@ -0,0 +1,52 @@
1
+ from classiq.interface.generator.arith.arithmetic import compute_arithmetic_result_type
2
+ from classiq.interface.generator.functions.port_declaration import (
3
+ PortDeclarationDirection,
4
+ )
5
+ from classiq.interface.model.quantum_expressions.arithmetic_operation import (
6
+ ArithmeticOperation,
7
+ ArithmeticOperationKind,
8
+ )
9
+ from classiq.interface.model.quantum_expressions.quantum_expression import (
10
+ QuantumAssignmentOperation,
11
+ )
12
+
13
+ from classiq.model_expansions.evaluators.quantum_type_utils import copy_type_information
14
+ from classiq.model_expansions.quantum_operations.emitter import Emitter
15
+ from classiq.model_expansions.scope import QuantumSymbol
16
+ from classiq.model_expansions.transformers.ast_renamer import rename_variables
17
+
18
+
19
+ class AssignmentResultProcessor(Emitter[QuantumAssignmentOperation]):
20
+ def emit(self, op: QuantumAssignmentOperation, /) -> bool:
21
+ if (
22
+ isinstance(op, ArithmeticOperation)
23
+ and op.operation_kind == ArithmeticOperationKind.Assignment
24
+ ):
25
+ direction = PortDeclarationDirection.Output
26
+ self._update_result_type(op)
27
+ else:
28
+ direction = PortDeclarationDirection.Inout
29
+ self._capture_handle(op.result_var, direction)
30
+ return False
31
+
32
+ def _update_result_type(self, op: ArithmeticOperation) -> None:
33
+ expr = self._evaluate_expression(op.expression)
34
+ symbols = self._get_symbols_in_expression(expr)
35
+ expr_str = rename_variables(
36
+ expr.expr,
37
+ {str(symbol.handle): symbol.handle.identifier for symbol in symbols}
38
+ | {symbol.handle.qmod_expr: symbol.handle.identifier for symbol in symbols},
39
+ )
40
+ for symbol in symbols:
41
+ expr_str = expr_str.replace(
42
+ symbol.handle.qmod_expr, symbol.handle.identifier
43
+ )
44
+ result_type = compute_arithmetic_result_type(
45
+ expr_str,
46
+ {symbol.handle.identifier: symbol.quantum_type for symbol in symbols},
47
+ self._machine_precision,
48
+ )
49
+ result_symbol = self._interpreter.evaluate(op.result_var).as_type(QuantumSymbol)
50
+ copy_type_information(
51
+ result_type, result_symbol.quantum_type, str(op.result_var)
52
+ )
@@ -1,4 +1,7 @@
1
- from classiq.interface.exceptions import ClassiqExpansionError
1
+ from classiq.interface.exceptions import (
2
+ ClassiqExpansionError,
3
+ ClassiqInternalExpansionError,
4
+ )
2
5
  from classiq.interface.model.bind_operation import BindOperation
3
6
 
4
7
  from classiq.model_expansions.evaluators.parameter_types import (
@@ -9,21 +12,12 @@ from classiq.model_expansions.evaluators.quantum_type_utils import (
9
12
  validate_bind_targets,
10
13
  )
11
14
  from classiq.model_expansions.quantum_operations.emitter import Emitter
12
- from classiq.model_expansions.scope import QuantumSymbol
15
+ from classiq.model_expansions.scope import Evaluated, QuantumSymbol
13
16
 
14
17
 
15
18
  class BindEmitter(Emitter[BindOperation]):
16
- def emit(self, bind: BindOperation, /) -> None:
17
- inputs: list[QuantumSymbol] = [
18
- self._interpreter.evaluate(arg).as_type(QuantumSymbol)
19
- for arg in bind.in_handles
20
- ]
21
- outputs: list[QuantumSymbol] = [
22
- self._interpreter.evaluate(arg).as_type(QuantumSymbol)
23
- for arg in bind.out_handles
24
- ]
25
- inputs = evaluate_types_in_quantum_symbols(inputs, self._current_scope)
26
- outputs = evaluate_types_in_quantum_symbols(outputs, self._current_scope)
19
+ def emit(self, bind: BindOperation, /) -> bool:
20
+ inputs, outputs = self._get_inputs_outputs(bind)
27
21
  validate_bind_targets(bind, self._current_scope)
28
22
  unsized_outputs = [
29
23
  output for output in outputs if not output.quantum_type.has_size_in_bits
@@ -56,5 +50,64 @@ class BindEmitter(Emitter[BindOperation]):
56
50
  )
57
51
 
58
52
  self.emit_statement(
59
- BindOperation(in_handles=bind.in_handles, out_handles=bind.out_handles)
53
+ BindOperation(
54
+ in_handles=bind.in_handles,
55
+ out_handles=bind.out_handles,
56
+ back_ref=bind.uuid,
57
+ )
60
58
  )
59
+ return True
60
+
61
+ def _get_inputs_outputs(
62
+ self, bind: BindOperation
63
+ ) -> tuple[list[QuantumSymbol], list[QuantumSymbol]]:
64
+ evaluated_inputs: list[Evaluated] = [
65
+ self._interpreter.evaluate(arg) for arg in bind.in_handles
66
+ ]
67
+ evaluated_outputs: list[Evaluated] = [
68
+ self._interpreter.evaluate(arg) for arg in bind.out_handles
69
+ ]
70
+ self._validate_handle_states(evaluated_inputs, evaluated_outputs)
71
+ inputs: list[QuantumSymbol] = [
72
+ input.as_type(QuantumSymbol) for input in evaluated_inputs
73
+ ]
74
+ outputs: list[QuantumSymbol] = [
75
+ output.as_type(QuantumSymbol) for output in evaluated_outputs
76
+ ]
77
+ inputs = evaluate_types_in_quantum_symbols(inputs, self._current_scope)
78
+ outputs = evaluate_types_in_quantum_symbols(outputs, self._current_scope)
79
+ return inputs, outputs
80
+
81
+ def _validate_handle_states(
82
+ self, inputs: list[Evaluated], outputs: list[Evaluated]
83
+ ) -> None:
84
+ input_var_names: set[str] = set()
85
+ for inp in inputs:
86
+ if inp.defining_function is None:
87
+ raise ClassiqInternalExpansionError
88
+ var_name = inp.value.handle.name
89
+ input_var_names.add(var_name)
90
+ state = self._builder.current_block.captured_vars.get_state(
91
+ var_name, inp.defining_function
92
+ )
93
+ if not state:
94
+ raise ClassiqExpansionError(
95
+ f"Cannot use uninitialized quantum variable "
96
+ f"{inp.value.handle.name!r} on the left-hand side of a bind "
97
+ f"statement"
98
+ )
99
+ for out in outputs:
100
+ if out.defining_function is None:
101
+ raise ClassiqInternalExpansionError
102
+ var_name = out.value.handle.name
103
+ if var_name in input_var_names:
104
+ continue
105
+ state = self._builder.current_block.captured_vars.get_state(
106
+ var_name, out.defining_function
107
+ )
108
+ if state:
109
+ raise ClassiqExpansionError(
110
+ f"Cannot use initialized quantum variable "
111
+ f"{out.value.handle.name!r} on the right-hand side of a bind "
112
+ f"statement"
113
+ )