classiq 0.88.0__py3-none-any.whl → 0.90.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.

Potentially problematic release.


This version of classiq might be problematic. Click here for more details.

Files changed (103) hide show
  1. classiq/__init__.py +1 -0
  2. classiq/_internals/api_wrapper.py +16 -32
  3. classiq/_internals/config.py +1 -1
  4. classiq/analyzer/show_interactive_hack.py +26 -1
  5. classiq/applications/chemistry/chemistry_model_constructor.py +14 -2
  6. classiq/applications/combinatorial_helpers/pyomo_utils.py +9 -6
  7. classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +2 -2
  8. classiq/applications/combinatorial_optimization/combinatorial_problem.py +16 -8
  9. classiq/evaluators/classical_expression.py +63 -41
  10. classiq/evaluators/control.py +31 -52
  11. classiq/evaluators/expression_evaluator.py +8 -4
  12. classiq/evaluators/parameter_types.py +200 -104
  13. classiq/evaluators/qmod_annotated_expression.py +10 -9
  14. classiq/evaluators/qmod_expression_visitors/qmod_expression_evaluator.py +2 -2
  15. classiq/evaluators/qmod_expression_visitors/qmod_expression_renamer.py +1 -1
  16. classiq/evaluators/qmod_node_evaluators/attribute_evaluation.py +66 -5
  17. classiq/evaluators/qmod_node_evaluators/classical_function_evaluation.py +12 -37
  18. classiq/evaluators/qmod_node_evaluators/constant_evaluation.py +8 -17
  19. classiq/evaluators/qmod_node_evaluators/measurement_evaluation.py +1 -1
  20. classiq/evaluators/qmod_node_evaluators/min_max_evaluation.py +7 -1
  21. classiq/evaluators/qmod_node_evaluators/name_evaluation.py +0 -1
  22. classiq/evaluators/qmod_node_evaluators/numeric_attrs_utils.py +9 -1
  23. classiq/evaluators/qmod_node_evaluators/utils.py +33 -0
  24. classiq/evaluators/qmod_type_inference/classical_type_inference.py +4 -7
  25. classiq/interface/_version.py +1 -1
  26. classiq/interface/analyzer/analysis_params.py +2 -26
  27. classiq/interface/analyzer/result.py +4 -0
  28. classiq/interface/backend/backend_preferences.py +1 -1
  29. classiq/interface/chemistry/ground_state_problem.py +16 -2
  30. classiq/interface/executor/optimizer_preferences.py +0 -112
  31. classiq/interface/generator/application_apis/chemistry_declarations.py +3 -1
  32. classiq/interface/generator/arith/arithmetic_expression_validator.py +2 -7
  33. classiq/interface/generator/arith/register_user_input.py +1 -1
  34. classiq/interface/generator/expressions/evaluated_expression.py +3 -13
  35. classiq/interface/generator/expressions/expression_types.py +8 -22
  36. classiq/interface/generator/expressions/proxies/classical/classical_proxy.py +2 -2
  37. classiq/interface/generator/expressions/proxies/classical/classical_struct_proxy.py +1 -2
  38. classiq/interface/generator/functions/classical_type.py +24 -3
  39. classiq/interface/generator/functions/concrete_types.py +1 -1
  40. classiq/interface/generator/functions/function_declaration.py +0 -4
  41. classiq/interface/generator/functions/type_name.py +25 -0
  42. classiq/interface/generator/generated_circuit_data.py +4 -0
  43. classiq/interface/generator/hardware_efficient_ansatz.py +1 -1
  44. classiq/interface/generator/preferences/qasm_to_qmod_params.py +14 -0
  45. classiq/interface/generator/quantum_function_call.py +3 -3
  46. classiq/interface/generator/user_defined_function_params.py +0 -3
  47. classiq/interface/helpers/model_normalizer.py +0 -6
  48. classiq/interface/ide/ide_data.py +1 -1
  49. classiq/interface/ide/visual_model.py +3 -2
  50. classiq/interface/model/block.py +5 -1
  51. classiq/interface/model/handle_binding.py +2 -2
  52. classiq/interface/model/port_declaration.py +2 -1
  53. classiq/interface/model/quantum_expressions/arithmetic_operation.py +16 -12
  54. classiq/interface/model/quantum_lambda_function.py +1 -1
  55. classiq/interface/model/quantum_statement.py +2 -4
  56. classiq/interface/model/quantum_type.py +47 -4
  57. classiq/interface/server/routes.py +2 -3
  58. classiq/model_expansions/atomic_expression_functions_defs.py +4 -22
  59. classiq/model_expansions/capturing/captured_vars.py +7 -3
  60. classiq/model_expansions/closure.py +8 -0
  61. classiq/model_expansions/interpreters/base_interpreter.py +84 -22
  62. classiq/model_expansions/interpreters/frontend_generative_interpreter.py +1 -1
  63. classiq/model_expansions/interpreters/generative_interpreter.py +7 -5
  64. classiq/model_expansions/quantum_operations/allocate.py +92 -21
  65. classiq/model_expansions/quantum_operations/assignment_result_processor.py +28 -27
  66. classiq/model_expansions/quantum_operations/call_emitter.py +32 -26
  67. classiq/model_expansions/quantum_operations/classical_var_emitter.py +6 -2
  68. classiq/model_expansions/quantum_operations/emitter.py +39 -69
  69. classiq/model_expansions/quantum_operations/expression_evaluator.py +13 -2
  70. classiq/model_expansions/quantum_operations/quantum_function_call.py +4 -5
  71. classiq/model_expansions/quantum_operations/variable_decleration.py +16 -11
  72. classiq/model_expansions/scope.py +36 -29
  73. classiq/model_expansions/scope_initialization.py +3 -6
  74. classiq/model_expansions/sympy_conversion/sympy_to_python.py +6 -2
  75. classiq/model_expansions/transformers/model_renamer.py +35 -64
  76. classiq/model_expansions/transformers/type_modifier_inference.py +6 -6
  77. classiq/model_expansions/visitors/boolean_expression_transformers.py +7 -31
  78. classiq/model_expansions/visitors/symbolic_param_inference.py +9 -3
  79. classiq/open_library/functions/__init__.py +2 -0
  80. classiq/open_library/functions/state_preparation.py +140 -5
  81. classiq/qmod/builtins/functions/allocation.py +8 -8
  82. classiq/qmod/builtins/functions/arithmetic.py +1 -1
  83. classiq/qmod/builtins/functions/chemistry.py +64 -0
  84. classiq/qmod/builtins/functions/exponentiation.py +7 -13
  85. classiq/qmod/builtins/functions/qsvm.py +1 -1
  86. classiq/qmod/builtins/operations.py +38 -10
  87. classiq/qmod/generative.py +2 -4
  88. classiq/qmod/native/pretty_printer.py +1 -1
  89. classiq/qmod/pretty_print/pretty_printer.py +1 -1
  90. classiq/qmod/qmod_constant.py +1 -1
  91. classiq/qmod/qmod_parameter.py +2 -2
  92. classiq/qmod/qmod_variable.py +62 -16
  93. classiq/qmod/quantum_expandable.py +1 -1
  94. classiq/synthesis.py +37 -1
  95. classiq/visualization.py +1 -1
  96. {classiq-0.88.0.dist-info → classiq-0.90.0.dist-info}/METADATA +2 -2
  97. {classiq-0.88.0.dist-info → classiq-0.90.0.dist-info}/RECORD +98 -102
  98. classiq/evaluators/arg_type_match.py +0 -168
  99. classiq/evaluators/classical_type_inference.py +0 -121
  100. classiq/interface/combinatorial_optimization/optimization_problem.py +0 -17
  101. classiq/interface/combinatorial_optimization/result.py +0 -9
  102. classiq/model_expansions/transformers/ast_renamer.py +0 -26
  103. {classiq-0.88.0.dist-info → classiq-0.90.0.dist-info}/WHEEL +0 -0
@@ -11,6 +11,7 @@ from classiq.interface.helpers.pydantic_model_helpers import nameables_to_dict
11
11
  from classiq.interface.model.quantum_function_declaration import (
12
12
  NamedParamsQuantumFunctionDeclaration,
13
13
  PositionalArg,
14
+ QuantumOperandDeclaration,
14
15
  )
15
16
  from classiq.interface.model.quantum_lambda_function import QuantumCallable
16
17
  from classiq.interface.model.quantum_statement import QuantumStatement
@@ -111,6 +112,13 @@ class FunctionClosure(Closure):
111
112
  def emit(self) -> QuantumCallable:
112
113
  return self.name
113
114
 
115
+ def as_operand_declaration(self, is_list: bool) -> QuantumOperandDeclaration:
116
+ return QuantumOperandDeclaration(
117
+ name=self.name,
118
+ positional_arg_declarations=self.positional_arg_declarations,
119
+ is_list=is_list,
120
+ )
121
+
114
122
 
115
123
  @dataclass(frozen=True)
116
124
  class GenerativeFunctionClosure(GenerativeClosure, FunctionClosure):
@@ -4,7 +4,7 @@ from collections import defaultdict
4
4
  from collections.abc import Sequence
5
5
  from contextlib import nullcontext
6
6
  from functools import singledispatchmethod
7
- from typing import Any, cast
7
+ from typing import Any, Callable, cast
8
8
 
9
9
  from pydantic import ValidationError
10
10
 
@@ -16,10 +16,8 @@ from classiq.interface.exceptions import (
16
16
  ClassiqExpansionError,
17
17
  ClassiqInternalExpansionError,
18
18
  )
19
- from classiq.interface.generator.expressions.atomic_expression_functions import (
20
- CLASSICAL_ATTRIBUTES,
21
- )
22
19
  from classiq.interface.generator.expressions.expression import Expression
20
+ from classiq.interface.generator.expressions.expression_types import ExpressionValue
23
21
  from classiq.interface.generator.functions.classical_function_declaration import (
24
22
  ClassicalFunctionDeclaration,
25
23
  )
@@ -42,8 +40,20 @@ from classiq.interface.model.quantum_lambda_function import (
42
40
  )
43
41
  from classiq.interface.model.quantum_statement import QuantumStatement
44
42
 
