classiq 0.47.0__py3-none-any.whl → 0.48.1__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 (56) hide show
  1. classiq/applications/combinatorial_helpers/pauli_helpers/pauli_utils.py +2 -7
  2. classiq/applications/grover/grover_model_constructor.py +2 -1
  3. classiq/execution/execution_session.py +40 -9
  4. classiq/execution/jobs.py +18 -6
  5. classiq/interface/_version.py +1 -1
  6. classiq/interface/execution/primitives.py +9 -1
  7. classiq/interface/executor/iqae_result.py +3 -3
  8. classiq/interface/executor/result.py +3 -1
  9. classiq/interface/generator/expressions/expression.py +8 -0
  10. classiq/interface/generator/functions/type_name.py +1 -3
  11. classiq/interface/generator/synthesis_metadata/synthesis_execution_data.py +17 -3
  12. classiq/interface/ide/visual_model.py +3 -4
  13. classiq/interface/model/bind_operation.py +0 -3
  14. classiq/interface/model/port_declaration.py +1 -12
  15. classiq/interface/model/quantum_expressions/arithmetic_operation.py +38 -6
  16. classiq/interface/model/quantum_lambda_function.py +4 -1
  17. classiq/interface/model/quantum_statement.py +16 -1
  18. classiq/interface/model/quantum_variable_declaration.py +0 -22
  19. classiq/interface/server/global_versions.py +4 -4
  20. classiq/model_expansions/capturing/propagated_var_stack.py +5 -2
  21. classiq/model_expansions/closure.py +7 -2
  22. classiq/model_expansions/evaluators/quantum_type_utils.py +0 -7
  23. classiq/model_expansions/generative_functions.py +146 -28
  24. classiq/model_expansions/interpreter.py +11 -5
  25. classiq/model_expansions/quantum_operations/classicalif.py +27 -10
  26. classiq/model_expansions/quantum_operations/control.py +22 -15
  27. classiq/model_expansions/quantum_operations/emitter.py +60 -5
  28. classiq/model_expansions/quantum_operations/expression_operation.py +25 -16
  29. classiq/model_expansions/quantum_operations/inplace_binary_operation.py +163 -95
  30. classiq/model_expansions/quantum_operations/invert.py +12 -6
  31. classiq/model_expansions/quantum_operations/phase.py +15 -3
  32. classiq/model_expansions/quantum_operations/power.py +9 -8
  33. classiq/model_expansions/quantum_operations/quantum_assignment_operation.py +20 -5
  34. classiq/model_expansions/quantum_operations/quantum_function_call.py +1 -1
  35. classiq/model_expansions/quantum_operations/repeat.py +32 -13
  36. classiq/model_expansions/quantum_operations/within_apply.py +19 -6
  37. classiq/model_expansions/scope.py +16 -5
  38. classiq/model_expansions/scope_initialization.py +11 -1
  39. classiq/model_expansions/sympy_conversion/expression_to_sympy.py +23 -1
  40. classiq/model_expansions/visitors/variable_references.py +11 -7
  41. classiq/qmod/builtins/__init__.py +10 -0
  42. classiq/qmod/builtins/constants.py +10 -0
  43. classiq/qmod/builtins/functions/state_preparation.py +4 -1
  44. classiq/qmod/builtins/operations.py +43 -163
  45. classiq/qmod/create_model_function.py +1 -1
  46. classiq/qmod/generative.py +14 -5
  47. classiq/qmod/native/pretty_printer.py +9 -5
  48. classiq/qmod/pretty_print/pretty_printer.py +8 -4
  49. classiq/qmod/qmod_constant.py +28 -18
  50. classiq/qmod/qmod_variable.py +43 -23
  51. classiq/qmod/quantum_expandable.py +14 -1
  52. classiq/qmod/semantics/static_semantics_visitor.py +10 -0
  53. classiq/qmod/semantics/validation/constants_validation.py +16 -0
  54. {classiq-0.47.0.dist-info → classiq-0.48.1.dist-info}/METADATA +3 -1
  55. {classiq-0.47.0.dist-info → classiq-0.48.1.dist-info}/RECORD +56 -54
  56. {classiq-0.47.0.dist-info → classiq-0.48.1.dist-info}/WHEEL +0 -0
@@ -1,6 +1,5 @@
1
- from typing import TYPE_CHECKING, List, Optional, Sequence, Tuple
1
+ from typing import TYPE_CHECKING, List, Tuple
2
2
 
