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,11 +1,10 @@
1
1
  import itertools
2
2
  from collections import UserDict
3
+ from contextlib import contextmanager
3
4
  from dataclasses import dataclass
4
5
  from functools import singledispatch
5
6
  from typing import TYPE_CHECKING, Any, Dict, Iterator, Optional, Type, TypeVar, Union
6
7
 
7
- from typing_extensions import Self
8
-
9
8
  from classiq.interface.exceptions import (
10
9
  ClassiqExpansionError,
11
10
  ClassiqInternalExpansionError,
@@ -180,13 +179,13 @@ class Scope(EvaluatedUserDict):
180
179
  data: Optional[Dict[str, Evaluated]] = None,
181
180
  /,
182
181
  *,
183
- parent: Optional[Self] = None,
182
+ parent: Optional["Scope"] = None,
184
183
  ) -> None:
185
184
  super().__init__(data or {})
186
- self._parent: Optional[Self] = parent
185
+ self._parent: Optional["Scope"] = parent
187
186
 
188
187
  @property
189
- def parent(self) -> Optional[Self]:
188
+ def parent(self) -> Optional["Scope"]:
190
189
  return self._parent
191
190
 
192
191
  def __getitem__(self, name: str) -> Evaluated:
@@ -224,3 +223,15 @@ class Scope(EvaluatedUserDict):
224
223
  (self.data or {}) | (other.data or {}),
225
224
  parent=parent,
226
225
  )
226
+
227
+ def _copy(self) -> "Scope":
228
+ return Scope(
229
+ self.data, parent=None if self._parent is None else self._parent.copy()
230
+ )
231
+
232
+ @contextmanager
233
+ def freeze(self) -> Iterator[None]:
234
+ previous = self._copy()
235
+ yield
236
+ self.data = previous.data
237
+ self._parent = previous._parent
@@ -1,5 +1,6 @@
1
1
  from typing import List, Sequence
2
2
 
3
+ from classiq.interface.exceptions import ClassiqError
3
4
  from classiq.interface.generator.constant import Constant
4
5
  from classiq.interface.generator.expressions.expression_constants import (
5
6
  CPARAM_EXECUTION_SUFFIX,
@@ -22,6 +23,7 @@ from classiq.model_expansions.evaluators.parameter_types import (
22
23
  )
23
24
  from classiq.model_expansions.expression_renamer import ExpressionRenamer
24
25
  from classiq.model_expansions.scope import Evaluated, QuantumSymbol, Scope
26
+ from classiq.qmod.builtins import BUILTIN_CONSTANTS
25
27
  from classiq.qmod.builtins.functions import (
26
28
  CORE_LIB_DECLS,
27
29
  OPEN_LIB_DECLS,
@@ -71,7 +73,7 @@ def _add_generative_functions_to_scope(
71
73
  name=function.func_decl.name,
72
74
  positional_arg_declarations=function.func_decl.positional_arg_declarations,
73
75
  scope=Scope(parent=scope),
74
- generative_function=function,
76
+ generative_blocks={"body": function},
75
77
  )
76
78
  )
77
79
 
@@ -104,6 +106,14 @@ def _init_builtins_scope(scope: Scope) -> None:
104
106
  is_atomic=True,
105
107
  )
106
108
  )
109
+ for constant in BUILTIN_CONSTANTS:
110
+ value = constant.value
111
+ if not value.is_evaluated():
112
+ raise ClassiqError(
113
+ f"Unevaluated built-in constants not supported. Offending constant: "
114
+ f"{constant.name} = {value}"
115
+ )
116
+ scope[constant.name] = Evaluated(value=value.value.value)
107
117
 
108
118
 
