classiq 0.54.0__py3-none-any.whl → 0.55.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 (33) hide show
  1. classiq/interface/_version.py +1 -1
  2. classiq/interface/generator/functions/builtins/internal_operators.py +9 -1
  3. classiq/interface/generator/generated_circuit_data.py +0 -1
  4. classiq/interface/generator/model/preferences/preferences.py +4 -0
  5. classiq/interface/generator/types/compilation_metadata.py +5 -0
  6. classiq/interface/ide/visual_model.py +3 -1
  7. classiq/interface/model/control.py +22 -1
  8. classiq/interface/model/model.py +4 -0
  9. classiq/interface/model/native_function_definition.py +1 -1
  10. classiq/interface/model/quantum_expressions/arithmetic_operation.py +2 -26
  11. classiq/interface/model/quantum_statement.py +3 -0
  12. classiq/model_expansions/closure.py +74 -11
  13. classiq/model_expansions/function_builder.py +0 -6
  14. classiq/model_expansions/generative_functions.py +2 -2
  15. classiq/model_expansions/interpreter.py +22 -25
  16. classiq/model_expansions/quantum_operations/control.py +79 -20
  17. classiq/model_expansions/quantum_operations/emitter.py +24 -8
  18. classiq/model_expansions/quantum_operations/expression_operation.py +25 -1
  19. classiq/model_expansions/quantum_operations/quantum_assignment_operation.py +3 -26
  20. classiq/model_expansions/quantum_operations/quantum_function_call.py +2 -32
  21. classiq/model_expansions/quantum_operations/within_apply.py +0 -16
  22. classiq/model_expansions/scope_initialization.py +0 -1
  23. classiq/qmod/builtins/operations.py +113 -17
  24. classiq/qmod/create_model_function.py +10 -12
  25. classiq/qmod/model_state_container.py +5 -1
  26. classiq/qmod/native/pretty_printer.py +6 -1
  27. classiq/qmod/pretty_print/pretty_printer.py +25 -11
  28. classiq/qmod/quantum_function.py +25 -19
  29. classiq/qmod/synthesize_separately.py +1 -2
  30. {classiq-0.54.0.dist-info → classiq-0.55.0.dist-info}/METADATA +1 -1
  31. {classiq-0.54.0.dist-info → classiq-0.55.0.dist-info}/RECORD +32 -32
  32. classiq/model_expansions/call_to_model_converter.py +0 -190
  33. {classiq-0.54.0.dist-info → classiq-0.55.0.dist-info}/WHEEL +0 -0
@@ -70,7 +70,12 @@ class Emitter(Generic[QuantumStatementT]):
70
70
 
71
71
  self._scope_guard = self._interpreter._scope_guard
72
72
  self._machine_precision = self._interpreter._model.preferences.machine_precision
73
-
73
+ self._expanded_functions_compilation_metadata = (
74
+ self._interpreter._expanded_functions_compilation_metadata
75
+ )
76
+ self._functions_compilation_metadata = (
77
+ self._interpreter._model.functions_compilation_metadata
78
+ )
74
79
  self._generative_contexts: dict[str, OperationContext] = {}
75
80
 
76
81
  @abstractmethod
@@ -100,7 +105,7 @@ class Emitter(Generic[QuantumStatementT]):
100
105
  return self._interpreter._current_scope
101
106
 
102
107
  @property
103
- def _expanded_functions(self) -> list[NativeFunctionDefinition]:
108
+ def _expanded_functions(self) -> dict[str, NativeFunctionDefinition]:
104
109
  return self._interpreter._expanded_functions
105
110
 
106
111
  @property
@@ -137,23 +142,34 @@ class Emitter(Generic[QuantumStatementT]):
137
142
  )
138
143
  new_positional_arg_decls = new_declaration.positional_arg_declarations
139
144
  is_atomic = function.is_atomic
140
- if not is_atomic: # perform monomorphization
145
+ new_function_name = function.name
146
+ if not is_atomic: # perform monomorphization per interpreted parameters set
141
147
  self._add_params_to_scope(
142
148
  new_positional_arg_decls, evaluated_args, function
143
149
  )
