classiq 0.90.0__py3-none-any.whl → 0.91.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 (41) hide show
  1. classiq/evaluators/expression_evaluator.py +24 -124
  2. classiq/evaluators/qmod_expression_visitors/qmod_expression_simplifier.py +10 -3
  3. classiq/evaluators/qmod_node_evaluators/utils.py +0 -8
  4. classiq/interface/_version.py +1 -1
  5. classiq/interface/executor/result.py +22 -3
  6. classiq/interface/generator/expressions/expression_types.py +2 -0
  7. classiq/interface/generator/expressions/proxies/classical/classical_array_proxy.py +3 -8
  8. classiq/interface/generator/functions/classical_type.py +2 -5
  9. classiq/interface/generator/functions/type_name.py +0 -12
  10. classiq/interface/model/quantum_type.py +0 -39
  11. classiq/model_expansions/capturing/captured_vars.py +3 -0
  12. classiq/model_expansions/function_builder.py +18 -2
  13. classiq/model_expansions/interpreters/frontend_generative_interpreter.py +0 -10
  14. classiq/model_expansions/interpreters/generative_interpreter.py +63 -18
  15. classiq/model_expansions/quantum_operations/emitter.py +13 -3
  16. classiq/model_expansions/quantum_operations/expression_evaluator.py +49 -5
  17. classiq/model_expansions/scope.py +5 -14
  18. classiq/model_expansions/utils/handles_collector.py +7 -0
  19. classiq/qmod/builtins/operations.py +7 -3
  20. classiq/qmod/qmod_variable.py +3 -4
  21. classiq/qmod/semantics/error_manager.py +34 -15
  22. classiq/qmod/symbolic.py +15 -4
  23. classiq/qmod/utilities.py +4 -1
  24. classiq/synthesis.py +1 -2
  25. {classiq-0.90.0.dist-info → classiq-0.91.1.dist-info}/METADATA +1 -1
  26. {classiq-0.90.0.dist-info → classiq-0.91.1.dist-info}/RECORD +27 -41
  27. classiq/interface/generator/expressions/handle_identifier.py +0 -6
  28. classiq/interface/generator/expressions/proxies/classical/any_classical_value.py +0 -41
  29. classiq/interface/generator/expressions/proxies/quantum/__init__.py +0 -0
  30. classiq/interface/generator/expressions/proxies/quantum/qmod_qarray_proxy.py +0 -80
  31. classiq/interface/generator/expressions/proxies/quantum/qmod_qscalar_proxy.py +0 -77
  32. classiq/interface/generator/expressions/proxies/quantum/qmod_qstruct_proxy.py +0 -38
  33. classiq/interface/generator/expressions/proxies/quantum/qmod_sized_proxy.py +0 -39
  34. classiq/interface/generator/expressions/type_proxy.py +0 -10
  35. classiq/model_expansions/atomic_expression_functions_defs.py +0 -395
  36. classiq/model_expansions/model_tables.py +0 -18
  37. classiq/model_expansions/sympy_conversion/__init__.py +0 -0
  38. classiq/model_expansions/sympy_conversion/expression_to_sympy.py +0 -181
  39. classiq/model_expansions/sympy_conversion/sympy_to_python.py +0 -136
  40. classiq/model_expansions/utils/sympy_utils.py +0 -24
  41. {classiq-0.90.0.dist-info → classiq-0.91.1.dist-info}/WHEEL +0 -0
@@ -1,38 +1,34 @@
1
- import ast
2
- from collections.abc import Mapping
3
- from enum import EnumMeta
1
+ from enum import Enum
4
2
  from typing import Any
5
3
 
6
- from sympy import SympifyError, sympify
4
+ import sympy
7
5
 
8
- from classiq.interface.exceptions import ClassiqExpansionError
6
+ from classiq.interface.exceptions import ClassiqInternalExpansionError
9
7
  from classiq.interface.generator.constant import Constant