3
- from classiq.interface.exceptions import ClassiqInternalExpansionError
4
3
  from classiq.interface.generator.expressions.expression import Expression
5
4
  from classiq.interface.generator.functions.port_declaration import (
6
5
  PortDeclarationDirection,
@@ -25,6 +24,7 @@ from classiq.interface.model.quantum_type import (
25
24
  from classiq.interface.model.variable_declaration_statement import (
26
25
  VariableDeclarationStatement,
27
26
  )
27
+ from classiq.interface.model.within_apply_operation import WithinApply
28
28
 
29
29
  from classiq.model_expansions.closure import FunctionClosure
30
30
  from classiq.model_expansions.evaluators.parameter_types import (
@@ -35,7 +35,7 @@ from classiq.model_expansions.evaluators.quantum_type_utils import (
35
35
  )
36
36
  from classiq.model_expansions.quantum_operations.emitter import Emitter
37
37
  from classiq.model_expansions.scope import QuantumSymbol, Scope
38
- from classiq.qmod.builtins.functions import integer_xor, modular_add
38
+ from classiq.qmod.builtins.functions import CX, allocate, integer_xor, modular_add
39
39
 
40
40
 
41
41
  def _binary_function_declaration(
@@ -59,16 +59,13 @@ class InplaceBinaryOperationEmitter(Emitter[InplaceBinaryOperation]):
59
59
  assert isinstance(value_var.quantum_type, QuantumNumeric)
60
60
  assert isinstance(target_var.quantum_type, QuantumNumeric)
61
61
 
62
- sign_diff = int(value_var.quantum_type.sign_value) - int(
63
- target_var.quantum_type.sign_value
64
- )
65
62
  frac_digits_diff = (
66
63
  value_var.quantum_type.fraction_digits_value
67
64
  - target_var.quantum_type.fraction_digits_value
68
65
  )
69
66
  if (
70
- sign_diff + frac_digits_diff == value_var.quantum_type.size_in_bits
71
- or -sign_diff - frac_digits_diff == target_var.quantum_type.size_in_bits
67
+ frac_digits_diff == value_var.quantum_type.size_in_bits
68
+ or -frac_digits_diff == target_var.quantum_type.size_in_bits
72
69
  ):
73
70
  with self._propagated_var_stack.capture_variables(op):
74
71
  return
@@ -97,7 +94,6 @@ class InplaceBinaryOperationEmitter(Emitter[InplaceBinaryOperation]):
97
94
  body=_build_inplace_binary_operation(
98
95
  value_var=value_var,
99
96
  target_var=target_var,
100
- frac_digits_diff=frac_digits_diff,
101
97
  internal_function_declaration=_binary_function_declaration(
102
98
  op.operation
103
99
  ),
@@ -113,53 +109,56 @@ class InplaceBinaryOperationEmitter(Emitter[InplaceBinaryOperation]):
113
109
  def _build_inplace_binary_operation(
114
110
  value_var: QuantumSymbol,
115
111
  target_var: QuantumSymbol,
116
- frac_digits_diff: int,
117
112
  internal_function_declaration: NamedParamsQuantumFunctionDeclaration,
118
113
  ) -> List[QuantumStatement]:
119
114
  if TYPE_CHECKING:
120
115
  assert isinstance(value_var.quantum_type, QuantumNumeric)
121
116
  assert isinstance(target_var.quantum_type, QuantumNumeric)
122
117
 
123
- value_overlap_var, value_sign_var, value_bind_targets = _get_inplace_bind_targets(
124
- "value", value_var, frac_digits_diff
118
+ frac_digits_diff = (
119
+ value_var.quantum_type.fraction_digits_value
120
+ - target_var.quantum_type.fraction_digits_value
125
121
  )
126
- target_overlap_var, target_sign_var, target_bind_targets = (
127
- _get_inplace_bind_targets("target", target_var, -frac_digits_diff)
122
+ size_diff = (
123
+ value_var.quantum_type.size_in_bits - target_var.quantum_type.size_in_bits
128
124
  )
129
125
 
130
- value_pre_ops, value_post_ops = _get_inplace_pre_post_ops(
131
- value_var, value_bind_targets
126
+ target_overlap_var, target_var_decls, target_bind_ops = (
127
+ _trim_superfluous_fraction_digits("target", target_var, -frac_digits_diff)
132
128
  )
133
- target_pre_ops, target_post_ops = _get_inplace_pre_post_ops(
134
- target_var, target_bind_targets
129
+ value_overlap_var, value_trim_var_decls, value_bind_ops = (
130
+ _trim_superfluous_fraction_digits("value", value_var, frac_digits_diff)
135
131
  )
132
+ (
133
+ value_padded_var,
134
+ value_pad_var_decls,
135
+ value_pad_pre_bind_ops,
136
+ value_pad_init_ops,
137
+ value_post_bind_ops,
138
+ ) = _pad_with_sign_bit("value", value_overlap_var, size_diff)
136
139
 
137
- binary_ops = []
138
- if value_overlap_var is not None and target_overlap_var is not None:
139
- binary_ops.append(
140
- _internal_inplace_binary_operation_function_call(
141
- internal_function_declaration,
142
- value_overlap_var.handle,
143
- target_overlap_var.handle,
144
- )
145
- )
146
- if value_sign_var is not None and target_sign_var is not None:
147
- binary_ops.append(
148
- _internal_inplace_binary_operation_function_call(
149
- internal_function_declaration,
150
- value_sign_var.handle,
151
- target_sign_var.handle,
152
- )
153
- )
154
- if len(binary_ops) == 0:
155
- raise ClassiqInternalExpansionError("Bug in unrolling inplace operation")
140
+ op_call = _internal_inplace_binary_operation_function_call(
141
+ internal_function_declaration,
142
+ value_padded_var.handle,
143
+ target_overlap_var.handle,
144
+ )
156
145
 
157
146
  return [
158
- *value_pre_ops,
159
- *target_pre_ops,
160
- *binary_ops,
161
- *target_post_ops,
162
- *value_post_ops,
147
+ *target_var_decls,
148
+ *value_trim_var_decls,
149
+ *value_pad_var_decls,
150
+ WithinApply(
151
+ compute=[
152
+ *target_bind_ops,
153
+ *value_bind_ops,
154
+ *value_pad_pre_bind_ops,
155
+ *value_pad_init_ops,
156
+ *value_post_bind_ops,
157
+ ],
158
+ action=[
159
+ op_call,
160
+ ],
161
+ ),
163
162
  ]
164
163
 
165
164
 
@@ -176,75 +175,144 @@ def _internal_inplace_binary_operation_function_call(
176
175
  return internal_function_call
177
176
 
178
177
 
179
- def _get_inplace_bind_targets(
178
+ def _trim_superfluous_fraction_digits(
180
179
  kind: str, var: QuantumSymbol, frac_digits_diff: int
181
- ) -> Tuple[Optional[QuantumSymbol], Optional[QuantumSymbol], List[QuantumSymbol]]:
180
+ ) -> Tuple[QuantumSymbol, List[VariableDeclarationStatement], List[BindOperation]]:
181
+ if frac_digits_diff <= 0:
182
+ return var, [], []
183
+
182
184
  quantum_type = var.quantum_type
183
185
  if TYPE_CHECKING:
184
186
  assert isinstance(quantum_type, QuantumNumeric)
185
187
 
186
- if not quantum_type.sign_value and frac_digits_diff <= 0:
187
- return var, None, []
188
-
189
- significand_overlap = (
190
- quantum_type.size_in_bits
191
- - quantum_type.fraction_digits_value
192
- - int(quantum_type.sign_value)
188
+ trimmed_fraction_digits_var = QuantumSymbol(
189
+ handle=HandleBinding(name=f"trimmed_{kind}_fraction_digits"),
190
+ quantum_type=QuantumBitvector(
191
+ length=Expression(expr=str(frac_digits_diff)),
192
+ ),
193
193
  )
194
- fraction_overlap = quantum_type.fraction_digits_value - max(0, frac_digits_diff)
195
- if significand_overlap + fraction_overlap == 0 and quantum_type.size_in_bits == 1:
196
- assert quantum_type.sign_value
197
- return None, var, []
198
-
199
- bind_targets = []
194
+ overlap_var = QuantumSymbol(
195
+ handle=HandleBinding(name=f"{kind}_overlap"),
196
+ quantum_type=QuantumNumeric(
197
+ size=Expression(expr=str(quantum_type.size_in_bits - frac_digits_diff)),
198
+ is_signed=quantum_type.is_signed,
199
+ fraction_digits=Expression(expr="0"),
200
+ ),
201
+ )
202
+ bind_targets = trimmed_fraction_digits_var, overlap_var
200
203
 
201
- if frac_digits_diff > 0:
202
- bind_targets.append(
203
- QuantumSymbol(
204
- handle=HandleBinding(name=f"trimmed_{kind}_fraction_digits"),
205
- quantum_type=QuantumBitvector(
206
- length=Expression(expr=str(frac_digits_diff)),
207
- ),
208
- )
204
+ split_var_declarations = [
205
+ VariableDeclarationStatement(
206
+ name=var.handle.name,
207
+ quantum_type=var.quantum_type,
209
208
  )
209
+ for var in bind_targets
210
+ ]
211
+ bind_op = BindOperation(
212
+ in_handles=[var.handle],
213
+ out_handles=[var.handle for var in bind_targets],
214
+ )
210
215
 
211
- overlap_var = None
212
- if significand_overlap + fraction_overlap > 0:
213
- overlap_var = QuantumSymbol(
214
- handle=HandleBinding(name=f"{kind}_overlap"),
215
- quantum_type=QuantumNumeric(
216
- size=Expression(expr=str(significand_overlap + fraction_overlap)),
217
- is_signed=Expression(expr="False"),
218
- fraction_digits=Expression(expr=str(fraction_overlap)),
219
- ),
220
- )
221
- bind_targets.append(overlap_var)
216
+ return overlap_var, split_var_declarations, [bind_op]
222
217
 
223
- sign_var = None
224
- if quantum_type.sign_value:
225
- sign_var = QuantumSymbol(
226
- handle=HandleBinding(name=f"trimmed_{kind}_sign"),
227
- quantum_type=QuantumBit(),
228
- )
229
- bind_targets.append(sign_var)
230
218
 
231
- return overlap_var, sign_var, bind_targets
219
+ def _pad_with_sign_bit(kind: str, var: QuantumSymbol, size_diff: int) -> Tuple[
220
+ QuantumSymbol,
221
+ List[VariableDeclarationStatement],
222
+ List[QuantumStatement],
223
+ List[QuantumFunctionCall],
224
+ List[BindOperation],
225
+ ]:
226
+ quantum_type = var.quantum_type
227
+ if TYPE_CHECKING:
228
+ assert isinstance(quantum_type, QuantumNumeric)
232
229
 
230
+ if not quantum_type.sign_value or size_diff >= 0:
231
+ return var, [], [], [], []
233
232
 
234
- def _get_inplace_pre_post_ops(
235
- var: QuantumSymbol, bind_targets: List[QuantumSymbol]
236
- ) -> Tuple[Sequence[QuantumStatement], Sequence[QuantumStatement]]:
237
- if len(bind_targets) == 0:
238
- return [], []
233
+ significand_var, sign_var, sign_split_bind = _split_sign(kind, var)
234
+ padding_var, padding_allocation = _allocate_padding(kind, size_diff)
235
+ padding_init_ops = _init_padding(sign_var, padding_var, size_diff)
239
236
 
240
- value_bind_op = BindOperation(
241
- in_handles=[var.handle],
242
- out_handles=[var.handle for var in bind_targets],
237
+ padded_var = QuantumSymbol(
238
+ handle=HandleBinding(name=f"padded_{kind}"),
239
+ quantum_type=QuantumNumeric(
240
+ size=Expression(expr=str(quantum_type.size_in_bits - size_diff)),
241
+ is_signed=Expression(expr="False"),
242
+ fraction_digits=Expression(expr="0"),
243
+ ),
243
244
  )
244
- return [
245
+ padding_rebind = BindOperation(
246
+ in_handles=[significand_var.handle, sign_var.handle, padding_var.handle],
247
+ out_handles=[padded_var.handle],
248
+ )
249
+
250
+ var_decls = [
245
251
  VariableDeclarationStatement(
246
252
  name=var.handle.name,
247
253
  quantum_type=var.quantum_type,
248
254
  )
249
- for var in bind_targets
250
- ] + [value_bind_op], [value_bind_op.reversed()]
255
+ for var in (significand_var, sign_var, padding_var, padded_var)
256
+ ]
257
+
258
+ return (
259
+ padded_var,
260
+ var_decls,
261
+ [sign_split_bind, padding_allocation],
262
+ padding_init_ops,
263
+ [padding_rebind],
264
+ )
265
+
266
+
267
+ def _init_padding(
268
+ sign_var: QuantumSymbol, padding_var: QuantumSymbol, size_diff: int
269
+ ) -> List[QuantumFunctionCall]:
270
+ padding_init_ops = [
271
+ QuantumFunctionCall(
272
+ function=CX.func_decl.name,
273
+ positional_args=[sign_var.handle, padding_var[idx].handle],
274
+ )
275
+ for idx in range(-size_diff)
276
+ ]
277
+ for cx_call in padding_init_ops:
278
+ cx_call.set_func_decl(CX.func_decl)
279
+ return padding_init_ops
280
+
281
+
282
+ def _allocate_padding(
283
+ kind: str, size_diff: int
284
+ ) -> Tuple[QuantumSymbol, QuantumFunctionCall]:
285
+ padding_var = QuantumSymbol(
286
+ handle=HandleBinding(name=f"{kind}_sign_padding"),
287
+ quantum_type=QuantumBitvector(
288
+ length=Expression(expr=str(-size_diff)),
289
+ ),
290
+ )
291
+ padding_allocation = QuantumFunctionCall(
292
+ function=allocate.func_decl.name,
293
+ positional_args=[Expression(expr=str(-size_diff)), padding_var.handle],
294
+ )
295
+ padding_allocation.set_func_decl(allocate.func_decl)
296
+ return padding_var, padding_allocation
297
+
298
+
299
+ def _split_sign(
300
+ kind: str, var: QuantumSymbol
301
+ ) -> Tuple[QuantumSymbol, QuantumSymbol, BindOperation]:
302
+ significand_var = QuantumSymbol(
303
+ handle=HandleBinding(name=f"{kind}_significand"),
304
+ quantum_type=QuantumNumeric(
305
+ size=Expression(expr=str(var.quantum_type.size_in_bits - 1)),
306
+ is_signed=Expression(expr="False"),
307
+ fraction_digits=Expression(expr="0"),
308
+ ),
309
+ )
310
+ sign_var = QuantumSymbol(
311
+ handle=HandleBinding(name=f"{kind}_sign_bit"),
312
+ quantum_type=QuantumBit(),
313
+ )
314
+ sign_split_bind = BindOperation(
315
+ in_handles=[var.handle],
316
+ out_handles=[significand_var.handle, sign_var.handle],
317
+ )
318
+ return significand_var, sign_var, sign_split_bind
@@ -10,6 +10,14 @@ from classiq.model_expansions.scope import Scope
10
10
 
11
11
  class InvertEmitter(Emitter[Invert]):
12
12
  def emit(self, invert: Invert, /) -> None:
13
+ with self._propagated_var_stack.capture_variables(invert):
14
+ self._emit_propagated(invert)
15
+
16
+ def _emit_propagated(self, invert: Invert, /) -> None:
17
+ if invert.is_generative():
18
+ context = self._register_generative_context(invert, INVERT_OPERATOR_NAME)
19
+ invert = invert.copy(update={"body": context.statements("body")})
20
+
13
21
  if self._should_wrap(invert.body):
14
22
  self._emit_wrapped(invert)
15
23
  return
@@ -22,17 +30,15 @@ class InvertEmitter(Emitter[Invert]):
22
30
  blocks={"body": invert.body},
23
31
  scope=Scope(parent=self._current_scope),
24
32
  )
25
- with self._propagated_var_stack.capture_variables(invert):
26
- context = self._expand_operation(invert_operation)
33
+ context = self._expand_operation(invert_operation)
27
34
  self._builder.emit_statement(
28
35
  Invert(body=context.statements("body"), source_ref=invert.source_ref)
29
36
  )
30
37
 
31
38
  def _emit_wrapped(self, invert: Invert) -> None:
32
- with self._propagated_var_stack.capture_variables(invert):
33
- wrapping_function = self._create_expanded_wrapping_function(
34
- INVERT_OPERATOR_NAME, invert.body
35
- )
39
+ wrapping_function = self._create_expanded_wrapping_function(
40
+ INVERT_OPERATOR_NAME, invert.body
41
+ )
36
42
  self._builder.emit_statement(
37
43
  Invert(body=[wrapping_function], source_ref=invert.source_ref)
38
44
  )
@@ -21,7 +21,6 @@ from classiq.interface.model.within_apply_operation import WithinApply
21
21
 
22
22
  from classiq.applications.combinatorial_helpers.transformations.ising_converter import (
23
23
  _find_sub_list_items,
24
- _get_coeff_from_expr,
25
24
  _get_vars,
26
25
  _refine_ising_expr,
27
26
  _to_ising_symbolic_objective_function,
@@ -34,6 +33,12 @@ from classiq.qmod.semantics.error_manager import ErrorManager
34
33
 
35
34
 
36
35
  class PhaseEmitter(ExpressionOperationEmitter[PhaseOperation]):
36
+ def _negate_expression(self, expression: Expression, /) -> Expression:
37
+ return self._evaluate_expression(
38
+ # TODO: change to model_copy for pydantic V2
39
+ expression.copy(update=dict(expr=f"-({expression.expr})"))
40
+ )
41
+
37
42
  def emit(self, phase_op: PhaseOperation, /) -> None:
38
43
  phase_expression = self._evaluate_op_expression(phase_op)
39
44
  phase_op = phase_op.copy(update=dict(expression=phase_expression))
@@ -41,7 +46,10 @@ class PhaseEmitter(ExpressionOperationEmitter[PhaseOperation]):
41
46
  if len(arrays_with_subscript) > 0:
42
47
  self._emit_with_split(phase_op, phase_op.expression, arrays_with_subscript)
43
48
  return
44
-
49
+ # TODO: change to model_copy for pydantic V2
50
+ phase_op = phase_op.copy(
51
+ update=dict(expression=self._negate_expression(phase_op.expression))
52
+ )
45
53
  phase_op = self._evaluate_types_in_expression(phase_op, phase_op.expression)
46
54
  if len(phase_op.var_handles) == 0:
47
55
  ErrorManager().add_error(
@@ -135,6 +143,7 @@ def _convert_ising_sympy_to_pauli_terms(
135
143
  ising_expr: sympy.Expr, ordered_sympy_vars: List[sympy.Symbol]
136
144
  ) -> str:
137
145
  pauli_terms: List[str] = []
146
+ coefficients = ising_expr.as_coefficients_dict(*ordered_sympy_vars)
138
147
  for expr_term in ising_expr.args:
139
148
  expr_vars = _get_vars(expr_term)
140
149
  z_vec = _find_sub_list_items(ordered_sympy_vars, expr_vars)
@@ -144,7 +153,10 @@ def _convert_ising_sympy_to_pauli_terms(
144
153
  pauli_elements[len(z_vec) - index - 1] = (
145
154
  "Z" # reminder: Pauli reverses the order!
146
155
  )
147
- coeff = _get_coeff_from_expr(expr_term)
156
+ term_var = sympy.Mul(
157
+ *(var for i, var in enumerate(ordered_sympy_vars) if z_vec[i])
158
+ )
159
+ coeff = float(coefficients[term_var])
148
160
  paulis = [f"Pauli.{pauli}" for pauli in pauli_elements]
149
161
  pauli_terms.append(
150
162
  # fmt: off
@@ -2,7 +2,6 @@ from typing import Union
2
2
 
3
3
  import sympy
4
4
 
5
- from classiq.interface.exceptions import ClassiqExpansionError
6
5
  from classiq.interface.generator.expressions.evaluated_expression import (
7
6
  EvaluatedExpression,
8
7
  )
@@ -22,6 +21,14 @@ class PowerEmitter(Emitter[Power]):
22
21
  _power_expr: Expression
23
22
 
24
23
  def emit(self, power: Power, /) -> None:
24
+ with self._propagated_var_stack.capture_variables(power):
25
+ self._emit_propagated(power)
26
+
27
+ def _emit_propagated(self, power: Power) -> None:
28
+ if power.is_generative():
29
+ context = self._register_generative_context(power, POWER_OPERATOR_NAME)
30
+ power = power.copy(update={"body": context.statements("body")})
31
+
25
32
  self._power = power
26
33
  self._power_value = self._get_power_value()
27
34
  self._power_expr = Expression(
@@ -41,8 +48,7 @@ class PowerEmitter(Emitter[Power]):
41
48
  blocks=dict(body=self._power.body),
42
49
  scope=Scope(parent=self._current_scope),
43
50
  )
44
- with self._propagated_var_stack.capture_variables(self._power):
45
- context = self._expand_operation(power_operation)
51
+ context = self._expand_operation(power_operation)
46
52
  self._builder.emit_statement(
47
53
  Power(
48
54
  body=context.statements("body"),
@@ -66,9 +72,4 @@ class PowerEmitter(Emitter[Power]):
66
72
 
67
73
  def _get_power_value(self) -> Union[int, sympy.Basic]:
68
74
  power_value = self._interpreter.evaluate(self._power.power).value
69
- if not (isinstance(power_value, int) or power_value.is_symbol):
70
- raise ClassiqExpansionError(
71
- f"`power`'s argument should be an integer or identifier. Complex "
72
- f"expressions are not supported. Got {str(power_value)!r}"
73
- )
74
75
  return power_value
@@ -11,6 +11,7 @@ from classiq.interface.model.inplace_binary_operation import (
11
11
  )
12
12
  from classiq.interface.model.quantum_expressions.arithmetic_operation import (
13
13
  ArithmeticOperation,
14
+ ArithmeticOperationKind,
14
15
  )
15
16
  from classiq.interface.model.quantum_expressions.quantum_expression import (
16
17
  QuantumAssignmentOperation,
@@ -62,7 +63,7 @@ class QuantumAssignmentOperationEmitter(
62
63
  self, op: ArithmeticOperation, expression: Expression
63
64
  ) -> None:
64
65
  op, expression, is_bool_opt = self._optimize_boolean_expression(op, expression)
65
- if op.inplace_result:
66
+ if op.is_inplace:
66
67
  self._emit_inplace_arithmetic_op(op, expression, is_bool_opt)
67
68
  else:
68
69
  self._emit_general_assignment_operation(op)
@@ -70,7 +71,9 @@ class QuantumAssignmentOperationEmitter(
70
71
  def _emit_inplace_arithmetic_op(
71
72
  self, op: ArithmeticOperation, expression: Expression, is_bool_opt: bool
72
73
  ) -> None:
73
- if op.result_type.size_in_bits > 1 or not _is_res_boolean(op):
74
+ if op.operation_kind != ArithmeticOperationKind.InplaceXor or (
75
+ op.result_type.size_in_bits > 1 or not _is_res_boolean(op)
76
+ ):
74
77
  _validate_naive_inplace_handles(op)
75
78
  self._build_naive_inplace(op, expression)
76
79
  return
@@ -100,7 +103,15 @@ class QuantumAssignmentOperationEmitter(
100
103
  def _optimize_boolean_expression(
101
104
  self, op: ArithmeticOperation, expression: Expression
102
105
  ) -> Tuple[ArithmeticOperation, Expression, bool]:
103
- if not _all_vars_boolean(op):
106
+ if (
107
+ self._interpreter._is_frontend
108
+ or op.operation_kind
109
+ not in (
110
+ ArithmeticOperationKind.Assignment,
111
+ ArithmeticOperationKind.InplaceXor,
112
+ )
113
+ or not _all_vars_boolean(op)
114
+ ):
104
115
  return op, expression, False
105
116
  optimizer = BooleanExpressionOptimizer()
106
117
  optimized_expression = Expression(
@@ -130,10 +141,14 @@ class QuantumAssignmentOperationEmitter(
130
141
  arith_expression = ArithmeticOperation(
131
142
  result_var=HandleBinding(name=aux_var),
132
143
  expression=new_expression,
133
- inplace_result=False,
144
+ operation_kind=ArithmeticOperationKind.Assignment,
134
145
  )
146
+ if qe.operation_kind == ArithmeticOperationKind.InplaceXor:
147
+ op = BinaryOperation.Xor
148
+ else:
149
+ op = BinaryOperation.Addition
135
150
  inplace_store = InplaceBinaryOperation(
136
- operation=BinaryOperation.Xor,
151
+ operation=op,
137
152
  target=qe.result_var,
138
153
  value=HandleBinding(name=aux_var),
139
154
  )
@@ -11,5 +11,5 @@ class QuantumFunctionCallEmitter(Emitter[QuantumFunctionCall]):
11
11
  args = call.positional_args
12
12
  with ErrorManager().call(
13
13
  function.name
14
- ), self._propagated_var_stack.capture_variables(call):
14
+ ), function.scope.freeze(), self._propagated_var_stack.capture_variables(call):
15
15
  self._emit_quantum_function_call(function, args)
@@ -1,3 +1,5 @@
1
+ from typing import Type
2
+
1
3
  from classiq.interface.generator.expressions.expression import Expression
2
4
  from classiq.interface.generator.functions.builtins.internal_operators import (
3
5
  REPEAT_OPERATOR_NAME,
@@ -8,9 +10,10 @@ from classiq.interface.model.classical_parameter_declaration import (
8
10
  )
9
11
  from classiq.interface.model.repeat import Repeat
10
12
 
11
- from classiq.model_expansions.closure import FunctionClosure
13
+ from classiq.model_expansions.closure import FunctionClosure, GenerativeFunctionClosure
12
14
  from classiq.model_expansions.quantum_operations.emitter import Emitter
13
15
  from classiq.model_expansions.scope import Scope
16
+ from classiq.qmod.quantum_function import GenerativeQFunc
14
17
 
15
18
 
16
19
  class RepeatEmitter(Emitter[Repeat]):
@@ -18,16 +21,32 @@ class RepeatEmitter(Emitter[Repeat]):
18
21
  count = self._interpreter.evaluate(repeat.count).as_type(int)
19
22
  for i in range(count):
20
23
  with self._propagated_var_stack.capture_variables(repeat):
21
- iteration_function = FunctionClosure.create(
22
- name=REPEAT_OPERATOR_NAME,
23
- positional_arg_declarations=[
24
- ClassicalParameterDeclaration(
25
- name=repeat.iter_var, classical_type=Integer()
26
- )
27
- ],
28
- body=repeat.body,
29
- scope=Scope(parent=self._current_scope),
30
- )
31
- self._emit_quantum_function_call(
32
- iteration_function, [Expression(expr=str(i))]
24
+ self._emit_propagated(repeat, i)
25
+
26
+ def _emit_propagated(self, repeat: Repeat, i: int) -> None:
27
+ closure_constructor: Type[FunctionClosure]
28
+ extra_args: dict
29
+ if repeat.is_generative():
30
+ closure_constructor = GenerativeFunctionClosure
31
+ extra_args = {
32
+ "generative_blocks": {
33
+ "body": GenerativeQFunc(
34
+ repeat.get_generative_block("body"),
35
+ ),
36
+ }
37
+ }
38
+ else:
39
+ closure_constructor = FunctionClosure
40
+ extra_args = {}
41
+ iteration_function = closure_constructor.create(
42
+ name=REPEAT_OPERATOR_NAME,
43
+ positional_arg_declarations=[
44
+ ClassicalParameterDeclaration(
45
+ name=repeat.iter_var, classical_type=Integer()
33
46
  )
47
+ ],
48
+ body=repeat.body,
49
+ scope=Scope(parent=self._current_scope),
50
+ **extra_args,
51
+ )
52
+ self._emit_quantum_function_call(iteration_function, [Expression(expr=str(i))])
@@ -11,6 +11,21 @@ from classiq.model_expansions.scope import Scope
11
11
 
12
12
  class WithinApplyEmitter(Emitter[WithinApply]):
13
13
  def emit(self, within_apply: WithinApply, /) -> None:
14
+ with self._propagated_var_stack.capture_variables(within_apply):
15
+ self._emit_propagated(within_apply)
16
+
17
+ def _emit_propagated(self, within_apply: WithinApply) -> None:
18
+ if within_apply.is_generative():
19
+ within_apply_context = self._register_generative_context(
20
+ within_apply, WITHIN_APPLY_NAME, ["within", "apply"]
21
+ )
22
+ within_apply = within_apply.copy(
23
+ update={
24
+ "compute": within_apply_context.statements("within"),
25
+ "action": within_apply_context.statements("apply"),
26
+ }
27
+ )
28
+
14
29
  if self._should_wrap(within_apply.compute):
15
30
  self._emit_wrapped(within_apply)
16
31
  return
@@ -23,8 +38,7 @@ class WithinApplyEmitter(Emitter[WithinApply]):
23
38
  blocks=dict(within=within_apply.compute, apply=within_apply.action),
24
39
  scope=Scope(parent=self._current_scope),
25
40
  )
26
- with self._propagated_var_stack.capture_variables(within_apply):
27
- context = self._expand_operation(within_apply_operation)
41
+ context = self._expand_operation(within_apply_operation)
28
42
  self._builder.emit_statement(
29
43
  WithinApply(
30
44
  compute=context.statements("within"),
@@ -34,10 +48,9 @@ class WithinApplyEmitter(Emitter[WithinApply]):
34
48
  )
35
49
 
36
50
  def _emit_wrapped(self, within_apply: WithinApply) -> None:
37
- with self._propagated_var_stack.capture_variables(within_apply):
38
- wrapped_compute = self._create_expanded_wrapping_function(
39
- COMPUTE_OPERATOR_NAME, within_apply.compute
40
- )
51
+ wrapped_compute = self._create_expanded_wrapping_function(
52
+ COMPUTE_OPERATOR_NAME, within_apply.compute
53
+ )
41
54
  wrapped_within_apply = WithinApply(
42
55
  compute=[wrapped_compute],
43
56
  action=within_apply.action,