144
150
  context = self._expand_operation(
145
151
  function.with_new_declaration(new_declaration)
146
152
  )
147
- self._expanded_functions.append(
148
- self._builder.create_definition(cast(FunctionContext, context))
153
+ function_context = cast(FunctionContext, context)
154
+ closure_id = function_context.closure.closure_id
155
+ function_def = self._expanded_functions.get(closure_id)
156
+ if function_def is None:
157
+ function_def = self._builder.create_definition(function_context)
158
+ self._expanded_functions[closure_id] = function_def
159
+ new_function_name = function_def.name
160
+ compilation_metadata = self._functions_compilation_metadata.get(
161
+ function.name
149
162
  )
163
+ if compilation_metadata is not None:
164
+ self._expanded_functions_compilation_metadata[new_function_name] = (
165
+ compilation_metadata
166
+ )
167
+
150
168
  new_positional_args = self._get_new_positional_args(
151
169
  evaluated_args, is_atomic, new_positional_arg_decls
152
170
  )
153
171
  new_call = QuantumFunctionCall(
154
- function=(
155
- new_declaration.name if is_atomic else self._expanded_functions[-1].name
156
- ),
172
+ function=new_function_name,
157
173
  positional_args=new_positional_args,
158
174
  )
159
175
  is_allocate_or_free = (
@@ -1,7 +1,7 @@
1
1
  import ast
2
2
  from abc import abstractmethod
3
3
  from itertools import chain
4
- from typing import TYPE_CHECKING, TypeVar
4
+ from typing import TYPE_CHECKING, TypeVar, Union
5
5
 
6
6
  from classiq.interface.exceptions import (
7
7
  ClassiqExpansionError,
@@ -13,6 +13,7 @@ from classiq.interface.generator.expressions.evaluated_expression import (
13
13
  from classiq.interface.generator.expressions.expression import Expression
14
14
  from classiq.interface.generator.functions.type_name import TypeName
15
15
  from classiq.interface.model.bind_operation import BindOperation
16
+ from classiq.interface.model.control import Control
16
17
  from classiq.interface.model.handle_binding import (
17
18
  FieldHandleBinding,
18
19
  HandleBinding,
@@ -20,6 +21,7 @@ from classiq.interface.model.handle_binding import (
20
21
  SubscriptHandleBinding,
21
22
  )
22
23
  from classiq.interface.model.quantum_expressions.quantum_expression import (
24
+ QuantumAssignmentOperation,
23
25
  QuantumExpressionOperation,
24
26
  )
25
27
  from classiq.interface.model.quantum_type import (
@@ -214,3 +216,25 @@ class ExpressionOperationEmitter(Emitter[ExpressionOperationT]):
214
216
  self._machine_precision,
215
217
  )
216
218
  return op_with_evaluated_types
219
+
220
+ @staticmethod
221
+ def _all_vars_boolean(op: QuantumExpressionOperation) -> bool:
222
+ if not all(
223
+ var_type.has_size_in_bits and var_type.size_in_bits == 1
224
+ for var_type in op.var_types.values()
225
+ ):
226
+ return False
227
+ return not any(
228
+ isinstance(var_type, QuantumNumeric)
229
+ and (var_type.sign_value or var_type.fraction_digits_value > 0)
230
+ for var_type in op.var_types.values()
231
+ )
232
+
233
+ @staticmethod
234
+ def _is_res_boolean(op: Union[QuantumAssignmentOperation, Control]) -> bool:
235
+ if not (op.result_type.has_size_in_bits and op.result_type.size_in_bits == 1):
236
+ return False
237
+ return not (
238
+ isinstance(op.result_type, QuantumNumeric)
239
+ and (op.result_type.sign_value or op.result_type.fraction_digits_value > 0)
240
+ )
@@ -101,7 +101,7 @@ class QuantumAssignmentOperationEmitter(
101
101
  if (
102
102
  op.operation_kind != ArithmeticOperationKind.InplaceXor
103
103
  or op.result_type.size_in_bits > 1
104
- or not _is_res_boolean(op)
104
+ or not self._is_res_boolean(op)
105
105
  or target.quantum_type.size_in_bits > 1
106
106
  or _is_constant(expression.expr)
107
107
  ):
@@ -141,7 +141,8 @@ class QuantumAssignmentOperationEmitter(
141
141
  ArithmeticOperationKind.Assignment,
142
142
  ArithmeticOperationKind.InplaceXor,
143
143
  )
144
- or not _all_vars_boolean(op)
144
+ or not self._all_vars_boolean(op)
145
+ or not self._is_res_boolean(op)
145
146
  ):
146
147
  return op, expression, False
147
148
  optimizer = BooleanExpressionOptimizer()
@@ -250,27 +251,3 @@ def _validate_naive_inplace_handles(qe: ArithmeticOperation) -> None:
250
251
  raise ClassiqExpansionError(
251
252
  HANDLE_ERROR_MESSAGE.format(handle_str=str(qe.result_var))
252
253
  )
253
-
254
-
255
- def _all_vars_boolean(op: ArithmeticOperation) -> bool:
256
- if not all(
257
- var_type.has_size_in_bits and var_type.size_in_bits == 1
258
- for var_type in op.var_types.values()
259
- ):
260
- return False
261
- if any(
262
- isinstance(var_type, QuantumNumeric)
263
- and (var_type.sign_value or var_type.fraction_digits_value > 0)
264
- for var_type in op.var_types.values()
265
- ):
266
- return False
267
- return _is_res_boolean(op)
268
-
269
-
270
- def _is_res_boolean(op: ArithmeticOperation) -> bool:
271
- if not (op.result_type.has_size_in_bits and op.result_type.size_in_bits == 1):
272
- return False
273
- return not (
274
- isinstance(op.result_type, QuantumNumeric)
275
- and (op.result_type.sign_value or op.result_type.fraction_digits_value > 0)
276
- )
@@ -2,10 +2,6 @@ from typing import TYPE_CHECKING
2
2
 
3
3
  from classiq.interface.model.quantum_function_call import QuantumFunctionCall
4
4
 
5
- from classiq.model_expansions.call_to_model_converter import (
6
- BlockFunctionInfo,
7
- CallToModelConverter,
8
- )
9
5
  from classiq.model_expansions.closure import FunctionClosure
10
6
  from classiq.model_expansions.quantum_operations.emitter import Emitter
11
7
  from classiq.qmod.semantics.error_manager import ErrorManager
@@ -18,37 +14,11 @@ class QuantumFunctionCallEmitter(Emitter[QuantumFunctionCall]):
18
14
  def __init__(self, interpreter: "Interpreter") -> None:
19
15
  super().__init__(interpreter)
20
16
  self._model = self._interpreter._model
21
- self._synthesized_separately_blocks = (
22
- self._interpreter._synthesized_separately_blocks
23
- )
24
17
 
25
18
  def emit(self, call: QuantumFunctionCall, /) -> None:
26
- function: FunctionClosure = self._interpreter.evaluate(call.function).as_type(
27
- FunctionClosure
28
- )
19
+ function = self._interpreter.evaluate(call.function).as_type(FunctionClosure)
29
20
  args = call.positional_args
30
21
  with ErrorManager().call(
31
22
  function.name
32
23
  ), function.scope.freeze(), self._propagated_var_stack.capture_variables(call):
33
- new_call = self._emit_quantum_function_call(function, args)
34
- if function.synthesis_data.should_synthesize_separately:
35
- interpreted_call_converter = CallToModelConverter(
36
- call,
37
- function.positional_arg_declarations,
38
- function.scope.data,
39
- self._model,
40
- )
41
- self._update_synthesized_separately_models(
42
- interpreted_call_converter, new_call.func_name
43
- )
44
-
45
- def _update_synthesized_separately_models(
46
- self, call_converter: CallToModelConverter, call_name: str
47
- ) -> None:
48
- synthesized_separately_blocks = self._synthesized_separately_blocks
49
- block_id = call_converter.block_id
50
- block_function = synthesized_separately_blocks.get(block_id)
51
- if block_function is None:
52
- block_function = BlockFunctionInfo.from_call_converter(call_converter)
53
- synthesized_separately_blocks[block_id] = block_function
54
- block_function.calls.add(call_name)
24
+ self._emit_quantum_function_call(function, args)
@@ -1,5 +1,4 @@
1
1
  from classiq.interface.generator.functions.builtins.internal_operators import (
2
- COMPUTE_OPERATOR_NAME,
3
2
  WITHIN_APPLY_NAME,
4
3
  )
5
4
  from classiq.interface.model.within_apply_operation import WithinApply
@@ -26,10 +25,6 @@ class WithinApplyEmitter(Emitter[WithinApply]):
26
25
  }
27
26
  )
28
27
 
29
- if self._should_wrap(within_apply.compute):
30
- self._emit_wrapped(within_apply)
31
- return
32
-
33
28
  self._emit_as_operation(within_apply)
34
29
 
35
30
  def _emit_as_operation(self, within_apply: WithinApply) -> None:
@@ -46,14 +41,3 @@ class WithinApplyEmitter(Emitter[WithinApply]):
46
41
  source_ref=within_apply.source_ref,
47
42
  )
48
43
  )
49
-
50
- def _emit_wrapped(self, within_apply: WithinApply) -> None:
51
- wrapped_compute = self._create_expanded_wrapping_function(
52
- COMPUTE_OPERATOR_NAME, within_apply.compute
53
- )
54
- wrapped_within_apply = WithinApply(
55
- compute=[wrapped_compute],
56
- action=within_apply.action,
57
- source_ref=within_apply.source_ref,
58
- )
59
- self._builder.emit_statement(wrapped_within_apply)
@@ -60,7 +60,6 @@ def _add_functions_to_scope(
60
60
  positional_arg_declarations=function.positional_arg_declarations,
61
61
  body=function.body,
62
62
  scope=Scope(parent=scope),
63
- synthesis_data=function.synthesis_data,
64
63
  )
65
64
  )
