classiq 0.89.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 (86) 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 +8 -4
  11. classiq/evaluators/parameter_types.py +200 -104
  12. classiq/evaluators/qmod_annotated_expression.py +3 -1
  13. classiq/evaluators/qmod_node_evaluators/classical_function_evaluation.py +12 -37
  14. classiq/evaluators/qmod_node_evaluators/constant_evaluation.py +8 -17
  15. classiq/evaluators/qmod_node_evaluators/measurement_evaluation.py +1 -1
  16. classiq/evaluators/qmod_node_evaluators/min_max_evaluation.py +7 -1
  17. classiq/evaluators/qmod_node_evaluators/name_evaluation.py +0 -1
  18. classiq/evaluators/qmod_node_evaluators/numeric_attrs_utils.py +9 -1
  19. classiq/evaluators/qmod_node_evaluators/utils.py +33 -0
  20. classiq/evaluators/qmod_type_inference/classical_type_inference.py +4 -7
  21. classiq/interface/_version.py +1 -1
  22. classiq/interface/analyzer/analysis_params.py +1 -25
  23. classiq/interface/analyzer/result.py +4 -0
  24. classiq/interface/chemistry/ground_state_problem.py +16 -2
  25. classiq/interface/executor/optimizer_preferences.py +0 -112
  26. classiq/interface/generator/application_apis/chemistry_declarations.py +3 -1
  27. classiq/interface/generator/arith/arithmetic_expression_validator.py +2 -7
  28. classiq/interface/generator/expressions/evaluated_expression.py +3 -13
  29. classiq/interface/generator/expressions/expression_types.py +8 -22
  30. classiq/interface/generator/expressions/proxies/classical/classical_proxy.py +2 -2
  31. classiq/interface/generator/expressions/proxies/classical/classical_struct_proxy.py +1 -2
  32. classiq/interface/generator/functions/concrete_types.py +1 -1
  33. classiq/interface/generator/generated_circuit_data.py +4 -0
  34. classiq/interface/generator/preferences/qasm_to_qmod_params.py +14 -0
  35. classiq/interface/helpers/model_normalizer.py +0 -6
  36. classiq/interface/ide/visual_model.py +1 -0
  37. classiq/interface/model/handle_binding.py +1 -1
  38. classiq/interface/model/port_declaration.py +2 -1
  39. classiq/interface/model/quantum_expressions/arithmetic_operation.py +16 -12
  40. classiq/interface/model/quantum_type.py +1 -1
  41. classiq/interface/server/routes.py +2 -3
  42. classiq/model_expansions/atomic_expression_functions_defs.py +4 -22
  43. classiq/model_expansions/capturing/captured_vars.py +7 -3
  44. classiq/model_expansions/closure.py +8 -0
  45. classiq/model_expansions/interpreters/base_interpreter.py +84 -22
  46. classiq/model_expansions/interpreters/frontend_generative_interpreter.py +1 -1
  47. classiq/model_expansions/interpreters/generative_interpreter.py +7 -5
  48. classiq/model_expansions/quantum_operations/allocate.py +92 -21
  49. classiq/model_expansions/quantum_operations/assignment_result_processor.py +28 -27
  50. classiq/model_expansions/quantum_operations/call_emitter.py +32 -26
  51. classiq/model_expansions/quantum_operations/classical_var_emitter.py +6 -2
  52. classiq/model_expansions/quantum_operations/emitter.py +39 -69
  53. classiq/model_expansions/quantum_operations/expression_evaluator.py +13 -2
  54. classiq/model_expansions/quantum_operations/quantum_function_call.py +4 -5
  55. classiq/model_expansions/quantum_operations/variable_decleration.py +16 -11
  56. classiq/model_expansions/scope.py +36 -29
  57. classiq/model_expansions/scope_initialization.py +3 -6
  58. classiq/model_expansions/sympy_conversion/sympy_to_python.py +6 -2
  59. classiq/model_expansions/transformers/model_renamer.py +35 -64
  60. classiq/model_expansions/transformers/type_modifier_inference.py +6 -6
  61. classiq/model_expansions/visitors/boolean_expression_transformers.py +7 -31
  62. classiq/model_expansions/visitors/symbolic_param_inference.py +9 -3
  63. classiq/open_library/functions/state_preparation.py +3 -3
  64. classiq/qmod/builtins/functions/allocation.py +8 -8
  65. classiq/qmod/builtins/functions/arithmetic.py +1 -1
  66. classiq/qmod/builtins/functions/chemistry.py +64 -0
  67. classiq/qmod/builtins/functions/exponentiation.py +7 -13
  68. classiq/qmod/builtins/functions/qsvm.py +1 -1
  69. classiq/qmod/builtins/operations.py +38 -10
  70. classiq/qmod/generative.py +2 -4
  71. classiq/qmod/native/pretty_printer.py +1 -1
  72. classiq/qmod/pretty_print/pretty_printer.py +1 -1
  73. classiq/qmod/qmod_constant.py +1 -1
  74. classiq/qmod/qmod_parameter.py +2 -2
  75. classiq/qmod/qmod_variable.py +15 -15
  76. classiq/qmod/quantum_expandable.py +1 -1
  77. classiq/synthesis.py +37 -1
  78. classiq/visualization.py +1 -1
  79. {classiq-0.89.0.dist-info → classiq-0.90.0.dist-info}/METADATA +1 -1
  80. {classiq-0.89.0.dist-info → classiq-0.90.0.dist-info}/RECORD +81 -85
  81. classiq/evaluators/arg_type_match.py +0 -168
  82. classiq/evaluators/classical_type_inference.py +0 -121
  83. classiq/interface/combinatorial_optimization/optimization_problem.py +0 -17
  84. classiq/interface/combinatorial_optimization/result.py +0 -9
  85. classiq/model_expansions/transformers/ast_renamer.py +0 -26
  86. {classiq-0.89.0.dist-info → classiq-0.90.0.dist-info}/WHEEL +0 -0