10
- from classiq.interface.generator.expressions.evaluated_expression import (
11
- EvaluatedExpression,
12
- )
13
- from classiq.interface.generator.expressions.expression import Expression
14
- from classiq.interface.generator.expressions.expression_types import ExpressionValue
15
- from classiq.interface.generator.expressions.non_symbolic_expr import NonSymbolicExpr
16
- from classiq.interface.generator.expressions.proxies.quantum.qmod_sized_proxy import (
17
- QmodSizedProxy,
18
- )
19
- from classiq.interface.generator.expressions.sympy_supported_expressions import (
20
- SYMPY_SUPPORTED_EXPRESSIONS,
8
+ from classiq.interface.generator.expressions.expression_types import RuntimeConstant
9
+ from classiq.interface.generator.expressions.proxies.classical.qmod_struct_instance import (
10
+ QmodStructInstance,
21
11
  )
22
12
 
23
13
  from classiq.evaluators.classical_expression import evaluate_classical_expression
24
- from classiq.model_expansions.atomic_expression_functions_defs import (
25
- ATOMIC_EXPRESSION_FUNCTIONS,
26
- qmod_val_to_python,
27
- )
28
14
  from classiq.model_expansions.scope import Evaluated, Scope
29
- from classiq.model_expansions.sympy_conversion.expression_to_sympy import (
30
- translate_to_sympy,
31
- )
32
- from classiq.model_expansions.sympy_conversion.sympy_to_python import sympy_to_python
33
- from classiq.qmod import symbolic
34
- from classiq.qmod.builtins.enums import BUILTIN_ENUM_DECLARATIONS
35
- from classiq.qmod.model_state_container import QMODULE
15
+
16
+
17
+ def qmod_val_to_python(val: RuntimeConstant) -> Any:
18
+ if isinstance(val, (int, float, bool, complex, Enum)):
19
+ return val
20
+ if isinstance(val, list):
21
+ return [qmod_val_to_python(item) for item in val]
22
+ if isinstance(val, QmodStructInstance):
23
+ return {
24
+ field_name: qmod_val_to_python(field_val)
25
+ for field_name, field_val in val.fields.items()
26
+ }
27
+ if isinstance(val, sympy.Expr):
28
+ return val.evalf()
29
+ raise ClassiqInternalExpansionError(
30
+ f"Could not convert Qmod value {str(val)!r} of type {type(val).__name__} to Python"
31
+ )
36
32
 
37
33
 
38
34
  def evaluate_constants(constants: list[Constant]) -> Scope:
@@ -47,102 +43,6 @@ def evaluate_constants(constants: list[Constant]) -> Scope:
47
43
  def evaluate_constants_as_python(constants: list[Constant]) -> dict[str, Any]:
48
44
  evaluated = evaluate_constants(constants)
49
45
  return {
50
- constant.name: qmod_val_to_python(
51
- evaluated[constant.name].value, constant.const_type
52
- )
46
+ constant.name: qmod_val_to_python(evaluated[constant.name].value)
53
47
  for constant in constants
54
48
  }
55
-
56
-
57
- def _quick_eval(expr: str) -> Any:
58
- try:
59
- return int(expr)
60
- except ValueError:
61
- pass
62
- try:
63
- return float(expr)
64
- except ValueError:
65
- pass
66
- return None
67
-
68
-
69
- def evaluate(
70
- expr: Expression, locals_dict: Mapping[str, EvaluatedExpression]
71
- ) -> EvaluatedExpression:
72
- val = _quick_eval(expr.expr)
73
- if val is not None:
74
- return EvaluatedExpression(value=val)
75
-
76
- model_locals: dict[str, Any] = {}
77
- model_locals.update(ATOMIC_EXPRESSION_FUNCTIONS)
78
- model_locals.update(
79
- {
80
- enum_decl.name: enum_decl.create_enum()
81
- for enum_decl in (QMODULE.enum_decls | BUILTIN_ENUM_DECLARATIONS).values()
82
- }
83
- )
84
- # locals override builtin-functions
85
- model_locals.update({name: expr.value for name, expr in locals_dict.items()})
86
-
87
- _validate_undefined_vars(expr.expr, model_locals)
88
-
89
- sympy_expr = translate_to_sympy(expr.expr)
90
- try:
91
- sympify_result = sympify(sympy_expr, locals=model_locals)
92
- except (TypeError, IndexError) as e:
93
- raise ClassiqExpansionError(str(e)) from e
94
- except AttributeError as e:
95
- if isinstance(e.obj, EnumMeta):
96
- raise ClassiqExpansionError(
97
- f"Enum {e.obj.__name__} has no member {e.name!r}. Available members: "
98
- f"{', '.join(e.obj.__members__)}"
99
- ) from e
100
- raise
101
- except SympifyError as e:
102
- expr = e.expr
103
- if isinstance(expr, QmodSizedProxy) and isinstance(expr, NonSymbolicExpr):
104
- raise ClassiqExpansionError(
105
- f"{expr.type_name} {str(expr)!r} does not support arithmetic operations"
106
- ) from e
107
- raise
108
-
109
- return EvaluatedExpression(
110
- value=sympy_to_python(sympify_result, locals=model_locals)
111
- )
112
-
113
-
114
- def _validate_undefined_vars(
115
- expr: str, model_locals: dict[str, ExpressionValue]
116
- ) -> None:
117
- id_visitor = _VarsCollector()
118
- id_visitor.visit(ast.parse(expr))
119
- identifiers = id_visitor.vars
120
- undefined_vars = (
121
- identifiers
122
- - model_locals.keys()
123
- - set(SYMPY_SUPPORTED_EXPRESSIONS)
124
- - set(symbolic.__all__)
125
- )
126
-
127
- if len(undefined_vars) == 1:
128
- undefined_var = undefined_vars.__iter__().__next__()
129
- raise ClassiqExpansionError(f"Variable {undefined_var!r} is undefined")
130
- elif len(undefined_vars) > 1:
131
- raise ClassiqExpansionError(f"Variables {list(undefined_vars)} are undefined")
132
-
133
-
134
- class _VarsCollector(ast.NodeTransformer):
135
- def __init__(self) -> None:
136
- self.vars: set[str] = set()
137
-
138
- def visit_Name(self, node: ast.Name) -> None:
139
- self.vars.add(node.id)
140
-
141
- def visit_Call(self, node: ast.Call) -> None:
142
- func = node.func
143
- self.visit(func)
144
- if not isinstance(func, ast.Name) or func.id != "struct_literal":
145
- for arg in node.args:
146
- self.visit(arg)
147
- for kw in node.keywords:
148
- self.visit(kw)
@@ -23,7 +23,14 @@ from classiq.evaluators.qmod_expression_visitors.sympy_wrappers import (
23
23
  LShift,
24
24
  RShift,
25
25
  )
26
- from classiq.model_expansions.atomic_expression_functions_defs import do_div
26
+
27
+
28
+ def _div_wrapper(lhs: Any, rhs: Any) -> Any:
29
+ res = lhs / rhs
30
+ if isinstance(res, sympy.Expr):
31
+ res = res.evalf()
32
+ return res
33
+
27
34
 
28
35
  _SYMPY_WRAPPERS = {
29
36
  wrapper.__name__: wrapper
@@ -36,7 +43,7 @@ _SYMPY_WRAPPERS = {
36
43
  RShift,
37
44
  ]
38
45
  } | {
39
- do_div.__name__: do_div,
46
+ _div_wrapper.__name__: _div_wrapper,
40
47
  }
41
48
 
42
49
  _PY_NODE = TypeVar("_PY_NODE", bound=ast.AST)
@@ -172,7 +179,7 @@ class _SympyCompatibilityTransformer(OutOfPlaceNodeTransformer):
172
179
  sympy_func = BitwiseAnd.__name__
173
180
  elif isinstance(node.op, ast.Div):
174
181
  return ast.Call(
175
- func=ast.Name(id=do_div.__name__),
182
+ func=ast.Name(id=_div_wrapper.__name__),
176
183
  args=[node.left, node.right],
177
184
  keywords=[],
178
185
  )
@@ -65,14 +65,6 @@ def get_numeric_properties(
65
65
  return size, is_signed, fraction_digits
66
66
 
67
67
 
68
- def qnum_is_qint(qmod_type: QuantumType) -> bool:
69
- return isinstance(qmod_type, QuantumBit) or (
70
- isinstance(qmod_type, QuantumNumeric)
71
- and (not qmod_type.has_sign or not qmod_type.sign_value)
72
- and (not qmod_type.has_fraction_digits or qmod_type.fraction_digits_value == 0)
73
- )
74
-
75
-
76
68
  def element_types(
77
69
  classical_type: Union[ClassicalArray, ClassicalTuple],
78
70
  ) -> list[ClassicalType]:
@@ -3,5 +3,5 @@ from packaging.version import Version
3
3
  # This file was generated automatically
4
4
  # Please don't track in version control (DONTTRACK)
5
5
 
6
- SEMVER_VERSION = '0.90.0'
6
+ SEMVER_VERSION = '0.91.1'
7
7
  VERSION = str(Version(SEMVER_VERSION))
@@ -1,4 +1,6 @@
1
+ import cmath
1
2
  import functools
3
+ import math
2
4
  import operator
3
5
  from collections import defaultdict
4
6
  from collections.abc import Iterator, Mapping
@@ -46,6 +48,8 @@ BITSTRING = "bitstring"
46
48
  PROBABILITY = "probability"
47
49
  AMPLITUDE = "amplitude"
48
50
  COUNT = "count"
51
+ MAGNITUDE = "magnitude"
52
+ PHASE = "phase"
49
53
 
50
54
  if TYPE_CHECKING:
51
55
  DotAccessParsedState = Mapping[Name, Any]
@@ -191,6 +195,14 @@ def _flatten_columns(df: pd.DataFrame, columns_to_flatten: list[str]) -> pd.Data
191
195
  return final_df[valid_final_columns]
192
196
 
193
197
 
198
+ def _pretty_magnitude_phase_from_amplitude(amplitude: complex) -> tuple[str, str]:
199
+ magnitude_str = f"{abs(amplitude):.2f}"
200
+ phase = cmath.phase(amplitude) / math.pi
201
+ phase_str = f"{phase:.2f}π"
202
+
203
+ return magnitude_str, phase_str
204
+
205
+
194
206
  class ExecutionDetails(BaseModel, QmodPyObject):
195
207
  vendor_format_result: dict[str, Any] = pydantic.Field(
196
208
  ..., description="Result in proprietary vendor format"
@@ -377,11 +389,14 @@ class ExecutionDetails(BaseModel, QmodPyObject):
377
389
  for bitstring, amplitude in self.state_vector.items():
378
390
  data[BITSTRING].append(bitstring)
379
391
  data[AMPLITUDE].append(amplitude)
392
+ magnitude, phase = _pretty_magnitude_phase_from_amplitude(amplitude)
393
+ data[MAGNITUDE].append(magnitude)
394
+ data[PHASE].append(phase)
380
395
  data[PROBABILITY].append(abs(amplitude) ** 2)
381
396
  for name, value in self.parsed_state_vector_states[bitstring].items():
382
397
  data[name].append(value)
383
398
 
384
- final_columns = [AMPLITUDE, PROBABILITY, BITSTRING]
399
+ final_columns = [AMPLITUDE, MAGNITUDE, PHASE, PROBABILITY, BITSTRING]
385
400
  columns = [
386
401
  col for col in data.keys() if col not in final_columns
387
402
  ] + final_columns
@@ -390,12 +405,16 @@ class ExecutionDetails(BaseModel, QmodPyObject):
390
405
 
391
406
  @functools.cached_property
392
407
  def dataframe(self) -> pd.DataFrame:
393
- reserved_words = frozenset([BITSTRING, PROBABILITY, COUNT, AMPLITUDE])
408
+ reserved_words = frozenset(
409
+ [BITSTRING, PROBABILITY, COUNT, AMPLITUDE, PHASE, MAGNITUDE]
410
+ )
394
411
  _invalid_output_names = reserved_words.intersection(
395
412
  self.output_qubits_map.keys()
396
413
  )
397
414
  if _invalid_output_names:
398
- raise ClassiqValueError(f"Invalid output names: {_invalid_output_names}")
415
+ raise ClassiqValueError(
416
+ f"The following names are reserved and cannot be used as outputs when constructing a dataframe: {_invalid_output_names}"
417
+ )
399
418
 
400
419
  if self.state_vector:
401
420
  df = self._state_vector_df()
@@ -1,3 +1,4 @@
1
+ from enum import IntEnum
1
2
  from typing import TYPE_CHECKING, Union
2
3
 
3
4
  import sympy
@@ -15,6 +16,7 @@ RuntimeConstant = Union[
15
16
  list,
16
17
  bool,
17
18
  complex,
19
+ IntEnum,
18
20
  QmodStructInstance,
19
21
  sympy.Basic,
20
22
  ]
@@ -1,5 +1,5 @@
1
1
  from collections.abc import Mapping
2
- from typing import TYPE_CHECKING, Any, Union
2
+ from typing import TYPE_CHECKING, Any, Optional, Union
3
3
 
4
4
  import sympy
5
5
  from sympy import Integer
@@ -8,9 +8,6 @@ from typing_extensions import TypeGuard
8
8
  from classiq.interface.exceptions import ClassiqIndexError
9
9
  from classiq.interface.generator.expressions.expression import Expression
10
10
  from classiq.interface.generator.expressions.non_symbolic_expr import NonSymbolicExpr
11
- from classiq.interface.generator.expressions.proxies.classical.any_classical_value import (
12
- AnyClassicalValue,
13
- )
14
11
  from classiq.interface.generator.expressions.proxies.classical.classical_proxy import (
15
12
  ClassicalProxy,
16
13
  )
@@ -28,8 +25,6 @@ if TYPE_CHECKING:
28
25
 
29
26
 
30
27
  def _is_int(val: Any) -> TypeGuard[Union[int, sympy.Basic]]:
31
- if isinstance(val, AnyClassicalValue):
32
- return False
33
28
  if isinstance(val, sympy.Basic):
34
29
  return val.is_Number
35
30
  return isinstance(val, int)
@@ -99,7 +94,7 @@ class ClassicalArrayProxy(ClassicalSequenceProxy):
99
94
  self,
100
95
  handle: HandleBinding,
101
96
  element_type: "ConcreteClassicalType",
102
- length: "ExpressionValue",
97
+ length: Optional["ExpressionValue"],
103
98
  ) -> None:
104
99
  super().__init__(handle)
105
100
  self._element_type = element_type
@@ -108,7 +103,7 @@ class ClassicalArrayProxy(ClassicalSequenceProxy):
108
103
  self._length = length
109
104
 
110
105
  @property
111
- def length(self) -> "ExpressionValue":
106
+ def length(self) -> Optional["ExpressionValue"]:
112
107
  return self._length
113
108
 
114
109
  def get_slice_at(self, start: Any, stop: Any) -> ClassicalProxy:
@@ -9,9 +9,6 @@ from classiq.interface.ast_node import HashableASTNode
9
9
  from classiq.interface.exceptions import ClassiqInternalExpansionError
10
10
  from classiq.interface.generator.expressions.expression import Expression
11
11
  from classiq.interface.generator.expressions.expression_types import ExpressionValue
12
- from classiq.interface.generator.expressions.proxies.classical.any_classical_value import (
13
- AnyClassicalValue,
14
- )
15
12
  from classiq.interface.generator.expressions.proxies.classical.classical_array_proxy import (
16
13
  ClassicalArrayProxy,
17
14
  ClassicalTupleProxy,
@@ -163,9 +160,9 @@ class ClassicalArray(ClassicalType):
163
160
  return self.length.to_int_value()
164
161
 
165
162
  def get_classical_proxy(self, handle: HandleBinding) -> ClassicalProxy:
166
- length: ExpressionValue
163
+ length: Optional[ExpressionValue]
167
164
  if self.length is None:
168
- length = AnyClassicalValue(f"get_field({handle}, 'len')")
165
+ length = None
169
166
  elif not self.length.is_evaluated():
170
167
  raise ClassiqInternalExpansionError(
171
168
  "Classical list length is not evaluated"
@@ -15,9 +15,6 @@ from classiq.interface.generator.expressions.proxies.classical.classical_scalar_
15
15
  from classiq.interface.generator.expressions.proxies.classical.classical_struct_proxy import (
16
16
  ClassicalStructProxy,
17
17
  )
18
- from classiq.interface.generator.expressions.proxies.quantum.qmod_qstruct_proxy import (
19
- QmodQStructProxy,
20
- )
21
18
  from classiq.interface.generator.functions.classical_type import (
22
19
  ClassicalType,
23
20
  )
@@ -59,15 +56,6 @@ class TypeName(ClassicalType, QuantumType):
59
56
  field_type.size_in_bits for field_type in fields_types
60
57
  )
61
58
 
62
- def get_proxy(self, handle: "HandleBinding") -> "QmodQStructProxy":
63
- from classiq.interface.generator.expressions.proxies.quantum.qmod_qstruct_proxy import (
64
- QmodQStructProxy,
65
- )
66
-
67
- return QmodQStructProxy(
68
- handle=handle, struct_name=self.name, fields=self.fields
69
- )
70
-
71
59
  @property
72
60
  def qmod_type_name(self) -> str:
73
61
  return self.name
@@ -15,20 +15,8 @@ from classiq.interface.generator.arith.register_user_input import (
15
15
  RegisterUserInput,
16
16
  )
17
17
  from classiq.interface.generator.expressions.expression import Expression
18
- from classiq.interface.generator.expressions.proxies.quantum.qmod_qarray_proxy import (
19
- QmodQArrayProxy,
20
- )
21
- from classiq.interface.generator.expressions.proxies.quantum.qmod_qscalar_proxy import (
22
- QmodQBitProxy,
23
- QmodQNumProxy,
24
- QmodQScalarProxy,
25
- )
26
- from classiq.interface.generator.expressions.proxies.quantum.qmod_sized_proxy import (
27
- QmodSizedProxy,
28
- )
29
18
  from classiq.interface.helpers.custom_pydantic_types import PydanticFloatTuple
30
19
  from classiq.interface.helpers.pydantic_model_helpers import values_with_discriminator
31
- from classiq.interface.model.handle_binding import HandleBinding
32
20
 
33
21
  if TYPE_CHECKING:
34
22
  from classiq.interface.generator.functions.concrete_types import ConcreteQuantumType
@@ -59,9 +47,6 @@ class QuantumType(HashableASTNode):
59
47
  def minimal_size_in_bits(self) -> int:
60
48
  raise NotImplementedError
61
49
 
62
- def get_proxy(self, handle: "HandleBinding") -> QmodSizedProxy:
63
- return QmodSizedProxy(handle=handle, size=self.size_in_bits)
64
-
65
50
  @property
66
51
  def qmod_type_name(self) -> str:
67
52
  raise NotImplementedError
@@ -95,9 +80,6 @@ class QuantumType(HashableASTNode):
95
80
 
96
81
 
97
82
  class QuantumScalar(QuantumType):
98
- def get_proxy(self, handle: "HandleBinding") -> QmodQScalarProxy:
99
- return QmodQScalarProxy(handle, size=self.size_in_bits)
100
-
101
83
  @property
102
84
  def has_sign(self) -> bool:
103
85
  raise NotImplementedError
@@ -147,9 +129,6 @@ class QuantumBit(QuantumScalar):
147
129
  def qmod_type_name(self) -> str:
148
130
  return "QBit"
149
131
 
150
- def get_proxy(self, handle: "HandleBinding") -> QmodQBitProxy:
151
- return QmodQBitProxy(handle)
152
-
153
132
  @property
154
133
  def type_name(self) -> str:
155
134
  return "Quantum bit"
@@ -233,16 +212,6 @@ class QuantumBitvector(QuantumType):
233
212
  assert self.length is not None
234
213
  return self.length.to_int_value()
235
214
 
236
- def get_proxy(self, handle: "HandleBinding") -> QmodQArrayProxy:
237
- element_size = self.element_type.size_in_bits
238
- assert self.size_in_bits % element_size == 0
239
- return QmodQArrayProxy(
240
- handle,
241
- self.element_type,
242
- element_size,
243
- self.size_in_bits // element_size,
244
- )
245
-
246
215
  @property
247
216
  def qmod_type_name(self) -> str:
248
217
  element_type = [self.element_type.qmod_type_name]
@@ -382,14 +351,6 @@ class QuantumNumeric(QuantumScalar):
382
351
  ):
383
352
  self._size_in_bits = self.size.to_int_value()
384
353
 
385
- def get_proxy(self, handle: "HandleBinding") -> QmodQNumProxy:
386
- return QmodQNumProxy(
387
- handle,
388
- size=self.size_in_bits,
389
- fraction_digits=self.fraction_digits_value,
390
- is_signed=self.sign_value,
391
- )
392
-
393
354
  @property
394
355
  def qmod_type_name(self) -> str:
395
356
  if (
@@ -554,6 +554,9 @@ class CapturedVars:
554
554
  ],
555
555
  )
556
556
 
557
+ def get_captured_classical_vars(self) -> list[str]:
558
+ return [var.name for var in self._captured_classical_vars]
559
+
557
560
  def get_captured_parameters(self) -> list[PositionalArg]:
558
561
  decls: list[PositionalArg]
559
562
  decls = [
@@ -3,7 +3,10 @@ from contextlib import contextmanager
3
3
  from dataclasses import dataclass, field
4
4
  from typing import Generic, Optional, TypeVar
5
5
 
6
- from classiq.interface.exceptions import ClassiqInternalExpansionError
6
+ from classiq.interface.exceptions import (
7
+ ClassiqExpansionError,
8
+ ClassiqInternalExpansionError,
9
+ )
7
10
  from classiq.interface.generator.compiler_keywords import (
8
11
  EXPANDED_KEYWORD,
9
12
  LAMBDA_KEYWORD,
@@ -31,7 +34,7 @@ from classiq.model_expansions.capturing.captured_vars import (
31
34
  validate_end_state,
32
35
  )
33
36
  from classiq.model_expansions.closure import Closure, FunctionClosure
34
- from classiq.model_expansions.scope import Scope
37
+ from classiq.model_expansions.scope import ClassicalSymbol, Scope
35
38
  from classiq.model_expansions.utils.counted_name_allocator import CountedNameAllocator
36
39
 
37
40
  ClosureType = TypeVar("ClosureType", bound=Closure)
@@ -190,6 +193,8 @@ class OperationBuilder:
190
193
  self.current_function
191
194
  ).set_propagated()
192
195
  validate_captured_directions(captured_vars)
196
+ else:
197
+ self._validate_no_captured_runtime_params(captured_vars)
193
198
  if len(self._operations) < 2:
194
199
  return
195
200
  parent_block = self._operations[-2].blocks[self._blocks[-1]]
@@ -231,3 +236,14 @@ class OperationBuilder:
231
236
  raise ClassiqInternalExpansionError("Could not allocate function name")
232
237
 
233
238
  return name
239
+
240
+ def _validate_no_captured_runtime_params(self, captured_vars: CapturedVars) -> None:
241
+ if any(
242
+ var in self.current_scope
243
+ and isinstance(self.current_scope[var].value, ClassicalSymbol)
244
+ for var in captured_vars.get_captured_classical_vars()
245
+ ):
246
+ raise ClassiqExpansionError(
247
+ "Runtime classical variables can only be declared and used at the "
248
+ "function's top scope"
249
+ )
@@ -4,8 +4,6 @@ import os
4
4
  from pydantic import ValidationError
5
5
 
6
6
  from classiq.interface.exceptions import ClassiqError
7
- from classiq.interface.model.allocate import Allocate
8
- from classiq.interface.model.bind_operation import BindOperation
9
7
  from classiq.interface.model.quantum_function_call import QuantumFunctionCall
10
8
  from classiq.interface.source_reference import SourceReference
11
9
 
@@ -13,8 +11,6 @@ from classiq.model_expansions.closure import FunctionClosure, GenerativeFunction
13
11
  from classiq.model_expansions.interpreters.generative_interpreter import (
14
12
  GenerativeInterpreter,
15
13
  )
16
- from classiq.model_expansions.quantum_operations import BindEmitter
17
- from classiq.model_expansions.quantum_operations.allocate import AllocateEmitter
18
14
  from classiq.model_expansions.quantum_operations.quantum_function_call import (
19
15
  DeclarativeQuantumFunctionCallEmitter,
20
16
  )
@@ -23,12 +19,6 @@ from classiq.qmod.model_state_container import QMODULE
23
19
 
24
20
 
25
21
  class FrontendGenerativeInterpreter(GenerativeInterpreter):
26
- def emit_allocate(self, allocate: Allocate) -> None:
27
- AllocateEmitter(self, allow_symbolic_attrs=True).emit(allocate)
28
-
29
- def emit_bind(self, bind: BindOperation) -> None:
30
- BindEmitter(self, allow_symbolic_size=True).emit(bind)
31
-
32
22
  def emit_quantum_function_call(self, call: QuantumFunctionCall) -> None:
33
23
  DeclarativeQuantumFunctionCallEmitter(self).emit(call)
34
24