classiq 0.46.1__py3-none-any.whl → 0.48.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 (71) hide show
  1. classiq/_internals/api_wrapper.py +45 -8
  2. classiq/applications/combinatorial_helpers/pauli_helpers/pauli_utils.py +2 -7
  3. classiq/applications/grover/grover_model_constructor.py +2 -1
  4. classiq/execution/execution_session.py +133 -45
  5. classiq/execution/jobs.py +120 -1
  6. classiq/interface/_version.py +1 -1
  7. classiq/interface/backend/quantum_backend_providers.py +0 -1
  8. classiq/interface/debug_info/debug_info.py +23 -1
  9. classiq/interface/execution/primitives.py +17 -0
  10. classiq/interface/executor/iqae_result.py +3 -3
  11. classiq/interface/executor/result.py +3 -1
  12. classiq/interface/generator/arith/arithmetic_operations.py +5 -2
  13. classiq/interface/generator/arith/binary_ops.py +21 -14
  14. classiq/interface/generator/arith/extremum_operations.py +9 -1
  15. classiq/interface/generator/arith/number_utils.py +6 -0
  16. classiq/interface/generator/arith/register_user_input.py +30 -21
  17. classiq/interface/generator/arith/unary_ops.py +13 -1
  18. classiq/interface/generator/expressions/expression.py +8 -0
  19. classiq/interface/generator/functions/type_name.py +1 -3
  20. classiq/interface/generator/generated_circuit_data.py +47 -2
  21. classiq/interface/generator/quantum_program.py +10 -2
  22. classiq/interface/generator/synthesis_metadata/synthesis_execution_data.py +17 -3
  23. classiq/interface/ide/visual_model.py +10 -5
  24. classiq/interface/interface_version.py +1 -1
  25. classiq/interface/model/bind_operation.py +0 -3
  26. classiq/interface/model/phase_operation.py +11 -0
  27. classiq/interface/model/port_declaration.py +1 -12
  28. classiq/interface/model/quantum_expressions/arithmetic_operation.py +34 -6
  29. classiq/interface/model/quantum_lambda_function.py +4 -1
  30. classiq/interface/model/quantum_statement.py +16 -1
  31. classiq/interface/model/quantum_variable_declaration.py +0 -22
  32. classiq/interface/model/statement_block.py +3 -0
  33. classiq/interface/server/global_versions.py +4 -4
  34. classiq/interface/server/routes.py +0 -3
  35. classiq/model_expansions/capturing/propagated_var_stack.py +5 -2
  36. classiq/model_expansions/closure.py +7 -2
  37. classiq/model_expansions/evaluators/quantum_type_utils.py +0 -7
  38. classiq/model_expansions/generative_functions.py +146 -28
  39. classiq/model_expansions/interpreter.py +17 -5
  40. classiq/model_expansions/quantum_operations/classicalif.py +27 -10
  41. classiq/model_expansions/quantum_operations/control.py +22 -15
  42. classiq/model_expansions/quantum_operations/emitter.py +68 -7
  43. classiq/model_expansions/quantum_operations/expression_operation.py +25 -16
  44. classiq/model_expansions/quantum_operations/inplace_binary_operation.py +167 -95
  45. classiq/model_expansions/quantum_operations/invert.py +12 -6
  46. classiq/model_expansions/quantum_operations/phase.py +189 -0
  47. classiq/model_expansions/quantum_operations/power.py +9 -8
  48. classiq/model_expansions/quantum_operations/quantum_assignment_operation.py +20 -5
  49. classiq/model_expansions/quantum_operations/quantum_function_call.py +1 -1
  50. classiq/model_expansions/quantum_operations/repeat.py +32 -13
  51. classiq/model_expansions/quantum_operations/within_apply.py +19 -6
  52. classiq/model_expansions/scope.py +16 -5
  53. classiq/model_expansions/scope_initialization.py +11 -1
  54. classiq/model_expansions/sympy_conversion/expression_to_sympy.py +23 -1
  55. classiq/model_expansions/visitors/variable_references.py +11 -7
  56. classiq/qmod/builtins/__init__.py +10 -0
  57. classiq/qmod/builtins/constants.py +10 -0
  58. classiq/qmod/builtins/functions/state_preparation.py +4 -1
  59. classiq/qmod/builtins/operations.py +55 -161
  60. classiq/qmod/create_model_function.py +1 -1
  61. classiq/qmod/generative.py +14 -5
  62. classiq/qmod/native/pretty_printer.py +14 -4
  63. classiq/qmod/pretty_print/pretty_printer.py +14 -4
  64. classiq/qmod/qmod_constant.py +28 -18
  65. classiq/qmod/qmod_variable.py +43 -23
  66. classiq/qmod/quantum_expandable.py +14 -1
  67. classiq/qmod/semantics/static_semantics_visitor.py +10 -0
  68. classiq/qmod/semantics/validation/constants_validation.py +16 -0
  69. {classiq-0.46.1.dist-info → classiq-0.48.0.dist-info}/METADATA +9 -4
  70. {classiq-0.46.1.dist-info → classiq-0.48.0.dist-info}/RECORD +71 -66
  71. {classiq-0.46.1.dist-info → classiq-0.48.0.dist-info}/WHEEL +0 -0