66
65
 
@@ -1,12 +1,16 @@
1
1
  import inspect
2
2
  import sys
3
+ import warnings
3
4
  from collections.abc import Mapping
4
5
  from types import FrameType
5
6
  from typing import (
7
+ TYPE_CHECKING,
6
8
  Any,
7
9
  Callable,
8
10
  Final,
11
+ Optional,
9
12
  Union,
13
+ overload,
10
14
  )
11
15
 
12
16
  from classiq.interface.exceptions import ClassiqValueError
@@ -21,13 +25,16 @@ from classiq.interface.model.classical_parameter_declaration import (
21
25
  ClassicalParameterDeclaration,
22
26
  )
23
27
  from classiq.interface.model.control import Control
24
- from classiq.interface.model.inplace_binary_operation import (
25
- BinaryOperation,
26
- InplaceBinaryOperation,
27
- )
28
28
  from classiq.interface.model.invert import Invert
29
29
  from classiq.interface.model.phase_operation import PhaseOperation
30
30
  from classiq.interface.model.power import Power
31
+ from classiq.interface.model.quantum_expressions.amplitude_loading_operation import (
32
+ AmplitudeLoadingOperation,
33
+ )
34
+ from classiq.interface.model.quantum_expressions.arithmetic_operation import (
35
+ ArithmeticOperation,
36
+ ArithmeticOperationKind,
37
+ )
31
38
  from classiq.interface.model.quantum_function_call import QuantumFunctionCall