109
119
  def add_entry_point_params_to_scope(
@@ -1,5 +1,5 @@
1
1
  import ast
2
- from typing import TYPE_CHECKING, Dict, Type
2
+ from typing import TYPE_CHECKING, Dict, Type, cast
3
3
 
4
4
  from classiq.interface.exceptions import ClassiqExpansionError
5
5
 
@@ -116,6 +116,9 @@ class ExpressionSympyTranslator(ast.NodeTransformer):
116
116
  return self.generic_visit(node)
117
117
 
118
118
  def visit_Call(self, node: ast.Call) -> ast.AST:
119
+ if isinstance(node.func, ast.Name) and node.func.id == "Piecewise":
120
+ return self._visit_piecewise(node)
121
+
119
122
  if (
120
123
  not isinstance(node.func, ast.Name)
121
124
  or node.func.id not in self.SPECIAL_FUNCTIONS
@@ -128,6 +131,25 @@ class ExpressionSympyTranslator(ast.NodeTransformer):
128
131
  keywords=[self.visit(arg) for arg in node.keywords],
129
132
  )
130
133
 
134
+ def _visit_piecewise(self, node: ast.Call) -> ast.AST:
135
+ # sympy Piecewise expression may include bitwise operations:
136
+ # Piecewise((0, Eq(x, 0)), (0.5, Eq(x, 1) | Eq(x, 2)), (1, True))
137
+ # ^
138
+ # We should avoid converting these to 'BitwiseOr' and such.
139
+ return ast.Call(
140
+ func=node.func,
141
+ args=[
142
+ ast.Tuple(
143
+ elts=(
144
+ self.generic_visit(cast(ast.Tuple, arg).elts[0]),
145
+ cast(ast.Tuple, arg).elts[1],
146
+ )
147
+ )
148
+ for arg in node.args
149
+ ],
150
+ keywords=node.keywords,
151
+ )
152
+
131
153
  def visit_Subscript(self, node: ast.Subscript) -> ast.AST:
132
154
  if isinstance(node.slice, ast.Slice):
133
155
  if node.slice.lower is not None:
@@ -1,6 +1,6 @@
1
1
  import ast
2
2
  from contextlib import contextmanager
3
- from typing import Dict, Iterator, Optional, Set, Union
3
+ from typing import Dict, Iterator, List, Optional, Union
4
4
 
5
5
  from classiq.interface.exceptions import (
6
6
  ClassiqExpansionError,
@@ -23,10 +23,14 @@ from classiq.interface.model.handle_binding import (
23
23
 
24
24
  class VarRefCollector(ast.NodeVisitor):
25
25
  def __init__(self, ignore_duplicated_handles: bool = False) -> None:
26
- self.var_handles: Set[HandleBinding] = set()
26
+ self._var_handles: Dict[HandleBinding, bool] = {}
27
27
  self._ignore_duplicated_handles = ignore_duplicated_handles
28
28
  self._is_nested = False
29
29
 
30
+ @property
31
+ def var_handles(self) -> List[HandleBinding]:
32
+ return list(self._var_handles)
33
+
30
34
  def visit(self, node: ast.AST) -> Union[
31
35
  SubscriptHandleBinding,
32
36
  SlicedHandleBinding,
@@ -35,8 +39,8 @@ class VarRefCollector(ast.NodeVisitor):
35
39
  None,
36
40
  ]:
37
41
  res = super().visit(node)
38
- if not self._ignore_duplicated_handles and len(self.var_handles) != len(
39
- {handle.name for handle in self.var_handles}
42
+ if not self._ignore_duplicated_handles and len(self._var_handles) != len(
43
+ {handle.name for handle in self._var_handles}
40
44
  ):
41
45
  raise ClassiqExpansionError(
42
46
  "Multiple non-identical variable references in an expression are not supported."
@@ -71,7 +75,7 @@ class VarRefCollector(ast.NodeVisitor):
71
75
  raise ClassiqInternalExpansionError("Unevaluated slice.")
72
76
 
73
77
  if not self._is_nested:
74
- self.var_handles.add(handle)
78
+ self._var_handles[handle] = True
75
79
  return handle
76
80
 
77
81
  def visit_Attribute(self, node: ast.Attribute) -> Optional[FieldHandleBinding]:
@@ -84,7 +88,7 @@ class VarRefCollector(ast.NodeVisitor):
84
88
  field=node.attr,
85
89
  )
86
90
  if not self._is_nested:
87
- self.var_handles.add(handle)
91
+ self._var_handles[handle] = True
88
92
  return handle
89
93
 
90
94
  def visit_Name(self, node: ast.Name) -> Optional[HandleBinding]:
@@ -94,7 +98,7 @@ class VarRefCollector(ast.NodeVisitor):
94
98
  return None
95
99
  handle = HandleBinding(name=node.id)
96
100
  if not self._is_nested:
97
- self.var_handles.add(handle)
101
+ self._var_handles[handle] = True
98
102
  return handle
99
103
 
100
104
  @contextmanager
@@ -6,6 +6,8 @@ from .classical_execution_primitives import (
6
6
  )
7
7
  from .classical_functions import * # noqa: F403
8
8
  from .classical_functions import __all__ as _builtin_classical_functions
9
+ from .constants import * # noqa: F403
10
+ from .constants import __all__ as _builtin_constants
9
11
  from .enums import * # noqa: F403
10
12
  from .enums import __all__ as _builtin_enums
11
13
  from .functions import * # noqa: F403
@@ -18,6 +20,13 @@ from .structs import __all__ as _builtin_structs
18
20
  FinanceFunctionInput.update_forward_refs(
19
21
  FinanceFunctionType=FinanceFunctionType # noqa: F405
20
22
  )
23
+ BUILTIN_CONSTANTS = [
24
+ constant._get_constant_node()
25
+ for constant in [
26
+ SIGNED, # noqa: F405
27
+ UNSIGNED, # noqa: F405
28
+ ]
29
+ ]
21
30
 
22
31
  __all__ = (
23
32
  _builtin_enums
@@ -26,4 +35,5 @@ __all__ = (
26
35
  + _builtin_operations
27
36
  + _builtin_classical_execution_primitives
28
37
  + _builtin_classical_functions
38
+ + _builtin_constants
29
39
  )
@@ -0,0 +1,10 @@
1
+ from classiq.qmod.cparam import CBool
2
+ from classiq.qmod.qmod_constant import QConstant
3
+
4
+ SIGNED = QConstant("SIGNED", CBool, True)
5
+ UNSIGNED = QConstant("UNSIGNED", CBool, False)
6
+
7
+ __all__ = [
8
+ "SIGNED",
9
+ "UNSIGNED",
10
+ ]
@@ -340,7 +340,10 @@ def inplace_prepare_int(value: CInt, target: QArray[QBit]) -> None:
340
340
 
341
341
 
342
342
  @qfunc(external=True)
343
- def prepare_int(value: CInt, out: Output[QNum]) -> None:
343
+ def prepare_int(
344
+ value: CInt,
345
+ out: Output[QNum[Literal["floor(log(value, 2)) + 1"], Literal[False], Literal[0]]],
346
+ ) -> None:
344
347
  """
345
348
  [Qmod Classiq-library function]
346
349
 
@@ -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
@@ -80,57 +76,33 @@ def if_(
80
76
  _validate_operand(else_)
81
77
  assert QCallable.CURRENT_EXPANDABLE is not None
82
78
  source_ref = get_source_ref(sys._getframe(1))
83
- QCallable.CURRENT_EXPANDABLE.append_statement_to_body(
84
- ClassicalIf(
85
- condition=Expression(expr=str(condition)),
86
- then=_operand_to_body(then, "then"),
87
- else_=_operand_to_body(else_, "else") if else_ != _MISSING_VALUE else [], # type: ignore[arg-type]
88
- source_ref=source_ref,
89
- )
90
- )
91
79
 
92
-
93
- @overload # FIXME: Remove overloading (CAD-21932)
94
- def control(
95
- ctrl: Union[QBit, QArray[QBit]], stmt_block: Union[QCallable, Callable[[], None]]
96
- ) -> None:
97
- pass
98
-
99
-
100
- @overload
101
- def control(
102
- ctrl: SymbolicExpr, stmt_block: Union[QCallable, Callable[[], None]]
103
- ) -> None:
104
- 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)
105
90
 
106
91
 
107
92
  def control(
108
93
  ctrl: Union[SymbolicExpr, QBit, QArray[QBit]],
109
- stmt_block: Optional[Union[QCallable, Callable[[], None]]] = None,
110
- operand: Optional[Union[QCallable, Callable[[], None]]] = None,
94
+ stmt_block: Union[QCallable, Callable[[], None]],
111
95
  ) -> None:
112
- if operand is not None:
113
- warnings.warn(
114
- "Parameter 'operand' of function 'control' has been renamed to "
115
- "'stmt_block'. Parameter 'operand' will be deprecated in a future "
116
- "release.\nHint: Change `control(ctrl=..., operand=...)` to "
117
- "`control(ctrl=..., stmt_block=...)` or `control(..., ...)`.",
118
- category=DeprecationWarning,
119
- stacklevel=2,
120
- )
121
- stmt_block = operand
122
- if TYPE_CHECKING:
123
- assert stmt_block is not None
124
96
  _validate_operand(stmt_block)
125
97
  assert QCallable.CURRENT_EXPANDABLE is not None
126
98
  source_ref = get_source_ref(sys._getframe(1))
127
- QCallable.CURRENT_EXPANDABLE.append_statement_to_body(
128
- Control(
129
- expression=Expression(expr=str(ctrl)),
130
- body=_operand_to_body(stmt_block, "stmt_block"),
131
- source_ref=source_ref,
132
- )
99
+ control_stmt = Control(
100
+ expression=Expression(expr=str(ctrl)),
101
+ body=_operand_to_body(stmt_block, "stmt_block"),
102
+ source_ref=source_ref,
133
103
  )
104
+ control_stmt.set_generative_block("body", stmt_block)
105
+ QCallable.CURRENT_EXPANDABLE.append_statement_to_body(control_stmt)
134
106
 
135
107
 
136
108
  def inplace_add(
@@ -165,56 +137,22 @@ def inplace_xor(
165
137
  )
166
138
 
167
139
 
168
- @overload # FIXME: Remove overloading (CAD-21932)
169
140
  def within_apply(
170
141
  within: Callable[[], None],
171
142
  apply: Callable[[], None],
172
143
  ) -> None:
173
- pass
174
-
175
-
176
- @overload
177
- def within_apply(
178
- within: Callable[[], List[None]],
179
- apply: Callable[[], List[None]],
180
- ) -> None:
181
- pass
182
-
183
-
184
- def within_apply( # type:ignore[misc]
185
- within: Optional[Callable[[], None]] = None,
186
- apply: Optional[Callable[[], None]] = None,
187
- compute: Optional[Callable[[], None]] = None,
188
- action: Optional[Callable[[], None]] = None,
189
- ) -> None:
190
- if compute is not None or action is not None:
191
- warnings.warn(
192
- "Parameters 'compute' and 'action' of function 'within_apply' have "
193
- "been renamed to 'within' and 'apply' respectively. Parameters 'compute' "
194
- "and 'action' will be deprecated in a future release.\nHint: Change "
195
- "`within_apply(compute=..., action=...)` to "
196
- "`within_apply(within=..., apply=...)` or `within_apply(..., ...)`.",
197
- category=DeprecationWarning,
198
- stacklevel=2,
199
- )
200
- if compute is not None:
201
- within = compute
202
- if action is not None:
203
- apply = action
204
- if TYPE_CHECKING:
205
- assert within is not None
206
- assert apply is not None
207
144
  _validate_operand(within)
208
145
  _validate_operand(apply)
209
146
  assert QCallable.CURRENT_EXPANDABLE is not None
210
147
  source_ref = get_source_ref(sys._getframe(1))
211
- QCallable.CURRENT_EXPANDABLE.append_statement_to_body(
212
- WithinApply(
213
- compute=_operand_to_body(within, "within"),
214
- action=_operand_to_body(apply, "apply"),
215
- source_ref=source_ref,
216
- )
148
+ within_apply_stmt = WithinApply(
149
+ compute=_operand_to_body(within, "within"),
150
+ action=_operand_to_body(apply, "apply"),
151
+ source_ref=source_ref,
217
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)
218
156
 
219
157
 
220
158
  def repeat(count: Union[SymbolicExpr, int], iteration: Callable[[int], None]) -> None:
@@ -237,99 +175,41 @@ def repeat(count: Union[SymbolicExpr, int], iteration: Callable[[int], None]) ->
237
175
  "Argument 'iteration' to 'repeat' should be a callable that takes one integer argument."
238
176
  )
239
177
 
240
- QCallable.CURRENT_EXPANDABLE.append_statement_to_body(
241
- Repeat(
242
- iter_var=inspect.getfullargspec(iteration).args[0],
243
- count=Expression(expr=str(count)),
244
- body=iteration_operand.body,
245
- source_ref=source_ref,
246
- )
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,
247
183
  )
184
+ repeat_stmt.set_generative_block("body", iteration)
185
+ QCallable.CURRENT_EXPANDABLE.append_statement_to_body(repeat_stmt)
248
186
 
249
187
 
250
- @overload # FIXME: Remove overloading (CAD-21932)
251
188
  def power(
252
- exponent: SymbolicExpr,
189
+ exponent: Union[SymbolicExpr, int],
253
190
  stmt_block: Union[QCallable, Callable[[], None]],
254
191
  ) -> None:
255
- pass
256
-
257
-
258
- @overload
259
- def power(
260
- exponent: int,
261
- stmt_block: Union[QCallable, Callable[[], None]],
262
- ) -> None:
263
- pass
264
-
265
-
266
- def power(
267
- exponent: Optional[Union[SymbolicExpr, int]] = None,
268
- stmt_block: Optional[Union[QCallable, Callable[[], None]]] = None,
269
- power: Optional[Union[SymbolicExpr, int]] = None,
270
- operand: Optional[Union[QCallable, Callable[[], None]]] = None,
271
- ) -> None:
272
- if power is not None or operand is not None:
273
- warnings.warn(
274
- "Parameters 'exponent' and 'operand' of function 'power' have been "
275
- "renamed to 'exponent' and 'stmt_block' respectively. Parameters "
276
- "'exponent' and 'operand' will be deprecated in a future release.\nHint: "
277
- "Change `power(power=..., operand=...)` to "
278
- "`power(exponent=..., stmt_block=...)` or `power(..., ...)`.",
279
- category=DeprecationWarning,
280
- stacklevel=2,
281
- )
282
- if power is not None:
283
- exponent = power
284
- if operand is not None:
285
- stmt_block = operand
286
- if TYPE_CHECKING:
287
- assert exponent is not None
288
- assert stmt_block is not None
289
192
  _validate_operand(stmt_block)
290
193
  assert QCallable.CURRENT_EXPANDABLE is not None
291
194
  source_ref = get_source_ref(sys._getframe(1))
292
- QCallable.CURRENT_EXPANDABLE.append_statement_to_body(
293
- Power(
294
- power=Expression(expr=str(exponent)),
295
- body=_operand_to_body(stmt_block, "stmt_block"),
296
- source_ref=source_ref,
297
- )
195
+ power_stmt = Power(
196
+ power=Expression(expr=str(exponent)),
197
+ body=_operand_to_body(stmt_block, "stmt_block"),
198
+ source_ref=source_ref,
298
199
  )
200
+ power_stmt.set_generative_block("body", stmt_block)
201
+ QCallable.CURRENT_EXPANDABLE.append_statement_to_body(power_stmt)
299
202
 
300
203
 
301
- @overload # FIXME: Remove overloading (CAD-21932)
302
- def invert(stmt_block: QCallable) -> None:
303
- pass
304
-
305
-
306
- @overload
307
- def invert(stmt_block: Callable[[], None]) -> None:
308
- pass
309
-
310
-
311
- def invert(
312
- stmt_block: Optional[Union[QCallable, Callable[[], None]]] = None,
313
- operand: Optional[Union[QCallable, Callable[[], None]]] = None,
314
- ) -> None:
315
- if operand is not None:
316
- warnings.warn(
317
- "Parameter 'operand' of function 'invert' has been renamed to "
318
- "'stmt_block'. Parameter 'operand' will be deprecated in a future "
319
- "release.\nHint: Change `invert(operand=...)` to `invert(stmt_block=...)` "
320
- "or `invert(...)`.",
321
- category=DeprecationWarning,
322
- stacklevel=2,
323
- )
324
- stmt_block = operand
325
- if TYPE_CHECKING:
326
- assert stmt_block is not None
204
+ def invert(stmt_block: Union[QCallable, Callable[[], None]]) -> None:
327
205
  _validate_operand(stmt_block)
328
206
  assert QCallable.CURRENT_EXPANDABLE is not None
329
207
  source_ref = get_source_ref(sys._getframe(1))
330
- QCallable.CURRENT_EXPANDABLE.append_statement_to_body(
331
- Invert(body=_operand_to_body(stmt_block, "stmt_block"), source_ref=source_ref)
208
+ invert_stmt = Invert(
209
+ body=_operand_to_body(stmt_block, "stmt_block"), source_ref=source_ref
332
210
  )
211
+ invert_stmt.set_generative_block("body", stmt_block)
212
+ QCallable.CURRENT_EXPANDABLE.append_statement_to_body(invert_stmt)
333
213
 
334
214
 
335
215
  def phase(expr: SymbolicExpr, theta: float = 1.0) -> None:
@@ -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
@@ -47,6 +47,7 @@ from classiq.interface.model.quantum_expressions.amplitude_loading_operation imp
47
47
  )
48
48
  from classiq.interface.model.quantum_expressions.arithmetic_operation import (
49
49
  ArithmeticOperation,
50
+ ArithmeticOperationKind,
50
51
  )
51
52
  from classiq.interface.model.quantum_function_call import (
52
53
  OperandIdentifier,
@@ -186,13 +187,11 @@ class DSLPrettyPrinter(Visitor):
186
187
  def visit_QuantumNumeric(self, qtype: QuantumNumeric) -> str:
187
188
  params = ""
188
189
  if qtype.size is not None:
189
- assert qtype.is_signed is not None
190
- assert qtype.fraction_digits is not None
191
-
192
190
  params = "<{}>".format(
193
191
  ", ".join(
194
192
  self.visit(param)
195
193
  for param in [qtype.size, qtype.is_signed, qtype.fraction_digits]
194
+ if param is not None
196
195
  )
197
196
  )
198
197
 
@@ -258,7 +257,7 @@ class DSLPrettyPrinter(Visitor):
258
257
 
259
258
  def visit_PhaseOperation(self, op: PhaseOperation) -> str:
260
259
  theta = f", {self.visit(op.theta)}" if op.theta else ""
261
- phase = f"{self._indent}phase ({self.visit(op.expression)}{theta})\n"
260
+ phase = f"{self._indent}phase ({self.visit(op.expression)}{theta});\n"
262
261
  return phase
263
262
 
264
263
  def visit_ClassicalIf(self, op: ClassicalIf) -> str:
@@ -346,7 +345,12 @@ class DSLPrettyPrinter(Visitor):
346
345
  return f"{self.visit(var_ref.base_handle)}.{self.visit(var_ref.field)}"
347
346
 
348
347
  def visit_ArithmeticOperation(self, arith_op: ArithmeticOperation) -> str:
349
- op = "^=" if arith_op.inplace_result else "="
348
+ if arith_op.operation_kind == ArithmeticOperationKind.Assignment:
349
+ op = "="
350
+ elif arith_op.operation_kind == ArithmeticOperationKind.InplaceXor:
351
+ op = "^="
352
+ else:
353
+ op = "+="
350
354
  return f"{self._indent}{self.visit(arith_op.result_var)} {op} {self.visit(arith_op.expression)};\n"
351
355
 
352
356
  def visit_AmplitudeLoadingOperation(
@@ -47,6 +47,7 @@ from classiq.interface.model.quantum_expressions.amplitude_loading_operation imp
47
47
  )
48
48
  from classiq.interface.model.quantum_expressions.arithmetic_operation import (
49
49
  ArithmeticOperation,
50
+ ArithmeticOperationKind,
50
51
  )
51
52
  from classiq.interface.model.quantum_expressions.quantum_expression import (
52
53
  QuantumAssignmentOperation,
@@ -115,12 +116,10 @@ class VariableDeclarationAssignment(Visitor):
115
116
 
116
117
  params = []
117
118
  if qtype.size is not None:
118
- assert qtype.is_signed is not None
119
- assert qtype.fraction_digits is not None
120
-
121
119
  params = [
122
120
  self.pretty_printer.visit(param)
123
121
  for param in [qtype.size, qtype.is_signed, qtype.fraction_digits]
122
+ if param is not None
124
123
  ]
125
124
 
126
125
  return "QNum", params
@@ -470,7 +469,12 @@ class PythonPrettyPrinter(Visitor):
470
469
  return f"{self.visit(var_ref.base_handle)}.{self.visit(var_ref.field)}"
471
470
 
472
471
  def visit_ArithmeticOperation(self, arith_op: ArithmeticOperation) -> str:
473
- op = "^=" if arith_op.inplace_result else "|="
472
+ if arith_op.operation_kind == ArithmeticOperationKind.Assignment:
473
+ op = "|="
474
+ elif arith_op.operation_kind == ArithmeticOperationKind.InplaceXor:
475
+ op = "^="
476
+ else:
477
+ op = "+="
474
478
  return f"{self._indent}{self.visit(arith_op.result_var)} {op} {self.visit(arith_op.expression)}\n"
475
479
 
476
480
  def visit_AmplitudeLoadingOperation(