@@ -15,6 +15,7 @@ from classiq.interface.generator.expressions.atomic_expression_functions import
15
15
  from classiq.interface.generator.expressions.expression_types import (
16
16
  ExpressionValue,
17
17
  QmodStructInstance,
18
+ RuntimeConstant,
18
19
  )
19
20
  from classiq.interface.generator.expressions.proxies.classical.any_classical_value import (
20
21
  AnyClassicalValue,
@@ -44,14 +45,12 @@ from classiq.interface.generator.functions.classical_type import (
44
45
  ClassicalArray,
45
46
  ClassicalTuple,
46
47
  ClassicalType,
47
- OpaqueHandle,
48
- QmodPyObject,
49
48
  Real,
50
- StructMetaType,
51
49
  )
52
50
  from classiq.interface.generator.functions.type_name import TypeName
53
51
  from classiq.interface.helpers.backward_compatibility import zip_strict
54
52
 
53
+ from classiq.evaluators.qmod_annotated_expression import QmodAnnotatedExpression
55
54
  from classiq.evaluators.qmod_expression_visitors.sympy_wrappers import (
56
55
  BitwiseAnd,
57
56
  BitwiseNot,
@@ -61,10 +60,6 @@ from classiq.evaluators.qmod_expression_visitors.sympy_wrappers import (
61
60
  LShift,
62
61
  RShift,
63
62
  )
64
- from classiq.model_expansions.model_tables import (
65
- HandleIdentifier,
66
- HandleTable,
67
- )
68
63
  from classiq.model_expansions.sympy_conversion.expression_to_sympy import (
69
64
  MISSING_SLICE_VALUE_PLACEHOLDER,
70
65
  )
@@ -106,10 +101,6 @@ def qmod_val_to_python(val: ExpressionValue, qmod_type: ClassicalType) -> Any:
106
101
  )
107
102
  ]