32
39
  from classiq.interface.model.quantum_function_declaration import (
33
40
  QuantumOperandDeclaration,
@@ -37,7 +44,7 @@ from classiq.interface.model.repeat import Repeat
37
44
  from classiq.interface.model.statement_block import StatementBlock
38
45
  from classiq.interface.model.within_apply_operation import WithinApply
39
46
 
40
- from classiq.qmod.qmod_variable import Input, Output, QArray, QBit, QNum, QVar
47
+ from classiq.qmod.qmod_variable import Input, Output, QArray, QBit, QNum, QScalar, QVar
41
48
  from classiq.qmod.quantum_callable import QCallable
42
49
  from classiq.qmod.quantum_expandable import prepare_arg
43
50
  from classiq.qmod.symbolic_expr import SymbolicExpr
@@ -91,6 +98,7 @@ def if_(
91
98
  def control(
92
99
  ctrl: Union[SymbolicExpr, QBit, QArray[QBit]],
93
100
  stmt_block: Union[QCallable, Callable[[], None]],
101
+ else_block: Union[QCallable, Callable[[], None], None] = None,
94
102
  ) -> None:
95
103
  _validate_operand(stmt_block)
96
104
  assert QCallable.CURRENT_EXPANDABLE is not None
@@ -98,39 +106,125 @@ def control(
98
106
  control_stmt = Control(
99
107
  expression=Expression(expr=str(ctrl)),
100
108
  body=_operand_to_body(stmt_block, "stmt_block"),
109
+ else_block=_operand_to_body(else_block, "else_block") if else_block else None,
101
110
  source_ref=source_ref,
102
111
  )
103
112
  control_stmt.set_generative_block("body", stmt_block)
113
+ if else_block is not None:
114
+ control_stmt.set_generative_block("else_block", else_block)
104
115
  QCallable.CURRENT_EXPANDABLE.append_statement_to_body(control_stmt)
105
116
 
106
117
 
118
+ def assign(expression: SymbolicExpr, target_var: QScalar) -> None:
119
+ assert QCallable.CURRENT_EXPANDABLE is not None
120
+ source_ref = get_source_ref(sys._getframe(1))
121
+ QCallable.CURRENT_EXPANDABLE.append_statement_to_body(
122
+ ArithmeticOperation(
123
+ expression=Expression(expr=str(expression)),
124
+ result_var=target_var.get_handle_binding(),
125
+ operation_kind=ArithmeticOperationKind.Assignment,
126
+ source_ref=source_ref,
127
+ )
128
+ )
129
+
130
+
131
+ def assign_amplitude(expression: SymbolicExpr, target_var: QScalar) -> None:
132
+ assert QCallable.CURRENT_EXPANDABLE is not None
133
+ source_ref = get_source_ref(sys._getframe(1))
134
+ QCallable.CURRENT_EXPANDABLE.append_statement_to_body(
135
+ AmplitudeLoadingOperation(
136
+ expression=Expression(expr=str(expression)),
137
+ result_var=target_var.get_handle_binding(),
138
+ source_ref=source_ref,
139
+ )
140
+ )
141
+
142
+
143
+ @overload
144
+ def inplace_add(expression: SymbolicExpr, target_var: QScalar) -> None:
145
+ pass
146
+
147
+
148
+ @overload
149
+ def inplace_add(*, value: QNum, target: QNum) -> None:
150
+ pass
151
+
152
+
107
153
  def inplace_add(
108
- value: QNum,
109
- target: QNum,
154
+ expression: Optional[SymbolicExpr] = None,
155
+ target_var: Optional[QScalar] = None,
156
+ value: Optional[QNum] = None,
157
+ target: Optional[QNum] = None,
110
158
  ) -> None:
111
159
  assert QCallable.CURRENT_EXPANDABLE is not None
160
+ if value is not None or target is not None:
161
+ warnings.warn(
162
+ "Parameters 'value' and 'target of function 'inplace_add' have "
163
+ "been renamed to 'expression' and 'target_var' respectively. Parameters "
164
+ "'value' and 'target' will no longer be supported starting on 02/12/24 at "
165
+ "the earliest.\nHint: Change `inplace_add(value=..., target=...)` to "
166
+ "`inplace_add(expression=..., target_var=...)` or `inplace_add(..., ...)`.",
167
+ category=DeprecationWarning,
168
+ stacklevel=2,
169
+ )
170
+ if value is not None:
171
+ expression = value
172
+ if target is not None:
173
+ target_var = target
174
+ if TYPE_CHECKING:
175
+ assert expression is not None
176
+ assert target_var is not None
112
177
  source_ref = get_source_ref(sys._getframe(1))
113
178
  QCallable.CURRENT_EXPANDABLE.append_statement_to_body(
114
- InplaceBinaryOperation(
115
- target=target.get_handle_binding(),
116
- value=value.get_handle_binding(),
117
- operation=BinaryOperation.Addition,
179
+ ArithmeticOperation(
180
+ expression=Expression(expr=str(expression)),
181
+ result_var=target_var.get_handle_binding(),
182
+ operation_kind=ArithmeticOperationKind.InplaceAdd,
118
183
  source_ref=source_ref,
119
184
  )
120
185
  )
121
186
 
122
187
 
188
+ @overload
189
+ def inplace_xor(expression: SymbolicExpr, target_var: QScalar) -> None:
190
+ pass
191
+
192
+
193
+ @overload
194
+ def inplace_xor(*, value: QNum, target: QNum) -> None:
195
+ pass
196
+
197
+
123
198
  def inplace_xor(
124
- value: QNum,
125
- target: QNum,
199
+ expression: Optional[SymbolicExpr] = None,
200
+ target_var: Optional[QScalar] = None,
201
+ value: Optional[QNum] = None,
202
+ target: Optional[QNum] = None,
126
203
  ) -> None:
127
204
  assert QCallable.CURRENT_EXPANDABLE is not None
205
+ if value is not None or target is not None:
206
+ warnings.warn(
207
+ "Parameters 'value' and 'target of function 'inplace_xor' have "
208
+ "been renamed to 'expression' and 'target_var' respectively. Parameters "
209
+ "'value' and 'target' will no longer be supported starting on 02/12/24 at "
210
+ "the earliest.\nHint: Change `inplace_xor(value=..., target=...)` to "
211
+ "`inplace_xor(expression=..., target_var=...)` or `inplace_xor(..., ...)`.",
212
+ category=DeprecationWarning,
213
+ stacklevel=2,
214
+ )
215
+ if value is not None:
216
+ expression = value
217
+ if target is not None:
218
+ target_var = target
219
+ if TYPE_CHECKING:
220
+ assert expression is not None
221
+ assert target_var is not None
128
222
  source_ref = get_source_ref(sys._getframe(1))
129
223
  QCallable.CURRENT_EXPANDABLE.append_statement_to_body(
130
- InplaceBinaryOperation(
131
- target=target.get_handle_binding(),
132
- value=value.get_handle_binding(),
133
- operation=BinaryOperation.Xor,
224
+ ArithmeticOperation(
225
+ expression=Expression(expr=str(expression)),
226
+ result_var=target_var.get_handle_binding(),
227
+ operation_kind=ArithmeticOperationKind.InplaceXor,
134
228
  source_ref=source_ref,
135
229
  )
136
230
  )
@@ -297,6 +391,8 @@ def _operand_to_body(
297
391
 
298
392
 
299
393
  __all__ = [
394
+ "assign",
395
+ "assign_amplitude",
300
396
  "bind",
301
397
  "control",
302
398
  "invert",
@@ -60,6 +60,7 @@ def create_model(
60
60
  user_gen_functions = {
61
61
  gen_func._py_callable.__name__ for gen_func in GEN_QFUNCS
62
62
  } - set(BUILTIN_FUNCTION_DECLARATIONS.keys())
63
+
63
64
  if len(user_gen_functions) > 0 and is_generative_expansion_enabled():
64
65
  model = _expand_generative_model(
65
66
  (
@@ -100,11 +101,17 @@ def _expand_generative_model(
100
101
  def _dummy() -> None:
101
102
  pass
102
103
 
104
+ functions_compilation_metadata = {
105
+ dec_func._py_callable.__name__: dec_func.compilation_metadata
106
+ for dec_func in DEC_QFUNCS
107
+ if dec_func.compilation_metadata is not None
108
+ }
103
109
  model = _dummy.create_model(
104
110
  constraints,
105
111
  execution_preferences,
106
112
  preferences,
107
113
  classical_execution_function,
114
+ functions_compilation_metadata,
108
115
  )
109
116
  generative_functions = _get_generative_functions(gen_main, preferences)
110
117
  model.functions = generative_functions
@@ -157,7 +164,8 @@ def _get_wrapper_main(gen_main: QFunc, preferences: Optional[Preferences]) -> Mo
157
164
  def _get_all_model_functions_as_generative_functions() -> list[GenerativeQFunc]:
158
165
 
159
166
  gen_functions = list(GEN_QFUNCS) + [
160
- _get_gen_from_dec(dec_func) for dec_func in DEC_QFUNCS
167
+ GenerativeQFunc(dec_func._py_callable, dec_func.func_decl)
168
+ for dec_func in DEC_QFUNCS
161
169
  ]
162
170
  return [
163
171
  (
@@ -173,16 +181,6 @@ def _get_all_model_functions_as_generative_functions() -> list[GenerativeQFunc]:
173
181
  ]
174
182
 
175
183
 
176
- def _get_gen_from_dec(dec_func: QFunc) -> GenerativeQFunc:
177
- synthesis_data = dec_func.synthesis_data
178
- if synthesis_data is not None and synthesis_data.should_synthesize_separately:
179
- raise ClassiqError(
180
- """The model contains generative functions,
181
- which cannot coexist with functions marked for separate synthesis"""
182
- )
183
- return GenerativeQFunc(dec_func._py_callable, dec_func.func_decl)
184
-
185
-
186
184
  def _interpret_generative_model(
187
185
  gen_model: Model, gen_functions: list[GenerativeQFunc]
188
186
  ) -> dict[str, NativeFunctionDefinition]:
@@ -192,7 +190,7 @@ def _interpret_generative_model(
192
190
  )
193
191
  interpreter = Interpreter(gen_model, gen_functions, is_frontend=True)
194
192
  set_frontend_interpreter(interpreter)
195
- expand_model, _ = interpreter.expand()
193
+ expand_model = interpreter.expand()
196
194
  functions_dict = nameables_to_dict(expand_model.functions)
197
195
 
198
196
  # Inline _gen_main call in main
@@ -1,8 +1,11 @@
1
1
  from classiq.interface.generator.constant import Constant
2
+ from classiq.interface.generator.types.compilation_metadata import CompilationMetadata
2
3
  from classiq.interface.generator.types.enum_declaration import EnumDeclaration
3
4
  from classiq.interface.generator.types.qstruct_declaration import QStructDeclaration
4
5
  from classiq.interface.generator.types.struct_declaration import StructDeclaration
5
- from classiq.interface.model.native_function_definition import NativeFunctionDefinition
6
+ from classiq.interface.model.native_function_definition import (
7
+ NativeFunctionDefinition,
8
+ )
6
9
 
7
10
 
8
11
  class ModelStateContainer:
@@ -11,6 +14,7 @@ class ModelStateContainer:
11
14
  qstruct_decls: dict[str, QStructDeclaration]
12
15
  native_defs: dict[str, NativeFunctionDefinition]
13
16
  constants: dict[str, Constant]
17
+ functions_compilation_metadata: dict[str, CompilationMetadata]
14
18
 
15
19
 
16
20
  QMODULE = ModelStateContainer()
@@ -253,7 +253,12 @@ class DSLPrettyPrinter(Visitor):
253
253
  def visit_Control(self, op: Control) -> str:
254
254
  control = f"{self._indent}control ({self.visit(op.expression)}) {{\n"
255
255
  control += self._visit_body(op.body)
256
- control += f"{self._indent}}}\n"
256
+ control += f"{self._indent}}}"
257
+ if op.else_block is not None:
258
+ control += " else {\n"
259
+ control += self._visit_body(op.else_block)
260
+ control += f"{self._indent}}}"
261
+ control += "\n"
257
262
  return control
258
263
 
259
264
  def visit_PhaseOperation(self, op: PhaseOperation) -> str:
@@ -50,9 +50,6 @@ from classiq.interface.model.quantum_expressions.arithmetic_operation import (
50
50
  ArithmeticOperation,
51
51
  ArithmeticOperationKind,
52
52
  )
53
- from classiq.interface.model.quantum_expressions.quantum_expression import (
54
- QuantumAssignmentOperation,
55
- )
56
53
  from classiq.interface.model.quantum_function_call import (
57
54
  OperandIdentifier,
58
55
  QuantumFunctionCall,
@@ -391,7 +388,10 @@ class PythonPrettyPrinter(Visitor):
391
388
 
392
389
  def visit_Control(self, op: Control) -> str:
393
390
  self._imports["control"] = 1
394
- return f"{self._indent}control({self.visit(op.expression)}, {self._visit_body(op.body)})\n"
391
+ control_else = (
392
+ f", {self._visit_body(op.else_block)}" if op.else_block is not None else ""
393
+ )
394
+ return f"{self._indent}control({self.visit(op.expression)}, {self._visit_body(op.body)}{control_else})\n"
395
395
 
396
396
  def visit_PhaseOperation(self, op: PhaseOperation) -> str:
397
397
  self._imports["phase"] = 1
@@ -429,13 +429,16 @@ class PythonPrettyPrinter(Visitor):
429
429
  code = f"lambda{argument_string}: {'[' if len(body) > 1 else ''}\n"
430
430
  self._level += 1
431
431
  for i, statement in enumerate(body):
432
- if isinstance(
433
- statement, (QuantumAssignmentOperation, VariableDeclarationStatement)
434
- ):
432
+ if isinstance(statement, VariableDeclarationStatement):
435
433
  raise AssertionError(
436
- "pretty printing quantum assignment operations or variable declaration statements in quantum lambda function is unsupported."
434
+ "pretty printing variable declaration statements in quantum lambda function is unsupported."
437
435
  )
438
- code += self.visit(statement)
436
+ if isinstance(statement, AmplitudeLoadingOperation):
437
+ code += self.visit_AmplitudeLoadingOperation(statement, in_lambda=True)
438
+ elif isinstance(statement, ArithmeticOperation):
439
+ code += self.visit_ArithmeticOperation(statement, in_lambda=True)
440
+ else:
441
+ code += self.visit(statement)
439
442
  if i < len(body) - 1:
440
443
  code += ","
441
444
  self._level -= 1
@@ -469,18 +472,29 @@ class PythonPrettyPrinter(Visitor):
469
472
  def visit_FieldHandleBinding(self, var_ref: FieldHandleBinding) -> str:
470
473
  return f"{self.visit(var_ref.base_handle)}.{self.visit(var_ref.field)}"
471
474
 
472
- def visit_ArithmeticOperation(self, arith_op: ArithmeticOperation) -> str:
475
+ def visit_ArithmeticOperation(
476
+ self, arith_op: ArithmeticOperation, in_lambda: bool = False
477
+ ) -> str:
473
478
  if arith_op.operation_kind == ArithmeticOperationKind.Assignment:
474
479
  op = "|="
480
+ func = "assign"
475
481
  elif arith_op.operation_kind == ArithmeticOperationKind.InplaceXor:
476
482
  op = "^="
483
+ func = "inplace_xor"
477
484
  else:
478
485
  op = "+="
486
+ func = "inplace_add"
487
+ if in_lambda:
488
+ self._imports[func] = 1
489
+ return f"{func}({self.visit(arith_op.expression)}, {self._indent}{self.visit(arith_op.result_var)})\n"
479
490
  return f"{self._indent}{self.visit(arith_op.result_var)} {op} {self.visit(arith_op.expression)}\n"
480
491
 
481
492
  def visit_AmplitudeLoadingOperation(
482
- self, amplitude_loading_op: AmplitudeLoadingOperation
493
+ self, amplitude_loading_op: AmplitudeLoadingOperation, in_lambda: bool = False
483
494
  ) -> str:
495
+ if in_lambda:
496
+ self._imports["assign_amplitude"] = 1
497
+ return f"assign_amplitude({self.visit(amplitude_loading_op.expression)}, {self._indent}{self.visit(amplitude_loading_op.result_var)})\n"
484
498
  return f"{self._indent}{self.visit(amplitude_loading_op.result_var)} *= {self.visit(amplitude_loading_op.expression)}\n"
485
499
 
486
500
  def _print_bind_handles(self, handles: list[HandleBinding]) -> str: