classiq 0.89.0__py3-none-any.whl → 0.91.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 (108) hide show
  1. classiq/__init__.py +1 -0
  2. classiq/_internals/api_wrapper.py +16 -32
  3. classiq/analyzer/show_interactive_hack.py +26 -1
  4. classiq/applications/chemistry/chemistry_model_constructor.py +14 -2
  5. classiq/applications/combinatorial_helpers/pyomo_utils.py +9 -6
  6. classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +2 -2
  7. classiq/applications/combinatorial_optimization/combinatorial_problem.py +16 -8
  8. classiq/evaluators/classical_expression.py +63 -41
  9. classiq/evaluators/control.py +31 -52
  10. classiq/evaluators/expression_evaluator.py +31 -127
  11. classiq/evaluators/parameter_types.py +200 -104
  12. classiq/evaluators/qmod_annotated_expression.py +3 -1
  13. classiq/evaluators/qmod_expression_visitors/qmod_expression_simplifier.py +10 -3
  14. classiq/evaluators/qmod_node_evaluators/classical_function_evaluation.py +12 -37
  15. classiq/evaluators/qmod_node_evaluators/constant_evaluation.py +8 -17
  16. classiq/evaluators/qmod_node_evaluators/measurement_evaluation.py +1 -1
  17. classiq/evaluators/qmod_node_evaluators/min_max_evaluation.py +7 -1
  18. classiq/evaluators/qmod_node_evaluators/name_evaluation.py +0 -1
  19. classiq/evaluators/qmod_node_evaluators/numeric_attrs_utils.py +9 -1
  20. classiq/evaluators/qmod_node_evaluators/utils.py +33 -8
  21. classiq/evaluators/qmod_type_inference/classical_type_inference.py +4 -7
  22. classiq/interface/_version.py +1 -1
  23. classiq/interface/analyzer/analysis_params.py +1 -25
  24. classiq/interface/analyzer/result.py +4 -0
  25. classiq/interface/chemistry/ground_state_problem.py +16 -2
  26. classiq/interface/executor/optimizer_preferences.py +0 -112
  27. classiq/interface/executor/result.py +22 -3
  28. classiq/interface/generator/application_apis/chemistry_declarations.py +3 -1
  29. classiq/interface/generator/arith/arithmetic_expression_validator.py +2 -7
  30. classiq/interface/generator/expressions/evaluated_expression.py +3 -13
  31. classiq/interface/generator/expressions/expression_types.py +10 -22
  32. classiq/interface/generator/expressions/proxies/classical/classical_array_proxy.py +3 -8
  33. classiq/interface/generator/expressions/proxies/classical/classical_proxy.py +2 -2
  34. classiq/interface/generator/expressions/proxies/classical/classical_struct_proxy.py +1 -2
  35. classiq/interface/generator/functions/classical_type.py +2 -5
  36. classiq/interface/generator/functions/concrete_types.py +1 -1
  37. classiq/interface/generator/functions/type_name.py +0 -12
  38. classiq/interface/generator/generated_circuit_data.py +4 -0
  39. classiq/interface/generator/preferences/qasm_to_qmod_params.py +14 -0
  40. classiq/interface/helpers/model_normalizer.py +0 -6
  41. classiq/interface/ide/visual_model.py +1 -0
  42. classiq/interface/model/handle_binding.py +1 -1
  43. classiq/interface/model/port_declaration.py +2 -1
  44. classiq/interface/model/quantum_expressions/arithmetic_operation.py +16 -12
  45. classiq/interface/model/quantum_type.py +1 -40
  46. classiq/interface/server/routes.py +2 -3
  47. classiq/model_expansions/capturing/captured_vars.py +10 -3
  48. classiq/model_expansions/closure.py +8 -0
  49. classiq/model_expansions/function_builder.py +18 -2
  50. classiq/model_expansions/interpreters/base_interpreter.py +84 -22
  51. classiq/model_expansions/interpreters/frontend_generative_interpreter.py +1 -11
  52. classiq/model_expansions/interpreters/generative_interpreter.py +67 -20
  53. classiq/model_expansions/quantum_operations/allocate.py +92 -21
  54. classiq/model_expansions/quantum_operations/assignment_result_processor.py +28 -27
  55. classiq/model_expansions/quantum_operations/call_emitter.py +32 -26
  56. classiq/model_expansions/quantum_operations/classical_var_emitter.py +6 -2
  57. classiq/model_expansions/quantum_operations/emitter.py +50 -70
  58. classiq/model_expansions/quantum_operations/expression_evaluator.py +62 -7
  59. classiq/model_expansions/quantum_operations/quantum_function_call.py +4 -5
  60. classiq/model_expansions/quantum_operations/variable_decleration.py +16 -11
  61. classiq/model_expansions/scope.py +39 -41
  62. classiq/model_expansions/scope_initialization.py +3 -6
  63. classiq/model_expansions/transformers/model_renamer.py +35 -64
  64. classiq/model_expansions/transformers/type_modifier_inference.py +6 -6
  65. classiq/model_expansions/utils/handles_collector.py +7 -0
  66. classiq/model_expansions/visitors/boolean_expression_transformers.py +7 -31
  67. classiq/model_expansions/visitors/symbolic_param_inference.py +9 -3
  68. classiq/open_library/functions/state_preparation.py +3 -3
  69. classiq/qmod/builtins/functions/allocation.py +8 -8
  70. classiq/qmod/builtins/functions/arithmetic.py +1 -1
  71. classiq/qmod/builtins/functions/chemistry.py +64 -0
  72. classiq/qmod/builtins/functions/exponentiation.py +7 -13
  73. classiq/qmod/builtins/functions/qsvm.py +1 -1
  74. classiq/qmod/builtins/operations.py +42 -10
  75. classiq/qmod/generative.py +2 -4
  76. classiq/qmod/native/pretty_printer.py +1 -1
  77. classiq/qmod/pretty_print/pretty_printer.py +1 -1
  78. classiq/qmod/qmod_constant.py +1 -1
  79. classiq/qmod/qmod_parameter.py +2 -2
  80. classiq/qmod/qmod_variable.py +18 -19
  81. classiq/qmod/quantum_expandable.py +1 -1
  82. classiq/qmod/semantics/error_manager.py +34 -15
  83. classiq/qmod/symbolic.py +15 -4
  84. classiq/qmod/utilities.py +4 -1
  85. classiq/synthesis.py +38 -3
  86. classiq/visualization.py +1 -1
  87. {classiq-0.89.0.dist-info → classiq-0.91.0.dist-info}/METADATA +1 -1
  88. {classiq-0.89.0.dist-info → classiq-0.91.0.dist-info}/RECORD +89 -107
  89. classiq/evaluators/arg_type_match.py +0 -168
  90. classiq/evaluators/classical_type_inference.py +0 -121
  91. classiq/interface/combinatorial_optimization/optimization_problem.py +0 -17
  92. classiq/interface/combinatorial_optimization/result.py +0 -9
  93. classiq/interface/generator/expressions/handle_identifier.py +0 -6
  94. classiq/interface/generator/expressions/proxies/classical/any_classical_value.py +0 -41
  95. classiq/interface/generator/expressions/proxies/quantum/__init__.py +0 -0
  96. classiq/interface/generator/expressions/proxies/quantum/qmod_qarray_proxy.py +0 -80
  97. classiq/interface/generator/expressions/proxies/quantum/qmod_qscalar_proxy.py +0 -77
  98. classiq/interface/generator/expressions/proxies/quantum/qmod_qstruct_proxy.py +0 -38
  99. classiq/interface/generator/expressions/proxies/quantum/qmod_sized_proxy.py +0 -39
  100. classiq/interface/generator/expressions/type_proxy.py +0 -10
  101. classiq/model_expansions/atomic_expression_functions_defs.py +0 -413
  102. classiq/model_expansions/model_tables.py +0 -18
  103. classiq/model_expansions/sympy_conversion/__init__.py +0 -0
  104. classiq/model_expansions/sympy_conversion/expression_to_sympy.py +0 -181
  105. classiq/model_expansions/sympy_conversion/sympy_to_python.py +0 -132
  106. classiq/model_expansions/transformers/ast_renamer.py +0 -26
  107. classiq/model_expansions/utils/sympy_utils.py +0 -24
  108. {classiq-0.89.0.dist-info → classiq-0.91.0.dist-info}/WHEEL +0 -0