45
- from classiq.evaluators.classical_expression import (
46
- evaluate_classical_expression,
43
+ from classiq.evaluators.classical_expression import process_scope_val
44
+ from classiq.evaluators.qmod_annotated_expression import QmodAnnotatedExpression
45
+ from classiq.evaluators.qmod_expression_visitors.qmod_expression_bwc import (
46
+ QmodExpressionBwc,
47
+ )
48
+ from classiq.evaluators.qmod_expression_visitors.qmod_expression_evaluator import (
49
+ evaluate_qmod_expression,
50
+ )
51
+ from classiq.evaluators.qmod_expression_visitors.qmod_expression_simplifier import (
52
+ simplify_qmod_expression,
53
+ )
54
+ from classiq.evaluators.qmod_node_evaluators.utils import is_classical_integer
55
+ from classiq.evaluators.qmod_type_inference.classical_type_inference import (
56
+ infer_classical_type,
47
57
  )
48
58
  from classiq.model_expansions.closure import (
49
59
  Closure,
@@ -67,7 +77,6 @@ from classiq.model_expansions.scope_initialization import (
67
77
  init_top_level_scope,
68
78
  )
69
79
  from classiq.model_expansions.utils.counted_name_allocator import CountedNameAllocator
70
- from classiq.model_expansions.visitors.variable_references import VarRefCollector
71
80
  from classiq.qmod.builtins.constants import __all__ as builtin_constants
72
81
  from classiq.qmod.builtins.enums import BUILTIN_ENUM_DECLARATIONS
73
82
  from classiq.qmod.builtins.structs import BUILTIN_STRUCT_DECLARATIONS
@@ -75,7 +84,17 @@ from classiq.qmod.model_state_container import QMODULE
75
84
  from classiq.qmod.semantics.annotation.qstruct_annotator import QStructAnnotator
76
85
  from classiq.qmod.semantics.error_manager import ErrorManager
77
86
  from classiq.qmod.semantics.validation.model_validation import validate_model
78
- from classiq.qmod.utilities import qmod_val_to_expr_str
87
+
88
+
89
+ def _validate_index(val: ExpressionValue, subject: str) -> None:
90
+ if isinstance(val, QmodAnnotatedExpression):
91
+ val_type = val.get_type(val.root)
92
+ else:
93
+ val_type = infer_classical_type(val)
94
+ if not is_classical_integer(val_type):
95
+ raise ClassiqExpansionError(
96
+ f"{subject} indices must be integers, not {val_type.raw_qmod_type_name}"
97
+ )
79
98
 
80
99
 
81
100
  class BaseInterpreter:
@@ -130,6 +149,12 @@ class BaseInterpreter:
130
149
  try:
131
150
  with self._error_manager.call("main"):
132
151
  self._expand_main_func()
152
+ except RecursionError:
153
+ self._error_manager.add_error(
154
+ "RecursionError: maximum recursion depth exceeded. Tip: If your "
155
+ "program contains recursive functions, check for base cases using a "
156
+ "Python if-statement (instead of Qmod's `if_`)"
157
+ )
133
158
  except Exception as e:
134
159
  if isinstance(e, ClassiqInternalExpansionError) or debug_mode.get():
135
160
  raise e
@@ -175,22 +200,56 @@ class BaseInterpreter:
175
200
  def evaluate(self, expression: Any) -> Evaluated:
176
201
  raise NotImplementedError(f"Cannot evaluate {expression!r}")
177
202
 
203
+ def get_foreign_functions(self) -> dict[str, Callable]:
204
+ return {}
205
+
178
206
  @evaluate.register
179
- def evaluate_classical_expression(self, expression: Expression) -> Evaluated:
180
- expr = evaluate_classical_expression(expression, self._builder.current_scope)
181
- vrc = VarRefCollector(ignore_duplicated_handles=True, unevaluated=True)
182
- vrc.visit(ast.parse(qmod_val_to_expr_str(expr.value)))
183
- for handle in vrc.var_handles:
184
- if handle.name in self._builder.current_scope and isinstance(
185
- self._builder.current_scope[handle.name].value, QuantumSymbol
186
- ):
207
+ def evaluate_classical_expression(
208
+ self,
209
+ expression: Expression,
210
+ *,
211
+ simplify: bool = False,
212
+ treat_qnum_as_float: bool = False,
213
+ ) -> Evaluated:
214
+ if expression.is_evaluated():
215
+ return Evaluated(value=expression.value.value)
216
+ expr_ast = ast.parse(expression.expr, mode="eval").body
217
+ expr_ast = QmodExpressionBwc().visit(expr_ast)
218
+ expr_val = self._eval_expr(ast.unparse(expr_ast), treat_qnum_as_float)
219
+ if simplify and not expr_val.has_value(expr_val.root):
220
+ simplified_expr = simplify_qmod_expression(expr_val)
221
+ expr_val = self._eval_expr(simplified_expr, treat_qnum_as_float)
222
+ if expr_val.has_value(expr_val.root):
223
+ value = expr_val.get_value(expr_val.root)
224
+ else:
225
+ value = expr_val
226
+
227
+ return Evaluated(value=value)
228
+
229
+ def _eval_expr(
230
+ self, expr: str, treat_qnum_as_float: bool
231
+ ) -> QmodAnnotatedExpression:
232
+ return evaluate_qmod_expression(
233
+ expr,
234
+ treat_qnum_as_float=treat_qnum_as_float,
235
+ machine_precision=self._model.preferences.machine_precision,
236
+ classical_struct_declarations=list(QMODULE.type_decls.values()),
237
+ enum_declarations=list(QMODULE.enum_decls.values()),
238
+ classical_function_declarations=list(
239
+ ClassicalFunctionDeclaration.FOREIGN_FUNCTION_DECLARATIONS.values()
240
+ ),
241
+ classical_function_callables=self.get_foreign_functions(),
242
+ scope={
243
+ name: processed_val
244
+ for name in list(self._builder.current_scope)
187
245
  if (
188
- isinstance(handle, FieldHandleBinding)
189
- and handle.field in CLASSICAL_ATTRIBUTES
190
- ):
191
- handle = handle.base_handle
192
- self.evaluate(handle)
193
- return expr
246
+ processed_val := process_scope_val(
247
+ self._builder.current_scope[name].value
248
+ )
249
+ )
250
+ is not None
251
+ },
252
+ )
194
253
 
195
254
  @evaluate.register
196
255
  def evaluate_identifier(self, identifier: str) -> Evaluated:
@@ -216,6 +275,8 @@ class BaseInterpreter:
216
275
  )