@@ -1,17 +1,13 @@
1
1
  import inspect
2
2
  import sys
3
- import warnings
4
3
  from types import FrameType
5
4
  from typing import (
6
- TYPE_CHECKING,
7
5
  Any,
8
6
  Callable,
9
7
  Final,
10
8
  List,
11
9
  Mapping,
12
- Optional,
13
10
  Union,
14
- overload,
15
11
  )
16
12
 
17
13
  from classiq.interface.exceptions import ClassiqValueError
@@ -31,6 +27,7 @@ from classiq.interface.model.inplace_binary_operation import (
31
27
  InplaceBinaryOperation,
32
28
  )
33
29
  from classiq.interface.model.invert import Invert
30
+ from classiq.interface.model.phase_operation import PhaseOperation
34
31
  from classiq.interface.model.power import Power
35
32
  from classiq.interface.model.quantum_function_call import QuantumFunctionCall
36
33
  from classiq.interface.model.quantum_function_declaration import (
@@ -79,57 +76,33 @@ def if_(
79
76
  _validate_operand(else_)
80
77
  assert QCallable.CURRENT_EXPANDABLE is not None
81
78
  source_ref = get_source_ref(sys._getframe(1))
82
- QCallable.CURRENT_EXPANDABLE.append_statement_to_body(
83
- ClassicalIf(
84
- condition=Expression(expr=str(condition)),
85
- then=_operand_to_body(then, "then"),
86
- else_=_operand_to_body(else_, "else") if else_ != _MISSING_VALUE else [], # type: ignore[arg-type]
87
- source_ref=source_ref,
88
- )
89
- )
90
-
91
-
92
- @overload # FIXME: Remove overloading (CAD-21932)
93
- def control(
94
- ctrl: Union[QBit, QArray[QBit]], stmt_block: Union[QCallable, Callable[[], None]]
95
- ) -> None:
96
- pass
97
-
98
79
 
99
- @overload
100
- def control(
101
- ctrl: SymbolicExpr, stmt_block: Union[QCallable, Callable[[], None]]
102
- ) -> None:
103
- pass
80
+ if_stmt = ClassicalIf(
81
+ condition=Expression(expr=str(condition)),
82
+ then=_operand_to_body(then, "then"),
83
+ else_=_operand_to_body(else_, "else") if else_ != _MISSING_VALUE else [], # type: ignore[arg-type]
84
+ source_ref=source_ref,
85
+ )
86
+ if_stmt.set_generative_block("then", then)
87
+ if callable(else_):
88
+ if_stmt.set_generative_block("else", else_)
89
+ QCallable.CURRENT_EXPANDABLE.append_statement_to_body(if_stmt)
104
90
 
105
91
 
106
92
  def control(
107
93
  ctrl: Union[SymbolicExpr, QBit, QArray[QBit]],
108
- stmt_block: Optional[Union[QCallable, Callable[[], None]]] = None,
109
- operand: Optional[Union[QCallable, Callable[[], None]]] = None,
94
+ stmt_block: Union[QCallable, Callable[[], None]],
110
95
  ) -> None:
111
- if operand is not None:
112
- warnings.warn(
113
- "Parameter 'operand' of function 'control' has been renamed to "
114
- "'stmt_block'. Parameter 'operand' will be deprecated in a future "
115
- "release.\nHint: Change `control(ctrl=..., operand=...)` to "
116
- "`control(ctrl=..., stmt_block=...)` or `control(..., ...)`.",
117
- category=DeprecationWarning,
118
- stacklevel=2,
119
- )
120
- stmt_block = operand
121
- if TYPE_CHECKING:
122
- assert stmt_block is not None
123
96
  _validate_operand(stmt_block)
124
97
  assert QCallable.CURRENT_EXPANDABLE is not None
125
98
  source_ref = get_source_ref(sys._getframe(1))
126
- QCallable.CURRENT_EXPANDABLE.append_statement_to_body(
127
- Control(
128
- expression=Expression(expr=str(ctrl)),
129
- body=_operand_to_body(stmt_block, "stmt_block"),
130
- source_ref=source_ref,
131
- )
99
+ control_stmt = Control(
100
+ expression=Expression(expr=str(ctrl)),
101
+ body=_operand_to_body(stmt_block, "stmt_block"),
102
+ source_ref=source_ref,
132
103
  )
104
+ control_stmt.set_generative_block("body", stmt_block)
105
+ QCallable.CURRENT_EXPANDABLE.append_statement_to_body(control_stmt)
133
106
 
134
107
 
135
108
  def inplace_add(
@@ -164,56 +137,22 @@ def inplace_xor(
164
137
  )
165
138
 
166
139
 
167
- @overload # FIXME: Remove overloading (CAD-21932)
168
140
  def within_apply(
169
141
  within: Callable[[], None],
170
142
  apply: Callable[[], None],
171
143
  ) -> None:
172
- pass
173
-
174
-
175
- @overload
176
- def within_apply(
177
- within: Callable[[], List[None]],
178
- apply: Callable[[], List[None]],
179
- ) -> None:
180
- pass
181
-
182
-
183
- def within_apply( # type:ignore[misc]
184
- within: Optional[Callable[[], None]] = None,
185
- apply: Optional[Callable[[], None]] = None,
186
- compute: Optional[Callable[[], None]] = None,
187
- action: Optional[Callable[[], None]] = None,
188
- ) -> None:
189
- if compute is not None or action is not None:
190
- warnings.warn(
191
- "Parameters 'compute' and 'action' of function 'within_apply' have "
192
- "been renamed to 'within' and 'apply' respectively. Parameters 'compute' "
193
- "and 'action' will be deprecated in a future release.\nHint: Change "
194
- "`within_apply(compute=..., action=...)` to "
195
- "`within_apply(within=..., apply=...)` or `within_apply(..., ...)`.",
196
- category=DeprecationWarning,
197
- stacklevel=2,
198
- )
199
- if compute is not None:
200
- within = compute
201
- if action is not None:
202
- apply = action
203
- if TYPE_CHECKING:
204
- assert within is not None
205
- assert apply is not None
206
144
  _validate_operand(within)
207
145
  _validate_operand(apply)
208
146
  assert QCallable.CURRENT_EXPANDABLE is not None
209
147
  source_ref = get_source_ref(sys._getframe(1))
210
- QCallable.CURRENT_EXPANDABLE.append_statement_to_body(
211
- WithinApply(
212
- compute=_operand_to_body(within, "within"),
213
- action=_operand_to_body(apply, "apply"),
214
- source_ref=source_ref,
215
- )
148
+ within_apply_stmt = WithinApply(
149
+ compute=_operand_to_body(within, "within"),
150
+ action=_operand_to_body(apply, "apply"),
151
+ source_ref=source_ref,
216
152
  )
153
+ within_apply_stmt.set_generative_block("within", within)
154
+ within_apply_stmt.set_generative_block("apply", apply)
155
+ QCallable.CURRENT_EXPANDABLE.append_statement_to_body(within_apply_stmt)
217
156
 
218
157
 
219
158
  def repeat(count: Union[SymbolicExpr, int], iteration: Callable[[int], None]) -> None:
@@ -236,98 +175,52 @@ def repeat(count: Union[SymbolicExpr, int], iteration: Callable[[int], None]) ->
236
175
  "Argument 'iteration' to 'repeat' should be a callable that takes one integer argument."
237
176
  )
238
177
 
239
- QCallable.CURRENT_EXPANDABLE.append_statement_to_body(
240
- Repeat(
241
- iter_var=inspect.getfullargspec(iteration).args[0],
242
- count=Expression(expr=str(count)),
243
- body=iteration_operand.body,
244
- source_ref=source_ref,
245
- )
178
+ repeat_stmt = Repeat(
179
+ iter_var=inspect.getfullargspec(iteration).args[0],
180
+ count=Expression(expr=str(count)),
181
+ body=iteration_operand.body,
182
+ source_ref=source_ref,
246
183
  )
184
+ repeat_stmt.set_generative_block("body", iteration)
185
+ QCallable.CURRENT_EXPANDABLE.append_statement_to_body(repeat_stmt)
247
186
 
248
187
 
249
- @overload # FIXME: Remove overloading (CAD-21932)
250
- def power(
251
- exponent: SymbolicExpr,
252
- stmt_block: Union[QCallable, Callable[[], None]],
253
- ) -> None:
254
- pass
255
-
256
-
257
- @overload
258
188
  def power(
259
- exponent: int,
189
+ exponent: Union[SymbolicExpr, int],
260
190
  stmt_block: Union[QCallable, Callable[[], None]],
261
191
  ) -> None:
262
- pass
263
-
264
-
265
- def power(
266
- exponent: Optional[Union[SymbolicExpr, int]] = None,
267
- stmt_block: Optional[Union[QCallable, Callable[[], None]]] = None,
268
- power: Optional[Union[SymbolicExpr, int]] = None,
269
- operand: Optional[Union[QCallable, Callable[[], None]]] = None,
270
- ) -> None:
271
- if power is not None or operand is not None:
272
- warnings.warn(
273
- "Parameters 'exponent' and 'operand' of function 'power' have been "
274
- "renamed to 'exponent' and 'stmt_block' respectively. Parameters "
275
- "'exponent' and 'operand' will be deprecated in a future release.\nHint: "
276
- "Change `power(power=..., operand=...)` to "
277
- "`power(exponent=..., stmt_block=...)` or `power(..., ...)`.",
278
- category=DeprecationWarning,
279
- stacklevel=2,
280
- )
281
- if power is not None:
282
- exponent = power
283
- if operand is not None:
284
- stmt_block = operand
285
- if TYPE_CHECKING:
286
- assert exponent is not None
287
- assert stmt_block is not None
288
192
  _validate_operand(stmt_block)
289
193
  assert QCallable.CURRENT_EXPANDABLE is not None
290
194
  source_ref = get_source_ref(sys._getframe(1))
291
- QCallable.CURRENT_EXPANDABLE.append_statement_to_body(
292
- Power(
293
- power=Expression(expr=str(exponent)),
294
- body=_operand_to_body(stmt_block, "stmt_block"),
295
- source_ref=source_ref,
296
- )
195
+ power_stmt = Power(
196
+ power=Expression(expr=str(exponent)),
197
+ body=_operand_to_body(stmt_block, "stmt_block"),
198
+ source_ref=source_ref,
297
199
  )
200
+ power_stmt.set_generative_block("body", stmt_block)
201
+ QCallable.CURRENT_EXPANDABLE.append_statement_to_body(power_stmt)
298
202
 
299
203
 
300
- @overload # FIXME: Remove overloading (CAD-21932)
301
- def invert(stmt_block: QCallable) -> None:
302
- pass
303
-
304
-
305
- @overload
306
- def invert(stmt_block: Callable[[], None]) -> None:
307
- pass
204
+ def invert(stmt_block: Union[QCallable, Callable[[], None]]) -> None:
205
+ _validate_operand(stmt_block)
206
+ assert QCallable.CURRENT_EXPANDABLE is not None
207
+ source_ref = get_source_ref(sys._getframe(1))
208
+ invert_stmt = Invert(
209
+ body=_operand_to_body(stmt_block, "stmt_block"), source_ref=source_ref
210
+ )
211
+ invert_stmt.set_generative_block("body", stmt_block)
212
+ QCallable.CURRENT_EXPANDABLE.append_statement_to_body(invert_stmt)
308
213
 
309
214
 
310
- def invert(
311
- stmt_block: Optional[Union[QCallable, Callable[[], None]]] = None,
312
- operand: Optional[Union[QCallable, Callable[[], None]]] = None,
313
- ) -> None:
314
- if operand is not None:
315
- warnings.warn(
316
- "Parameter 'operand' of function 'invert' has been renamed to "
317
- "'stmt_block'. Parameter 'operand' will be deprecated in a future "
318
- "release.\nHint: Change `invert(operand=...)` to `invert(stmt_block=...)` "
319
- "or `invert(...)`.",
320
- category=DeprecationWarning,
321
- stacklevel=2,
322
- )
323
- stmt_block = operand
324
- if TYPE_CHECKING:
325
- assert stmt_block is not None
326
- _validate_operand(stmt_block)
215
+ def phase(expr: SymbolicExpr, theta: float = 1.0) -> None:
327
216
  assert QCallable.CURRENT_EXPANDABLE is not None
328
217
  source_ref = get_source_ref(sys._getframe(1))
329
218
  QCallable.CURRENT_EXPANDABLE.append_statement_to_body(
330
- Invert(body=_operand_to_body(stmt_block, "stmt_block"), source_ref=source_ref)
219
+ PhaseOperation(
220
+ expression=Expression(expr=str(expr)),
221
+ theta=Expression(expr=str(theta)),
222
+ source_ref=source_ref,
223
+ )
331
224
  )
332
225
 
333
226
 
@@ -414,6 +307,7 @@ __all__ = [
414
307
  "power",
415
308
  "within_apply",
416
309
  "repeat",
310
+ "phase",
417
311
  ]
418
312
 
419
313
 
@@ -174,7 +174,7 @@ def _interpret_generative_model(
174
174
  gen_model,
175
175
  {gen_func.func_decl.name: gen_func.func_decl for gen_func in gen_functions},
176
176
  )
177
- interpreter = Interpreter(gen_model, gen_functions)
177
+ interpreter = Interpreter(gen_model, gen_functions, is_frontend=True)
178
178
  set_frontend_interpreter(interpreter)
179
179
  functions_dict = nameables_to_dict(interpreter.expand().functions)
180
180
 
@@ -1,7 +1,8 @@
1
1
  from contextlib import contextmanager
2
- from typing import TYPE_CHECKING, Iterator, Optional
2
+ from typing import TYPE_CHECKING, Any, Iterator, Optional
3
3
 
4
4
  from classiq.interface.exceptions import ClassiqError
5
+ from classiq.interface.generator.expressions.expression import Expression
5
6
 
6
7
  if TYPE_CHECKING:
7
8
  from classiq.model_expansions.interpreter import Interpreter
@@ -20,8 +21,10 @@ def generative_mode_context(generative: bool) -> Iterator[None]:
20
21
  global _GENERATIVE_MODE
21
22
  previous = _GENERATIVE_MODE
22
23
  _GENERATIVE_MODE = generative
23
- yield
24
- _GENERATIVE_MODE = previous
24
+ try:
25
+ yield
26
+ finally:
27
+ _GENERATIVE_MODE = previous
25
28
 
26
29
 
27
30
  @contextmanager
@@ -29,8 +32,10 @@ def enable_generative_expansion(enabled: bool) -> Iterator[None]:
29
32
  global _GENERATIVE_ENABLED_SWITCH
30
33
  previous = _GENERATIVE_ENABLED_SWITCH
31
34
  _GENERATIVE_ENABLED_SWITCH = enabled
32
- yield
33
- _GENERATIVE_ENABLED_SWITCH = previous
35
+ try:
36
+ yield
37
+ finally:
38
+ _GENERATIVE_ENABLED_SWITCH = previous
34
39
 
35
40
 
36
41
  def is_generative_expansion_enabled() -> bool:
@@ -46,3 +51,7 @@ def get_frontend_interpreter() -> "Interpreter":
46
51
  if _FRONTEND_INTERPRETER is None:
47
52
  raise ClassiqError("Interpreter was not set")
48
53
  return _FRONTEND_INTERPRETER
54
+
55
+
56
+ def interpret_expression(expr: str) -> Any:
57
+ return get_frontend_interpreter().evaluate(Expression(expr=expr)).value
@@ -37,6 +37,7 @@ from classiq.interface.model.inplace_binary_operation import InplaceBinaryOperat
37
37
  from classiq.interface.model.invert import Invert
38
38
  from classiq.interface.model.model import Model
39
39
  from classiq.interface.model.native_function_definition import NativeFunctionDefinition
40
+ from classiq.interface.model.phase_operation import PhaseOperation
40
41
  from classiq.interface.model.port_declaration import (
41
42
  AnonPortDeclaration,
42
43
  )
@@ -46,6 +47,7 @@ from classiq.interface.model.quantum_expressions.amplitude_loading_operation imp
46
47
  )
47
48
  from classiq.interface.model.quantum_expressions.arithmetic_operation import (
48
49
  ArithmeticOperation,
50
+ ArithmeticOperationKind,
49
51
  )
50
52
  from classiq.interface.model.quantum_function_call import (
51
53
  OperandIdentifier,
@@ -185,13 +187,11 @@ class DSLPrettyPrinter(Visitor):
185
187
  def visit_QuantumNumeric(self, qtype: QuantumNumeric) -> str:
186
188
  params = ""
187
189
  if qtype.size is not None:
188
- assert qtype.is_signed is not None
189
- assert qtype.fraction_digits is not None
190
-
191
190
  params = "<{}>".format(
192
191
  ", ".join(
193
192
  self.visit(param)
194
193
  for param in [qtype.size, qtype.is_signed, qtype.fraction_digits]
194
+ if param is not None
195
195
  )
196
196
  )
197
197
 
@@ -255,6 +255,11 @@ class DSLPrettyPrinter(Visitor):
255
255
  control += f"{self._indent}}}\n"
256
256
  return control
257
257
 
258
+ def visit_PhaseOperation(self, op: PhaseOperation) -> str:
259
+ theta = f", {self.visit(op.theta)}" if op.theta else ""
260
+ phase = f"{self._indent}phase ({self.visit(op.expression)}{theta});\n"
261
+ return phase
262
+
258
263
  def visit_ClassicalIf(self, op: ClassicalIf) -> str:
259
264
  classical_if = f"{self._indent}if ({self.visit(op.condition)}) {{\n"
260
265
  if not op.then:
@@ -340,7 +345,12 @@ class DSLPrettyPrinter(Visitor):
340
345
  return f"{self.visit(var_ref.base_handle)}.{self.visit(var_ref.field)}"
341
346
 
342
347
  def visit_ArithmeticOperation(self, arith_op: ArithmeticOperation) -> str:
343
- op = "^=" if arith_op.inplace_result else "="
348
+ if arith_op.get_operation_kind() == ArithmeticOperationKind.Assignment:
349
+ op = "="
350
+ elif arith_op.get_operation_kind() == ArithmeticOperationKind.InplaceXor:
351
+ op = "^="
352
+ else:
353
+ op = "+="
344
354
  return f"{self._indent}{self.visit(arith_op.result_var)} {op} {self.visit(arith_op.expression)};\n"
345
355
 
346
356
  def visit_AmplitudeLoadingOperation(
@@ -39,6 +39,7 @@ from classiq.interface.model.inplace_binary_operation import InplaceBinaryOperat
39
39
  from classiq.interface.model.invert import Invert
40
40
  from classiq.interface.model.model import Model
41
41
  from classiq.interface.model.native_function_definition import NativeFunctionDefinition
42
+ from classiq.interface.model.phase_operation import PhaseOperation
42
43
  from classiq.interface.model.port_declaration import AnonPortDeclaration
43
44
  from classiq.interface.model.power import Power
44
45
  from classiq.interface.model.quantum_expressions.amplitude_loading_operation import (
@@ -46,6 +47,7 @@ from classiq.interface.model.quantum_expressions.amplitude_loading_operation imp
46
47
  )
47
48
  from classiq.interface.model.quantum_expressions.arithmetic_operation import (
48
49
  ArithmeticOperation,
50
+ ArithmeticOperationKind,
49
51
  )
50
52
  from classiq.interface.model.quantum_expressions.quantum_expression import (
51
53
  QuantumAssignmentOperation,
@@ -114,12 +116,10 @@ class VariableDeclarationAssignment(Visitor):
114
116
 
115
117
  params = []
116
118
  if qtype.size is not None:
117
- assert qtype.is_signed is not None
118
- assert qtype.fraction_digits is not None
119
-
120
119
  params = [
121
120
  self.pretty_printer.visit(param)
122
121
  for param in [qtype.size, qtype.is_signed, qtype.fraction_digits]
122
+ if param is not None
123
123
  ]
124
124
 
125
125
  return "QNum", params
@@ -392,6 +392,11 @@ class PythonPrettyPrinter(Visitor):
392
392
  self._imports["control"] = 1
393
393
  return f"{self._indent}control({self.visit(op.expression)}, {self._visit_body(op.body)})\n"
394
394
 
395
+ def visit_PhaseOperation(self, op: PhaseOperation) -> str:
396
+ self._imports["phase"] = 1
397
+ theta = f", {self.visit(op.theta)}" if op.theta is not None else ""
398
+ return f"{self._indent}phase({self.visit(op.expression)},{theta})\n"
399
+
395
400
  def visit_ClassicalIf(self, op: ClassicalIf) -> str:
396
401
  self._imports["if_"] = 1
397
402
  return f"{self._indent}if_(condition={self.visit(op.condition)}, then={self._visit_body(op.then)}, else_={self._visit_body(op.else_)})\n"
@@ -464,7 +469,12 @@ class PythonPrettyPrinter(Visitor):
464
469
  return f"{self.visit(var_ref.base_handle)}.{self.visit(var_ref.field)}"
465
470
 
466
471
  def visit_ArithmeticOperation(self, arith_op: ArithmeticOperation) -> str:
467
- op = "^=" if arith_op.inplace_result else "|="
472
+ if arith_op.get_operation_kind() == ArithmeticOperationKind.Assignment:
473
+ op = "|="
474
+ elif arith_op.get_operation_kind() == ArithmeticOperationKind.InplaceXor:
475
+ op = "^="
476
+ else:
477
+ op = "+="
468
478
  return f"{self._indent}{self.visit(arith_op.result_var)} {op} {self.visit(arith_op.expression)}\n"
469
479
 
470
480
  def visit_AmplitudeLoadingOperation(
@@ -16,11 +16,16 @@ from classiq.qmod.qmod_parameter import CParam, CParamList, CParamStruct
16
16
  from classiq.qmod.symbolic_expr import SymbolicExpr
17
17
  from classiq.qmod.utilities import qmod_val_to_expr_str
18
18
 
19
+ QMODULE_ERROR_MESSAGE = (
20
+ "Error trying to add a constant to a model without a current QModule."
21
+ )
22
+
19
23
 
20
24
  class QConstant(SymbolicExpr):
21
25
  CURRENT_QMODULE: Optional[ModelStateContainer] = None
22
26
 
23
27
  def __init__(self, name: str, py_type: type, value: Any) -> None:
28
+ super().__init__(name, False)
24
29
  self.name = name
25
30
  self._py_type = py_type
26
31
  self._value = value
@@ -30,10 +35,12 @@ class QConstant(SymbolicExpr):
30
35
  QConstant.CURRENT_QMODULE = qmodule
31
36
 
32
37
  def add_to_model(self) -> None:
38
+ from classiq.qmod.builtins.constants import __all__ as builtin_constants
39
+
40
+ if self.name in builtin_constants:
41
+ return
33
42
  if QConstant.CURRENT_QMODULE is None:
34
- raise ClassiqError(
35
- "Error trying to add a constant to a model without a current QModule."
36
- )
43
+ raise ClassiqError(QMODULE_ERROR_MESSAGE)
37
44
 
38
45
  expr = qmod_val_to_expr_str(self._value)
39
46
  if (
@@ -42,26 +49,32 @@ class QConstant(SymbolicExpr):
42
49
  ):
43
50
  raise ClassiqError(f"Constant {self.name} is already defined in the model")
44
51
 
52
+ QConstant.CURRENT_QMODULE.constants[self.name] = self._get_constant_node()
53
+
54
+ def _get_constant_node(self) -> Constant:
45
55
  if isinstance(self._value, QConstant):
46
- QConstant.CURRENT_QMODULE.constants[self.name] = Constant(
56
+ if QConstant.CURRENT_QMODULE is None:
57
+ raise ClassiqError(QMODULE_ERROR_MESSAGE)
58
+ return Constant(
47
59
  name=self.name,
48
60
  const_type=QConstant.CURRENT_QMODULE.constants[
49
61
  self._value.name
50
62
  ].const_type,
51
63
  value=Expression(expr=self._value.name),
52
64
  )
53
- else:
54
- qmod_type = python_type_to_qmod(
55
- self._py_type, qmodule=QConstant.CURRENT_QMODULE
56
- )
57
- if qmod_type is None:
58
- raise ClassiqError("Invalid QMOD type")
59
65
 
60
- QConstant.CURRENT_QMODULE.constants[self.name] = Constant(
61
- name=self.name,
62
- const_type=qmod_type,
63
- value=Expression(expr=expr),
64
- )
66
+ qmod_type = python_type_to_qmod(
67
+ self._py_type, qmodule=QConstant.CURRENT_QMODULE
68
+ )
69
+ if qmod_type is None:
70
+ raise ClassiqError("Invalid QMOD type")
71
+
72
+ expr = qmod_val_to_expr_str(self._value)
73
+ return Constant(
74
+ name=self.name,
75
+ const_type=qmod_type,
76
+ value=Expression(expr=expr),
77
+ )
65
78
 
66
79
  def __getattr__(self, name: str) -> CParam:
67
80
  self.add_to_model()
@@ -100,6 +113,3 @@ class QConstant(SymbolicExpr):
100
113
  qmod_type,
101
114
  QConstant.CURRENT_QMODULE,
102
115
  )[item]
103
-
104
- def __str__(self) -> str:
105
- return self.name