@@ -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
@@ -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
 
@@ -45,7 +35,7 @@ class FrontendGenerativeInterpreter(GenerativeInterpreter):
45
35
  return super()._get_main_closure(main_func)
46
36
 
47
37
  def process_exception(self, e: Exception) -> None:
48
- if not isinstance(e, (ClassiqError, ValidationError)):
38
+ if not isinstance(e, (ClassiqError, ValidationError, RecursionError)):
49
39
  frame = inspect.trace()[-1]
50
40
  module = inspect.getmodule(frame[0])
51
41
  if module is None or not module.__name__.startswith("classiq."):
@@ -11,6 +11,7 @@ from classiq.interface.generator.functions.builtins.internal_operators import (
11
11
  CLASSICAL_IF_OPERATOR_NAME,
12
12
  CONTROL_OPERATOR_NAME,
13
13
  INVERT_OPERATOR_NAME,
14
+ POWER_OPERATOR_NAME,
14
15
  REPEAT_OPERATOR_NAME,
15
16
  WITHIN_APPLY_NAME,
16
17
  )
@@ -108,6 +109,7 @@ class GenerativeInterpreter(BaseInterpreter):
108
109
  self.infer_symbolic_parameters(
109
110
  model.functions, [gen_func.func_decl for gen_func in generative_functions]
110
111
  )
112
+ self._symbolic_parameters_switch = self.allow_symbolic_parameters()
111
113
 
112
114
  def infer_symbolic_parameters(
113
115
  self,
@@ -118,6 +120,9 @@ class GenerativeInterpreter(BaseInterpreter):
118
120
  ) -> None:
119
121
  pass
120
122
 
123
+ def allow_symbolic_parameters(self) -> bool:
124
+ return True
125
+
121
126
  def evaluate_lambda(self, function: QuantumLambdaFunction) -> Evaluated:
122
127
  func_decl = NamedParamsQuantumFunctionDeclaration(
123
128
  name=self._counted_name_allocator.allocate(
@@ -164,18 +169,28 @@ class GenerativeInterpreter(BaseInterpreter):
164
169
  QuantumFunctionCallEmitter(self).emit(call)
165
170
 
166
171
  @emit.register
167
- def _emit_allocate(self, allocate: Allocate) -> None:
168
- return self.emit_allocate(allocate)
169
-
170
172
  def emit_allocate(self, allocate: Allocate) -> None:
171
- AllocateEmitter(self).emit(allocate)
173
+ CompositeEmitter[Allocate](
174
+ self,
175
+ [
176
+ ExpressionEvaluator(
177
+ self,
178
+ "size",
179
+ readable_expression_name="allocation size",
180
+ allow_link_time_vars=self._symbolic_parameters_switch,
181
+ allow_runtime_vars=self._symbolic_parameters_switch,
182
+ ),
183
+ AllocateEmitter(
184
+ self, allow_symbolic_attrs=self._symbolic_parameters_switch
185
+ ),
186
+ ],
187
+ ).emit(allocate)
172
188
 
173
189
  @emit.register
174
- def _emit_bind(self, bind: BindOperation) -> None:
175
- self.emit_bind(bind)
176
-
177
190
  def emit_bind(self, bind: BindOperation) -> None:
178
- BindEmitter(self).emit(bind)
191
+ BindEmitter(self, allow_symbolic_size=self._symbolic_parameters_switch).emit(
192
+ bind
193
+ )
179
194
 
180
195
  @emit.register
181
196
  def emit_amplitude_loading_operation(self, op: AmplitudeLoadingOperation) -> None:
@@ -183,7 +198,14 @@ class GenerativeInterpreter(BaseInterpreter):
183
198
  self,
184
199
  [
185
200
  HandleEvaluator(self, "result_var"),
186
- ExpressionEvaluator(self, "expression"),
201
+ ExpressionEvaluator(
202
+ self,
203
+ "expression",
204
+ readable_expression_name="amplitude-encoding expression",
205
+ simplify=True,
206
+ treat_qnum_as_float=True,
207
+ allow_runtime_vars=self._symbolic_parameters_switch,
208
+ ),
187
209
  AssignmentResultProcessor(self),
188
210
  ],
189
211
  ).emit(op)
@@ -197,7 +219,7 @@ class GenerativeInterpreter(BaseInterpreter):
197
219
  self,
198
220
  [
199
221
  HandleEvaluator(self, "result_var"),
200
- ExpressionEvaluator(self, "expression"),
222
+ ExpressionEvaluator(self, "expression", simplify=True),
201
223
  ClassicalVarEmitter(self),
202
224
  AssignmentResultProcessor(self),
203
225
  ],
@@ -210,7 +232,7 @@ class GenerativeInterpreter(BaseInterpreter):
210
232
  [
211
233
  HandleEvaluator(self, "target"),
212
234
  HandleEvaluator(self, "value"),
213
- ExpressionEvaluator(self, "value"),
235
+ ExpressionEvaluator(self, "value", simplify=True),
214
236
  ],
215
237
  ).emit(op)