217
276
  start = self.evaluate(sliced_handle_binding.start).value
218
277
  end = self.evaluate(sliced_handle_binding.end).value
278
+ _validate_index(start, "Slice")
279
+ _validate_index(end, "Slice")
219
280
  return Evaluated(value=quantum_variable[start:end])
220
281
 
221
282
  @evaluate.register
@@ -232,6 +293,7 @@ class BaseInterpreter:
232
293
  def evaluate_subscript_operand(self, subscript: OperandIdentifier) -> Evaluated:
233
294
  base_value = self.evaluate(subscript.name)
234
295
  index_value = self.evaluate(subscript.index).as_type(int)
296
+ _validate_index(index_value, "Array")
235
297
  return Evaluated(value=base_value.value[index_value])
236
298
 
237
299
  @evaluate.register
@@ -45,7 +45,7 @@ class FrontendGenerativeInterpreter(GenerativeInterpreter):
45
45
  return super()._get_main_closure(main_func)
46
46
 
47
47
  def process_exception(self, e: Exception) -> None:
48
- if not isinstance(e, (ClassiqError, ValidationError)):
48
+ if not isinstance(e, (ClassiqError, ValidationError, RecursionError)):
49
49
  frame = inspect.trace()[-1]
50
50
  module = inspect.getmodule(frame[0])
51
51
  if module is None or not module.__name__.startswith("classiq."):
@@ -183,7 +183,9 @@ class GenerativeInterpreter(BaseInterpreter):
183
183
  self,
184
184
  [
185
185
  HandleEvaluator(self, "result_var"),
186
- ExpressionEvaluator(self, "expression"),
186
+ ExpressionEvaluator(
187
+ self, "expression", simplify=True, treat_qnum_as_float=True
188
+ ),
187
189
  AssignmentResultProcessor(self),
188
190
  ],
189
191
  ).emit(op)
@@ -197,7 +199,7 @@ class GenerativeInterpreter(BaseInterpreter):
197
199
  self,
198
200
  [
199
201
  HandleEvaluator(self, "result_var"),
200
- ExpressionEvaluator(self, "expression"),
202
+ ExpressionEvaluator(self, "expression", simplify=True),
201
203
  ClassicalVarEmitter(self),
202
204
  AssignmentResultProcessor(self),
203
205
  ],
@@ -210,7 +212,7 @@ class GenerativeInterpreter(BaseInterpreter):
210
212
  [
211
213
  HandleEvaluator(self, "target"),
212
214
  HandleEvaluator(self, "value"),
213
- ExpressionEvaluator(self, "value"),
215
+ ExpressionEvaluator(self, "value", simplify=True),
214
216
  ],
215
217
  ).emit(op)
216
218
 