108
103
 
109
- elif isinstance(qmod_type, OpaqueHandle):
110
- if isinstance(val, HandleIdentifier):
111
- return HandleTable.get_handle_object(val)
112
-
113
104
  elif isinstance(val, Expr):
114
105
  return sympy_to_python(val)
115
106
 
@@ -121,10 +112,6 @@ def qmod_val_to_python(val: ExpressionValue, qmod_type: ClassicalType) -> Any:
121
112
  if isinstance(val, bool):
122
113
  return val
123
114
 
124
- elif isinstance(qmod_type, StructMetaType):
125
- if isinstance(val, TypeProxy):
126
- return val.struct_declaration
127
-
128
115
  elif isinstance(val, int): # other scalars are represented as int
129
116
  return val
130
117
 
@@ -162,11 +149,6 @@ def python_val_to_qmod(val: Any, qmod_type: ClassicalType) -> ExpressionValue:
162
149
  for elem, elem_type in zip_strict(val, qmod_type.element_types, strict=True)
163
150
  ]
164
151
 
165
- if isinstance(qmod_type, OpaqueHandle):
166
- if not isinstance(val, QmodPyObject):
167
- raise ClassiqInternalExpansionError("Bad value opaque handle")
168
- return HandleTable.set_handle_object(val)
169
-
170
152
  return val
171
153
 
172
154
 
@@ -194,7 +176,7 @@ def get_field(
194
176
  QmodSizedProxy, QmodStructInstance, list, ClassicalProxy, AnyClassicalValue
195
177
  ],
196
178
  field: str,
197
- ) -> ExpressionValue:
179
+ ) -> Any:
198
180
  if isinstance(proxy, AnyClassicalValue) or (
199
181
  isinstance(proxy, Symbol)
200
182
  and not isinstance(proxy, QmodSizedProxy)
@@ -244,7 +226,7 @@ def do_div(lhs: Any, rhs: Any) -> Any:
244
226
  return res
245
227
 
246
228
 
247
- _EXPRESSION_TYPES = get_args(ExpressionValue)
229
+ _EXPRESSION_TYPES = (*get_args(RuntimeConstant), QmodAnnotatedExpression)
248
230
 
249
231
 
250
232
  def _is_qmod_value(val: Any) -> bool:
@@ -1,3 +1,4 @@
1
+ import ast
1
2
  import dataclasses
2
3
  from collections.abc import Sequence
3
4
  from dataclasses import dataclass, field
@@ -41,6 +42,7 @@ from classiq.interface.model.variable_declaration_statement import (
41
42
  VariableDeclarationStatement,
42
43
  )
43
44
 
45
+ from classiq.evaluators.qmod_annotated_expression import QmodAnnotatedExpression
44
46
  from classiq.model_expansions.capturing.mangling_utils import (
45
47
  demangle_handle,
46
48
  mangle_captured_var_name,
@@ -60,9 +62,11 @@ UNINITIALIZED_VAR_MESSAGE = "Variable '{}' should be initialized here"
60
62
 
61
63
  def _get_symbol_expr(symbol: str, classical_type: ClassicalType) -> Expression:
62
64
  expr = Expression(expr=symbol)
63
- expr._evaluated_expr = EvaluatedExpression(
64
- value=classical_type.get_classical_proxy(handle=HandleBinding(name=symbol))
65
- )
65
+ expr_val = QmodAnnotatedExpression(ast.Name(id=symbol))
66
+ expr_val.set_type(expr_val.root, classical_type)
67
+ expr_val.set_var(expr_val.root, HandleBinding(name=symbol))
68
+ expr_val.lock()
69
+ expr._evaluated_expr = EvaluatedExpression(value=expr_val)
66
70
  return expr
67
71
 
68
72
 
@@ -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] = []