216
238
 
@@ -221,14 +243,16 @@ class GenerativeInterpreter(BaseInterpreter):
221
243
  VariableDeclarationStatementEmitter(self).emit(variable_declaration)
222
244
 
223
245
  @emit.register
224
- def _emit_classical_if(self, classical_if: ClassicalIf) -> None:
225
- self.emit_classical_if(classical_if)
226
-
227
246
  def emit_classical_if(self, classical_if: ClassicalIf) -> None:
228
247
  CompositeEmitter[ClassicalIf](
229
248
  self,
230
249
  [
231
- ExpressionEvaluator(self, "condition"),
250
+ ExpressionEvaluator(
251
+ self,
252
+ "condition",
253
+ readable_expression_name="classical-if condition",
254
+ allow_link_time_vars=self._symbolic_parameters_switch,
255
+ ),
232
256
  IfElimination(self),
233
257
  BlockEvaluator(
234
258
  self,
@@ -276,7 +300,14 @@ class GenerativeInterpreter(BaseInterpreter):
276
300
  CompositeEmitter[Control](
277
301
  self,
278
302
  [
279
- ExpressionEvaluator(self, "expression"),
303
+ ExpressionEvaluator(
304
+ self,
305
+ "expression",
306
+ simplify=True,
307
+ readable_expression_name="control expression",
308
+ allow_link_time_vars=self._symbolic_parameters_switch,
309
+ allow_runtime_vars=self._symbolic_parameters_switch,
310
+ ),
280
311
  BlockEvaluator(
281
312
  self,
282
313
  CONTROL_OPERATOR_NAME,
@@ -291,8 +322,13 @@ class GenerativeInterpreter(BaseInterpreter):
291
322
  CompositeEmitter[Power](
292
323
  self,
293
324
  [
294
- ExpressionEvaluator(self, "power"),
295
- BlockEvaluator(self, CONTROL_OPERATOR_NAME, "body"),
325
+ ExpressionEvaluator(
326
+ self,
327
+ "power",
328
+ readable_expression_name="power exponent",
329
+ allow_runtime_vars=self._symbolic_parameters_switch,
330
+ ),
331
+ BlockEvaluator(self, POWER_OPERATOR_NAME, "body"),
296
332
  ],
297
333
  ).emit(power)
298
334
 
@@ -301,8 +337,19 @@ class GenerativeInterpreter(BaseInterpreter):
301
337
  CompositeEmitter[PhaseOperation](
302
338
  self,
303
339
  [
304
- ExpressionEvaluator(self, "expression"),
305
- ExpressionEvaluator(self, "theta"),
340
+ ExpressionEvaluator(
341
+ self,
342
+ "expression",
343
+ readable_expression_name="phase expression",
344
+ simplify=True,
345
+ allow_runtime_vars=self._symbolic_parameters_switch,
346
+ ),
347
+ ExpressionEvaluator(
348
+ self,
349
+ "theta",
350
+ readable_expression_name="phase theta expression",
351
+ allow_runtime_vars=self._symbolic_parameters_switch,
352
+ ),
306
353
  ],
307
354
  ).emit(phase)
308
355
 
@@ -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] = []