@@ -276,7 +278,7 @@ class GenerativeInterpreter(BaseInterpreter):
276
278
  CompositeEmitter[Control](
277
279
  self,
278
280
  [
279
- ExpressionEvaluator(self, "expression"),
281
+ ExpressionEvaluator(self, "expression", simplify=True),
280
282
  BlockEvaluator(
281
283
  self,
282
284
  CONTROL_OPERATOR_NAME,
@@ -301,7 +303,7 @@ class GenerativeInterpreter(BaseInterpreter):
301
303
  CompositeEmitter[PhaseOperation](
302
304
  self,
303
305
  [
304
- ExpressionEvaluator(self, "expression"),
306
+ ExpressionEvaluator(self, "expression", simplify=True),
305
307
  ExpressionEvaluator(self, "theta"),
306
308
  ],
307
309
  ).emit(phase)
@@ -1,10 +1,16 @@
1
- from typing import TYPE_CHECKING, Union
1
+ from typing import TYPE_CHECKING, Any, Union
2
2
 
3
3
  import sympy
4
4
 
5
5
  from classiq.interface.debug_info.debug_info import FunctionDebugInfo
6
6
  from classiq.interface.exceptions import ClassiqExpansionError, ClassiqValueError
7
+ from classiq.interface.generator.expressions.evaluated_expression import (
8
+ EvaluatedExpression,
9
+ )
7
10
  from classiq.interface.generator.expressions.expression import Expression
11
+ from classiq.interface.generator.expressions.expression_types import ExpressionValue
12
+ from classiq.interface.generator.functions.classical_type import Bool, Integer, Real
13
+ from classiq.interface.helpers.text_utils import s
8
14
  from classiq.interface.model.allocate import Allocate
9
15
  from classiq.interface.model.handle_binding import NestedHandleBinding
10
16
  from classiq.interface.model.quantum_type import (
@@ -12,6 +18,8 @@ from classiq.interface.model.quantum_type import (
12
18
  QuantumNumeric,
13
19
  )
14
20
 
21
+ from classiq.evaluators.qmod_annotated_expression import QmodAnnotatedExpression
22
+ from classiq.evaluators.qmod_node_evaluators.utils import get_sympy_type
15
23
  from classiq.evaluators.qmod_type_inference.quantum_type_inference import (
16
24
  inject_quantum_type_attributes_inplace,
17
25
  )
@@ -22,6 +30,13 @@ if TYPE_CHECKING:
22
30
  from classiq.model_expansions.interpreters.base_interpreter import BaseInterpreter
23
31
 
24
32
 
33
+ def _assign_attr(
34
+ op_update_dict: dict[str, Expression], attr: str, size_value: ExpressionValue
35
+ ) -> None:
36
+ op_update_dict[attr] = Expression(expr=str(size_value))
37
+ op_update_dict[attr]._evaluated_expr = EvaluatedExpression(value=size_value)
38
+
39
+
25
40
  class AllocateEmitter(Emitter[Allocate]):
26
41
  def __init__(
27
42
  self, interpreter: "BaseInterpreter", allow_symbolic_attrs: bool = False
@@ -86,7 +101,7 @@ class AllocateEmitter(Emitter[Allocate]):
86
101
  raise ClassiqValueError(
87
102
  f"Could not infer the size of variable {str(target.handle)!r}"
88
103
  )
89
- op_update_dict["size"] = Expression(expr=expr)
104
+ op_update_dict["size"] = self._evaluate_expression(Expression(expr=expr))
90
105
 
91
106
  def _handle_with_size(
92
107
  self,
@@ -95,10 +110,10 @@ class AllocateEmitter(Emitter[Allocate]):
95
110
  op_update_dict: dict[str, Expression],
96
111
  ) -> None:
97
112
  size_value = self._interpret_size(size, str(target.handle))
98
- op_update_dict["size"] = Expression(expr=str(size_value))
113
+ _assign_attr(op_update_dict, "size", size_value)
99
114
 
100
115
  if not isinstance(
101
- size_value, sympy.Basic
116
+ size_value, QmodAnnotatedExpression
102
117
  ) and not inject_quantum_type_attributes_inplace(
103
118
  QuantumBitvector(length=op_update_dict["size"]), target.quantum_type
104
119
  ):
@@ -122,16 +137,19 @@ class AllocateEmitter(Emitter[Allocate]):
122
137
  )
123
138
 
124
139
  size_value = self._interpret_size(size, var_name)
125
- op_update_dict["size"] = Expression(expr=str(size_value))
140
+ _assign_attr(op_update_dict, "size", size_value)
126
141
  is_signed_value = self._interpret_is_signed(is_signed)
127
- op_update_dict["is_signed"] = Expression(expr=str(is_signed_value))
142
+ _assign_attr(op_update_dict, "is_signed", is_signed_value)
128
143
  fraction_digits_value = self._interpret_fraction_digits(fraction_digits)
129
- op_update_dict["fraction_digits"] = Expression(expr=str(fraction_digits_value))
144
+ _assign_attr(op_update_dict, "fraction_digits", fraction_digits_value)
145
+ self._validate_numeric_atrributes(
146
+ var_name, size_value, is_signed_value, fraction_digits_value
147
+ )
130
148
 
131
149
  if not (
132
- isinstance(size_value, sympy.Basic)
133
- or isinstance(is_signed_value, sympy.Basic)
134
- or isinstance(fraction_digits_value, sympy.Basic)
150
+ isinstance(size_value, QmodAnnotatedExpression)
151
+ or isinstance(is_signed_value, QmodAnnotatedExpression)
152
+ or isinstance(fraction_digits_value, QmodAnnotatedExpression)
135
153
  ) and not inject_quantum_type_attributes_inplace(
136
154
  QuantumNumeric(
137
155
  size=op_update_dict["size"],
@@ -145,24 +163,73 @@ class AllocateEmitter(Emitter[Allocate]):
145
163
  f"{var_name!r} of type {target.quantum_type.qmod_type_name}"
146
164
  )
147
165
 
166
+ def _validate_numeric_atrributes(
167
+ self,
168
+ var_name: str,
169
+ size_value: Any,
170
+ is_signed_value: Any,
171
+ fraction_digits_value: Any,
172
+ ) -> None:
173
+ if (
174
+ isinstance(size_value, int)
175
+ and isinstance(is_signed_value, bool)
176
+ and isinstance(fraction_digits_value, int)
177
+ ):
178
+ if size_value < 0:
179
+ raise ClassiqValueError(
180
+ f"Cannot allocate {size_value} qubit{s(size_value)} for variable "
181
+ f"{var_name!r}"
182
+ )
183
+ if fraction_digits_value < 0:
184
+ raise ClassiqValueError(
185
+ f"Variable {var_name!r} cannot have a negative number of fraction "
186
+ f"digits (got {fraction_digits_value})"
187
+ )
188
+ if size_value < fraction_digits_value:
189
+ raise ClassiqValueError(
190
+ f"Cannot allocate {size_value} qubit{s(size_value)} for variable "
191
+ f"{var_name!r} with {fraction_digits_value} fraction digits"
192
+ )
193
+
148
194
  def _interpret_size(
149
195
  self, size: Expression, var_name: str
150
- ) -> Union[int, float, sympy.Basic]:
196
+ ) -> Union[int, float, sympy.Basic, QmodAnnotatedExpression]:
151
197
  size_value = self._interpreter.evaluate(size).value
152
- if not self._allow_symbolic_attrs and not isinstance(size_value, (int, float)):
153
- if size.expr == f"{var_name}.size":
154
- raise ClassiqValueError(
155
- f"Could not infer the size of variable {var_name!r}"
156
- )
198
+ if not (
199
+ (
200
+ isinstance(size_value, QmodAnnotatedExpression)
201
+ and isinstance(size_value.get_type(size_value.root), (Integer, Real))
202
+ )
203
+ or isinstance(size_value, (int, float))
204
+ or (
205
+ isinstance(size_value, sympy.Basic)
206
+ and isinstance(get_sympy_type(size_value), (Integer, Real))
207
+ )
208
+ ):
157
209
  raise ClassiqValueError(
158
210
  f"The number of allocated qubits must be an integer. Got "
159
211
  f"{str(size_value)!r}"
160
212
  )
213
+ if (
214
+ isinstance(size_value, QmodAnnotatedExpression)
215
+ and not self._allow_symbolic_attrs
216
+ ):
217
+ raise ClassiqValueError(
218
+ f"Could not infer the size of variable {var_name!r}"
219
+ )
161
220
  return size_value
162
221
 
163
- def _interpret_is_signed(self, is_signed: Expression) -> Union[bool, sympy.Basic]:
222
+ def _interpret_is_signed(
223
+ self, is_signed: Expression
224
+ ) -> Union[bool, sympy.Basic, QmodAnnotatedExpression]:
164
225
  is_signed_value = self._interpreter.evaluate(is_signed).value
165
- if not self._allow_symbolic_attrs and not isinstance(is_signed_value, bool):
226
+ if not self._allow_symbolic_attrs and not (
227
+ isinstance(is_signed_value, bool)
228
+ or (
229
+ isinstance(is_signed_value, sympy.Basic)
230
+ and isinstance(get_sympy_type(is_signed_value), Bool)
231
+ )
232
+ ):
166
233
  raise ClassiqValueError(
167
234
  f"The sign of a variable must be boolean. Got "
168
235
  f"{str(is_signed_value)!r}"
@@ -171,10 +238,14 @@ class AllocateEmitter(Emitter[Allocate]):
171
238
 
172
239
  def _interpret_fraction_digits(
173
240
  self, fraction_digits: Expression
174
- ) -> Union[int, float, sympy.Basic]:
241
+ ) -> Union[int, float, sympy.Expr, QmodAnnotatedExpression]:
175
242
  fraction_digits_value = self._interpreter.evaluate(fraction_digits).value
176
- if not self._allow_symbolic_attrs and not isinstance(
177
- fraction_digits_value, (int, float)
243
+ if not self._allow_symbolic_attrs and not (
244
+ isinstance(fraction_digits_value, (int, float))
245
+ or (
246
+ isinstance(fraction_digits_value, sympy.Expr)
247
+ and fraction_digits_value.is_integer
248
+ )
178
249
  ):
179
250
  raise ClassiqValueError(
180
251
  f"The fraction digits of a variable must be an integer. Got "
@@ -1,7 +1,6 @@
1
1
  from typing import TYPE_CHECKING, Optional
2
2
 
3
3
  from classiq.interface.exceptions import ClassiqExpansionError
4
- from classiq.interface.generator.arith.arithmetic import compute_arithmetic_result_type
5
4
  from classiq.interface.generator.expressions.expression import Expression
6
5
  from classiq.interface.model.allocate import Allocate
7
6
  from classiq.interface.model.bind_operation import BindOperation
@@ -20,23 +19,31 @@ from classiq.interface.model.quantum_expressions.quantum_expression import (
20
19
  QuantumAssignmentOperation,
21
20
  )
22
21
  from classiq.interface.model.quantum_function_call import QuantumFunctionCall
23
- from classiq.interface.model.quantum_type import QuantumBitvector, QuantumNumeric
22
+ from classiq.interface.model.quantum_type import (
23
+ QuantumBitvector,
24
+ QuantumNumeric,
25
+ QuantumScalar,
26
+ )
24
27
  from classiq.interface.model.statement_block import StatementBlock
25
28
  from classiq.interface.model.variable_declaration_statement import (
26
29
  VariableDeclarationStatement,
27
30
  )
28
31
  from classiq.interface.model.within_apply_operation import WithinApply
29
32
 
33
+ from classiq.evaluators.qmod_annotated_expression import QmodAnnotatedExpression
30
34
  from classiq.evaluators.qmod_type_inference.quantum_type_inference import (
31
35
  inject_quantum_type_attributes_inplace,
32
36
  )
37
+ from classiq.model_expansions.arithmetic import NumericAttributes
38
+ from classiq.model_expansions.arithmetic_compute_result_attrs import (
39
+ compute_result_attrs_assign,
40
+ )
33
41
  from classiq.model_expansions.quantum_operations.arithmetic.explicit_boolean_expressions import (
34
42
  convert_assignment_bool_expression,
35
43
  validate_assignment_bool_expression,
36
44
  )
37
45
  from classiq.model_expansions.quantum_operations.emitter import Emitter
38
46
  from classiq.model_expansions.scope import ClassicalSymbol
39
- from classiq.model_expansions.transformers.ast_renamer import rename_variables
40
47
  from classiq.qmod.builtins.functions.standard_gates import CX
41
48
 
42
49
  if TYPE_CHECKING:
@@ -100,33 +107,27 @@ class AssignmentResultProcessor(Emitter[QuantumAssignmentOperation]):
100
107
 
101
108
  def _infer_expression_type(
102
109
  self, op: ArithmeticOperation
103
- ) -> Optional[QuantumNumeric]:
110
+ ) -> Optional[QuantumScalar]:
104
111
  expr = self._evaluate_expression(op.expression)
105
- if len(self._get_classical_vars_in_expression(expr)):
106
- return None
107
-
108
- symbols = self._get_symbols_in_expression(expr)
109
- if any(not symbol.quantum_type.is_instantiated for symbol in symbols):
112
+ expr_val = expr.value.value
113
+ if isinstance(expr_val, QmodAnnotatedExpression):
114
+ root_type = expr_val.get_type(expr_val.root)
115
+ if isinstance(root_type, QuantumScalar) and root_type.is_instantiated:
116
+ root_type = compute_result_attrs_assign(
117
+ NumericAttributes.from_type_or_constant(root_type),
118
+ self._machine_precision,
119
+ ).to_quantum_numeric()
120
+ return root_type
110
121
  return None
111
-
112
- expr_str = rename_variables(
113
- expr.expr,
114
- {str(symbol.handle): symbol.handle.identifier for symbol in symbols}
115
- | {symbol.handle.qmod_expr: symbol.handle.identifier for symbol in symbols},
116
- )
117
- for symbol in symbols:
118
- expr_str = expr_str.replace(
119
- symbol.handle.qmod_expr, symbol.handle.identifier
120
- )
121
- return compute_arithmetic_result_type(
122
- expr_str,
123
- {symbol.handle.identifier: symbol.quantum_type for symbol in symbols},
124
- self._machine_precision,
125
- )
122
+ if isinstance(expr_val, (int, float)):
123
+ return NumericAttributes.from_constant(
124
+ expr_val, self._machine_precision
125
+ ).to_quantum_numeric()
126
+ return None
126
127
 
127
128
  @staticmethod
128
129
  def _same_numeric_attributes(
129
- result_type: QuantumNumeric, expression_type: QuantumNumeric
130
+ result_type: QuantumNumeric, expression_type: QuantumScalar
130
131
  ) -> bool:
131
132
  return (
132
133
  result_type.size_in_bits == expression_type.size_in_bits
@@ -137,7 +138,7 @@ class AssignmentResultProcessor(Emitter[QuantumAssignmentOperation]):
137
138
 
138
139
  @classmethod
139
140
  def _validate_declared_attributes(
140
- cls, result_type: QuantumNumeric, expression_type: QuantumNumeric, var: str
141
+ cls, result_type: QuantumNumeric, expression_type: QuantumScalar, var: str
141
142
  ) -> None:
142
143
  result_size, result_sign, result_fractions = (
143
144
  result_type.size_in_bits,
@@ -183,7 +184,7 @@ class AssignmentResultProcessor(Emitter[QuantumAssignmentOperation]):
183
184
  self,
184
185
  op: ArithmeticOperation,
185
186
  result_type: QuantumNumeric,
186
- expression_type: QuantumNumeric,
187
+ expression_type: QuantumScalar,
187
188
  ) -> None:
188
189
  stmts: StatementBlock = []
189
190
  handles: list[HandleBinding] = []
@@ -1,29 +1,18 @@
1
1
  from collections.abc import Sequence
2
2
  from itertools import chain, combinations
3
3
  from typing import (
4
- Any,
5
4
  Generic,
6
5
  Optional,
7
6
  cast,
8
7
  )
9
8
  from uuid import UUID
10
9
 
11
- import sympy
12
-
13
10
  from classiq.interface.debug_info.debug_info import (
14
11
  FunctionDebugInfo,
15
12
  calculate_port_to_passed_variable_mapping,
13
+ new_function_debug_info_by_node,
16
14
  )
17
15
  from classiq.interface.exceptions import ClassiqExpansionError
18
- from classiq.interface.generator.expressions.proxies.classical.any_classical_value import (
19
- AnyClassicalValue,
20
- )
21
- from classiq.interface.generator.expressions.proxies.classical.classical_proxy import (
22
- ClassicalProxy,
23
- )
24
- from classiq.interface.generator.expressions.proxies.classical.qmod_struct_instance import (
25
- QmodStructInstance,
26
- )
27
16
  from classiq.interface.generator.functions.port_declaration import (
28
17
  PortDeclarationDirection,
29
18
  )
@@ -31,6 +20,7 @@ from classiq.interface.generator.functions.type_modifier import TypeModifier
31
20
  from classiq.interface.generator.types.compilation_metadata import CompilationMetadata
32
21
  from classiq.interface.helpers.backward_compatibility import zip_strict
33
22
  from classiq.interface.helpers.text_utils import are, readable_list, s
23
+ from classiq.interface.model.block import Block
34
24
  from classiq.interface.model.classical_parameter_declaration import (
35
25
  ClassicalParameterDeclaration,
36
26
  )
@@ -56,6 +46,7 @@ from classiq.evaluators.argument_types import (
56
46
  from classiq.evaluators.parameter_types import (
57
47
  evaluate_parameter_types_from_args,
58
48
  )
49
+ from classiq.evaluators.qmod_annotated_expression import QmodAnnotatedExpression
59
50
  from classiq.model_expansions.capturing.captured_vars import (
60
51
  INITIALIZED_VAR_MESSAGE,
61
52
  UNINITIALIZED_VAR_MESSAGE,
@@ -73,6 +64,7 @@ from classiq.model_expansions.quantum_operations.function_calls_cache import (
73
64
  get_func_call_cache_key,
74
65
  )
75
66
  from classiq.model_expansions.scope import (
67
+ ClassicalSymbol,
76
68
  Evaluated,
77
69
  QuantumSymbol,
78
70
  QuantumSymbolList,
@@ -111,18 +103,6 @@ def _validate_cloning(evaluated_args: list[Evaluated]) -> None:
111
103
  )
112
104
 
113
105
 
114
- def _is_symbolic(arg: Any) -> bool:
115
- if isinstance(arg, (ClassicalProxy, AnyClassicalValue)):
116
- return True
117
- if isinstance(arg, list):
118
- return any(_is_symbolic(item) for item in arg)
119
- if isinstance(arg, QmodStructInstance):
120
- return any(_is_symbolic(item) for item in arg.fields.values())
121
- if isinstance(arg, sympy.Basic):
122
- return len(arg.free_symbols) > 0
123
- return False
124
-
125
-
126
106
  def _validate_gen_args(
127
107
  function: FunctionClosure, evaluated_args: list[Evaluated]
128
108
  ) -> None:
@@ -132,7 +112,7 @@ def _validate_gen_args(
132
112
  if (
133
113
  isinstance(param, ClassicalParameterDeclaration)
134
114
  and not param.classical_type.is_purely_declarative
135
- and _is_symbolic(arg.value)
115
+ and isinstance(arg.value, QmodAnnotatedExpression)
136
116
  ):
137
117
  readable_expr = transform_expression(str(arg.value), {}, {}, one_line=True)
138
118
  raise ClassiqExpansionError(
@@ -141,6 +121,27 @@ def _validate_gen_args(
141
121
  )
142
122
 
143
123
 
124
+ def _validate_runtime_args(args: list[Evaluated], scope: Scope) -> None:
125
+ for arg in args:
126
+ arg_val = arg.value
127
+ if not isinstance(arg_val, QmodAnnotatedExpression):
128
+ continue
129
+ classical_vars = dict.fromkeys(
130
+ var.name for var in arg_val.get_classical_vars().values()
131
+ )
132
+ runtime_classical_vars = [
133
+ var
134
+ for var in classical_vars
135
+ if isinstance(scope[var].value, ClassicalSymbol)
136
+ ]
137
+ if len(runtime_classical_vars) > 0:
138
+ raise ClassiqExpansionError(
139
+ f"Passing runtime variable{s(runtime_classical_vars)} "
140
+ f"{readable_list(runtime_classical_vars, quote=True)} as function call "
141
+ f"arguments is not supported"
142
+ )
143
+
144
+
144
145
  class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], ModelRenamer):
145
146
  @staticmethod
146
147
  def _should_wrap(body: Sequence[QuantumStatement]) -> bool:
@@ -148,6 +149,11 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], ModelR
148
149
  # I'm sure there are better ways to handle it, but this is the simplest way to do it for now
149
150
  return any(isinstance(stmt, VariableDeclarationStatement) for stmt in body)
150
151
 
152
+ def _create_block_labeled_ref(self, label: str) -> FunctionDebugInfo:
153
+ bake_ref_node = Block(statements=[], label=label)
154
+ self._interpreter.add_to_debug_info(statement=bake_ref_node)
155
+ return new_function_debug_info_by_node(bake_ref_node)
156
+
151
157
  def _create_expanded_wrapping_function(
152
158
  self,
153
159
  name: str,
@@ -195,6 +201,7 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], ModelR
195
201
  self._validate_call_args(function.positional_arg_declarations, args)
196
202
  evaluated_args = [self._interpreter.evaluate(arg) for arg in args]
197
203
  _validate_cloning(evaluated_args)
204
+ _validate_runtime_args(evaluated_args, self._current_scope)
198
205
  new_declaration = self._prepare_fully_typed_declaration(
199
206
  function, evaluated_args
200
207
  )
@@ -385,7 +392,6 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], ModelR
385
392
  name=function.name,
386
393
  positional_arg_declarations=evaluate_parameter_types_from_args(
387
394
  function,
388
- function.signature_scope,
389
395
  evaluated_args,
390
396
  ),
391
397
  )