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
@@ -1,5 +1,6 @@
1
1
  import re
2
2
 
3
+ from classiq.interface.exceptions import ClassiqExpansionError
3
4
  from classiq.interface.model.handle_binding import HandleBinding
4
5
  from classiq.interface.model.quantum_expressions.arithmetic_operation import (
5
6
  ArithmeticOperation,
@@ -14,9 +15,12 @@ class ClassicalVarEmitter(Emitter[ArithmeticOperation]):
14
15
  result_symbol = self._interpreter.evaluate(op.result_var).value
15
16
  if not isinstance(result_symbol, ClassicalSymbol):
16
17
  return False
17
- op._classical_assignment = True
18
+ op.classical_assignment = True
18
19
  match = re.search(r"measure\((.*?)\)", op.expression.expr)
19
20
  if match is not None:
20
- op.set_var_handles([HandleBinding(name=match.group(1))])
21
+ var = match.group(1)
22
+ if "[" in var or "." in var:
23
+ raise ClassiqExpansionError("'measure' must receive a whole variable")
24
+ op.set_var_handles([HandleBinding(name=var)])
21
25
  self.emit_statement(op)
22
26
  return True
@@ -1,4 +1,3 @@
1
- import ast
2
1
  from abc import ABC, abstractmethod
3
2
  from collections.abc import Sequence
4
3
  from typing import (
@@ -9,17 +8,10 @@ from typing import (
9
8
  Union,
10
9
  )
11
10
 
12
- import sympy
13
-
14
11
  from classiq.interface.debug_info.debug_info import (
15
12
  DebugInfoCollection,
16
13
  )
17
14
  from classiq.interface.exceptions import ClassiqInternalExpansionError
18
- from classiq.interface.generator.expressions.atomic_expression_functions import (
19
- CLASSICAL_ATTRIBUTES,
20
- SUPPORTED_CLASSIQ_BUILTIN_FUNCTIONS,
21
- SUPPORTED_PYTHON_BUILTIN_FUNCTIONS,
22
- )
23
15
  from classiq.interface.generator.expressions.evaluated_expression import (
24
16
  EvaluatedExpression,
25
17
  )
@@ -47,17 +39,14 @@ from classiq.interface.model.quantum_function_declaration import (
47
39
  )
48
40
  from classiq.interface.model.quantum_statement import QuantumOperation, QuantumStatement
49
41
 
42
+ from classiq.evaluators.qmod_annotated_expression import QmodAnnotatedExpression
50
43
  from classiq.model_expansions.closure import Closure, FunctionClosure, GenerativeClosure
51
44
  from classiq.model_expansions.function_builder import (
52
45
  OperationBuilder,
53
46
  OperationContext,
54
47
  )
55
48
  from classiq.model_expansions.scope import QuantumSymbol, Scope
56
- from classiq.model_expansions.sympy_conversion.sympy_to_python import (
57
- translate_sympy_quantum_expression,
58
- )
59
49
  from classiq.model_expansions.utils.counted_name_allocator import CountedNameAllocator
60
- from classiq.model_expansions.visitors.variable_references import VarRefCollector
61
50
  from classiq.qmod.quantum_function import GenerativeQFunc
62
51
 
63
52
  if TYPE_CHECKING:
@@ -153,22 +142,18 @@ class Emitter(Generic[QuantumStatementT], ABC):
153
142
  return context
154
143
 
155
144
  def _evaluate_expression(
156
- self, expression: Expression, preserve_bool_ops: bool = False
145
+ self,
146
+ expression: Expression,
147
+ *,
148
+ simplify: bool = False,
149
+ treat_qnum_as_float: bool = False,
157
150
  ) -> Expression:
158
- evaluated_expression = self._interpreter.evaluate(expression)
159
- if isinstance(evaluated_expression.value, sympy.Basic):
160
- new_expression = Expression(
161
- expr=translate_sympy_quantum_expression(
162
- evaluated_expression.value,
163
- preserve_bool_ops=preserve_bool_ops,
164
- )
165
- )
166
- else:
167
- new_expression = Expression(expr=str(evaluated_expression.value))
168
- new_expression._evaluated_expr = EvaluatedExpression(
169
- value=evaluated_expression.value
170
- )
171
- return new_expression
151
+ expr_val = self._interpreter.evaluate(
152
+ expression, simplify=simplify, treat_qnum_as_float=treat_qnum_as_float
153
+ ).value
154
+ new_expr = Expression(expr=str(expr_val))
155
+ new_expr._evaluated_expr = EvaluatedExpression(value=expr_val)
156
+ return new_expr
172
157
 
173
158
  def emit_statement(self, statement: QuantumStatement) -> None:
174
159
  self._update_captured_classical_vars(statement)
@@ -196,10 +181,8 @@ class Emitter(Generic[QuantumStatementT], ABC):
196
181
  ) -> None:
197
182
  if handle.name not in self._current_scope:
198
183
  return
199
- if not handle.is_constant():
200
- self._update_captured_classical_vars_in_expression(
201
- Expression(expr=str(handle))
202
- )
184
+ for expr in handle.expressions():
185
+ self._update_captured_classical_vars_in_expression(expr)
203
186
  while isinstance(handle, NestedHandleBinding) and not handle.is_constant():
204
187
  handle = handle.base_handle
205
188
  defining_function = self._current_scope[handle.name].defining_function
@@ -237,52 +220,39 @@ class Emitter(Generic[QuantumStatementT], ABC):
237
220
  )
238
221
 
239
222
  def _get_symbols_in_expression(self, expr: Expression) -> list[QuantumSymbol]:
240
- vrc = VarRefCollector(ignore_duplicated_handles=True, unevaluated=True)
241
- vrc.visit(ast.parse(expr.expr))
242
- handles = dict.fromkeys(
243
- handle
244
- for handle in vrc.var_handles
245
- if handle.name
246
- not in SUPPORTED_PYTHON_BUILTIN_FUNCTIONS
247
- | SUPPORTED_CLASSIQ_BUILTIN_FUNCTIONS
248
- and isinstance(self._current_scope[handle.name].value, QuantumSymbol)
249
- )
223
+ expr_val = expr.value.value
224
+ if not isinstance(expr_val, QmodAnnotatedExpression):
225
+ return []
250
226
  return [
251
- self._interpreter.evaluate(handle).value
252
- for handle in handles
253
- if not isinstance(handle, FieldHandleBinding)
254
- or handle.field not in CLASSICAL_ATTRIBUTES
227
+ QuantumSymbol(handle=var, quantum_type=expr_val.get_quantum_type(node_id))
228
+ for node_id, var in expr_val.get_quantum_vars().items()
255
229
  ]
256
230
 
257
231
  def _get_classical_vars_in_expression(
258
232
  self, expr: Expression
259
233
  ) -> list[tuple[str, ClassicalType]]:
260
- vrc = VarRefCollector(
261
- ignore_duplicated_handles=True, ignore_sympy_symbols=True, unevaluated=True
262
- )
263
- vrc.visit(ast.parse(expr.expr))
264
- return list(
265
- {
266
- handle.name: get_proxy_type(proxy)
267
- for handle in vrc.var_handles
268
- if handle.name in self._current_scope
269
- and isinstance(
270
- proxy := self._current_scope[handle.name].value, ClassicalProxy
271
- )
272
- }.items()
234
+ if not expr.is_evaluated():
235
+ raise ClassiqInternalExpansionError
236
+ expr_val = expr.value.value
237
+ if not isinstance(expr_val, QmodAnnotatedExpression):
238
+ return []
239
+ classical_vars = list(
240
+ dict.fromkeys(var.name for var in expr_val.get_classical_vars().values())
273
241
  )
242
+ return [
243
+ (var, get_proxy_type(proxy))
244
+ for var in classical_vars
245
+ if var in self._current_scope
246
+ and isinstance(proxy := self._current_scope[var].value, ClassicalProxy)
247
+ ]
274
248
 
275
249
  def _get_quantum_type_attributes_in_expression(
276
250
  self, expr: Expression
277
251
  ) -> list[FieldHandleBinding]:
278
- vrc = VarRefCollector(ignore_duplicated_handles=True, unevaluated=True)
279
- vrc.visit(ast.parse(expr.expr))
280
- return list(
281
- dict.fromkeys(
282
- handle
283
- for handle in vrc.var_handles
284
- if isinstance(handle, FieldHandleBinding)
285
- and handle.field in CLASSICAL_ATTRIBUTES
286
- and isinstance(self._current_scope[handle.name].value, QuantumSymbol)
287
- )
288
- )
252
+ expr_val = expr.value.value
253
+ if not isinstance(expr_val, QmodAnnotatedExpression):
254
+ return []
255
+ return [
256
+ FieldHandleBinding(base_handle=type_attr.value, field=type_attr.attr)
257
+ for type_attr in expr_val.get_quantum_type_attributes().values()
258
+ ]
@@ -13,16 +13,27 @@ if TYPE_CHECKING:
13
13
 
14
14
 
15
15
  class ExpressionEvaluator(Emitter[QuantumOperation]):
16
- def __init__(self, interpreter: "BaseInterpreter", expression_name: str) -> None:
16
+ def __init__(
17
+ self,
18
+ interpreter: "BaseInterpreter",
19
+ expression_name: str,
20
+ *,
21
+ simplify: bool = False,
22
+ treat_qnum_as_float: bool = False,
23
+ ) -> None:
17
24
  super().__init__(interpreter)
18
25
  self._expression_name = expression_name
26
+ self._simplify = simplify
27
+ self._treat_qnum_as_float = treat_qnum_as_float
19
28
 
20
29
  def emit(self, op: QuantumOperation, /) -> bool:
21
30
  expression = getattr(op, self._expression_name)
22
31
  if not isinstance(expression, Expression) or expression.is_evaluated():
23
32
  return False
24
33
  evaluated_expression = self._evaluate_expression(
25
- expression, preserve_bool_ops=True
34
+ expression,
35
+ simplify=self._simplify,
36
+ treat_qnum_as_float=self._treat_qnum_as_float,
26
37
  )
27
38
  for symbol in self._get_symbols_in_expression(evaluated_expression):
28
39
  self._capture_handle(symbol.handle, PortDeclarationDirection.Inout)
@@ -1,7 +1,5 @@
1
1
  from typing import TYPE_CHECKING
2
2
 
3
- import sympy
4
-
5
3
  from classiq.interface.exceptions import ClassiqInternalExpansionError
6
4
  from classiq.interface.generator.expressions.expression import Expression
7
5
  from classiq.interface.model.classical_if import ClassicalIf
@@ -9,6 +7,7 @@ from classiq.interface.model.quantum_function_call import QuantumFunctionCall
9
7
  from classiq.interface.model.quantum_lambda_function import OperandIdentifier
10
8
  from classiq.interface.model.quantum_statement import QuantumStatement
11
9
 
10
+ from classiq.evaluators.qmod_annotated_expression import QmodAnnotatedExpression
12
11
  from classiq.model_expansions.closure import FunctionClosure
13
12
  from classiq.model_expansions.quantum_operations.call_emitter import CallEmitter
14
13
  from classiq.model_expansions.quantum_operations.declarative_call_emitter import (
@@ -28,7 +27,7 @@ class QuantumFunctionCallEmitter(CallEmitter[QuantumFunctionCall]):
28
27
  def emit(self, call: QuantumFunctionCall, /) -> bool:
29
28
  if isinstance(call.function, OperandIdentifier):
30
29
  index_val = self._interpreter.evaluate(call.function.index).value
31
- if isinstance(index_val, sympy.Basic):
30
+ if isinstance(index_val, QmodAnnotatedExpression):
32
31
  return self._emit_symbolic_lambda_list(call, index_val)
33
32
  function = self._interpreter.evaluate(call.function).as_type(FunctionClosure)
34
33
  args = call.positional_args
@@ -39,7 +38,7 @@ class QuantumFunctionCallEmitter(CallEmitter[QuantumFunctionCall]):
39
38
  return True
40
39
 
41
40
  def _emit_symbolic_lambda_list(
42
- self, call: QuantumFunctionCall, index: sympy.Basic
41
+ self, call: QuantumFunctionCall, index: QmodAnnotatedExpression
43
42
  ) -> bool:
44
43
  if TYPE_CHECKING:
45
44
  assert isinstance(call.function, OperandIdentifier)
@@ -54,7 +53,7 @@ class QuantumFunctionCallEmitter(CallEmitter[QuantumFunctionCall]):
54
53
 
55
54
  @staticmethod
56
55
  def _create_recursive_if(
57
- call: QuantumFunctionCall, index: sympy.Basic, num_funcs: int
56
+ call: QuantumFunctionCall, index: QmodAnnotatedExpression, num_funcs: int
58
57
  ) -> list[QuantumStatement]:
59
58
  if TYPE_CHECKING:
60
59
  assert isinstance(call.function, OperandIdentifier)
@@ -1,7 +1,8 @@
1
- from typing import TYPE_CHECKING, Union
1
+ from typing import TYPE_CHECKING, Union, cast
2
2
 
3
3
  from classiq.interface.exceptions import ClassiqExpansionError
4
4
  from classiq.interface.generator.functions.classical_type import ClassicalType
5
+ from classiq.interface.generator.functions.concrete_types import ConcreteType
5
6
  from classiq.interface.model.handle_binding import HandleBinding
6
7
  from classiq.interface.model.quantum_type import QuantumType
7
8
  from classiq.interface.model.variable_declaration_statement import (
@@ -30,13 +31,15 @@ class VariableDeclarationStatementEmitter(Emitter[VariableDeclarationStatement])
30
31
  if variable_declaration.is_quantum:
31
32
  if TYPE_CHECKING:
32
33
  assert isinstance(var_decl.qmod_type, QuantumType)
34
+ updated_quantum_type = evaluate_type_in_quantum_symbol(
35
+ var_decl.qmod_type,
36
+ self._current_scope,
37
+ var_decl.name,
38
+ )
39
+ var_decl.qmod_type = updated_quantum_type
33
40
  var_value = QuantumSymbol(
34
41
  handle=HandleBinding(name=var_decl.name),
35
- quantum_type=evaluate_type_in_quantum_symbol(
36
- var_decl.qmod_type,
37
- self._current_scope,
38
- var_decl.name,
39
- ),
42
+ quantum_type=updated_quantum_type,
40
43
  )
41
44
  self._builder.current_block.captured_vars.init_var(
42
45
  var_decl.name, self._builder.current_function
@@ -44,13 +47,15 @@ class VariableDeclarationStatementEmitter(Emitter[VariableDeclarationStatement])
44
47
  else:
45
48
  if TYPE_CHECKING:
46
49
  assert isinstance(var_decl.qmod_type, ClassicalType)
50
+ updated_classical_type = evaluate_type_in_classical_symbol(
51
+ var_decl.qmod_type,
52
+ self._current_scope,
53
+ var_decl.name,
54
+ )
55
+ var_decl.qmod_type = cast(ConcreteType, updated_classical_type)
47
56
  var_value = ClassicalSymbol(
48
57
  handle=HandleBinding(name=var_decl.name),
49
- classical_type=evaluate_type_in_classical_symbol(
50
- var_decl.qmod_type,
51
- self._current_scope,
52
- var_decl.name,
53
- ),
58
+ classical_type=updated_classical_type,
54
59
  )
55
60
  self._current_scope[variable_declaration.name] = Evaluated(
56
61
  value=var_value, defining_function=self._builder.current_function
@@ -15,9 +15,6 @@ from classiq.interface.generator.expressions.evaluated_expression import (
15
15
  EvaluatedExpression,
16
16
  )
17
17
  from classiq.interface.generator.expressions.expression import Expression
18
- from classiq.interface.generator.expressions.proxies.classical.classical_scalar_proxy import (
19
- ClassicalScalarProxy,
20
- )
21
18
  from classiq.interface.generator.expressions.proxies.classical.qmod_struct_instance import (
22
19
  QmodStructInstance,
23
20
  )
@@ -43,6 +40,9 @@ from classiq.interface.model.quantum_type import (
43
40
  QuantumType,
44
41
  )
45
42
 
43
+ from classiq.evaluators.qmod_annotated_expression import QmodAnnotatedExpression
44
+ from classiq.evaluators.qmod_node_evaluators.utils import get_sympy_val
45
+
46
46
  if TYPE_CHECKING:
47
47
  from classiq.model_expansions.closure import FunctionClosure
48
48
 
@@ -69,7 +69,7 @@ class QuantumSymbol(QuantumVariable):
69
69
  return self.handle
70
70
 
71
71
  def __getitem__(
72
- self, item: Union[slice, int, ClassicalScalarProxy]
72
+ self, item: Union[slice, int, QmodAnnotatedExpression]
73
73
  ) -> "QuantumSymbol":
74
74
  if isinstance(item, slice):
75
75
  return self._slice(item.start, item.stop)
@@ -77,8 +77,8 @@ class QuantumSymbol(QuantumVariable):
77
77
 
78
78
  def _slice(
79
79
  self,
80
- start: Union[int, ClassicalScalarProxy],
81
- end: Union[int, ClassicalScalarProxy],
80
+ start: Union[int, QmodAnnotatedExpression],
81
+ end: Union[int, QmodAnnotatedExpression],
82
82
  ) -> "QuantumSymbol":
83
83
  if not isinstance(self.quantum_type, QuantumBitvector):
84
84
  raise ClassiqExpansionError(
@@ -100,19 +100,27 @@ class QuantumSymbol(QuantumVariable):
100
100
  f"{self.quantum_type.type_name.lower()} {str(self.handle)!r} (of "
101
101
  f"length {self.quantum_type.length_value})"
102
102
  )
103
+ start_expr = Expression(expr=str(start))
104
+ start_expr._evaluated_expr = EvaluatedExpression(value=start)
105
+ end_expr = Expression(expr=str(end))
106
+ end_expr._evaluated_expr = EvaluatedExpression(value=end)
107
+ if isinstance(start, int) and isinstance(end, int):
108
+ length_expr = Expression(expr=str(end - start))
109
+ else:
110
+ length_expr = None
103
111
  return QuantumSymbol(
104
112
  handle=SlicedHandleBinding(
105
113
  base_handle=self.handle,
106
- start=Expression(expr=str(start)),
107
- end=Expression(expr=str(end)),
114
+ start=start_expr,
115
+ end=end_expr,
108
116
  ),
109
117
  quantum_type=QuantumBitvector(
110
118
  element_type=self.quantum_type.element_type,
111
- length=Expression(expr=str(end - start)),
119
+ length=length_expr,
112
120
  ),
113
121
  )
114
122
 
115
- def _subscript(self, index: Union[int, ClassicalScalarProxy]) -> "QuantumSymbol":
123
+ def _subscript(self, index: Union[int, QmodAnnotatedExpression]) -> "QuantumSymbol":
116
124
  if not isinstance(self.quantum_type, QuantumBitvector):
117
125
  raise ClassiqExpansionError(
118
126
  f"{self.quantum_type.type_name} is not subscriptable"
@@ -135,11 +143,10 @@ class QuantumSymbol(QuantumVariable):
135
143
  f"{self.quantum_type.type_name.lower()} {str(self.handle)!r}"
136
144
  f"{length_suffix}"
137
145
  )
146
+ index_expr = Expression(expr=str(index))
147
+ index_expr._evaluated_expr = EvaluatedExpression(value=index)
138
148
  return QuantumSymbol(
139
- handle=SubscriptHandleBinding(
140
- base_handle=self.handle,
141
- index=Expression(expr=str(index)),
142
- ),
149
+ handle=SubscriptHandleBinding(base_handle=self.handle, index=index_expr),
143
150
  quantum_type=self.quantum_type.element_type,
144
151
  )
145
152
 
@@ -204,11 +211,15 @@ class QuantumSymbolList(QuantumVariable):
204
211
 
205
212
 
206
213
  @dataclass(frozen=True)
207
- class ClassicalSymbol:
208
- handle: HandleBinding
214
+ class ClassicalVariable:
209
215
  classical_type: ClassicalType
210
216
 
211
217
 
218
+ @dataclass(frozen=True)
219
+ class ClassicalSymbol(ClassicalVariable):
220
+ handle: HandleBinding
221
+
222
+
212
223
  @singledispatch
213
224
  def evaluated_to_str(value: Any) -> str:
214
225
  return str(value)
@@ -225,8 +236,8 @@ def _evaluated_to_str_struct_literal(value: QmodStructInstance) -> str:
225
236
 
226
237
 
227
238
  def _raise_type_error(val: Any, t: type, location_hint: Optional[str]) -> NoReturn:
228
- if isinstance(val, sympy.Basic) and len(val.free_symbols) > 0:
229
- symbolic_vars = sorted(map(str, val.free_symbols))
239
+ if isinstance(val, QmodAnnotatedExpression) and len(val.get_classical_vars()) > 0:
240
+ symbolic_vars = sorted(map(str, val.get_classical_vars().values()))
230
241
  suffix = f" {location_hint}" if location_hint is not None else ""
231
242
  raise ClassiqExpansionError(
232
243
  f"Cannot use execution parameter{s(symbolic_vars)} {readable_list(symbolic_vars, quote=True)} in a compile-time context{suffix}"
@@ -240,19 +251,15 @@ class Evaluated: # FIXME: Merge with EvaluatedExpression if possible
240
251
  defining_function: Optional["FunctionClosure"] = None
241
252
 
242
253
  def as_type(self, t: type[T], location_hint: Optional[str] = None) -> T:
243
- if t is int:
244
- return self._as_int(location_hint) # type: ignore[return-value]
245
-
246
- if not isinstance(self.value, t):
254
+ value = self.value
255
+ if isinstance(value, sympy.Basic):
256
+ value = get_sympy_val(value)
257
+ if t is int and isinstance(value, float):
258
+ value = int(value)
259
+ if not isinstance(value, t):
247
260
  _raise_type_error(self.value, t, location_hint)
248
261
 
249
- return self.value
250
-
251
- def _as_int(self, location_hint: Optional[str]) -> int:
252
- if not isinstance(self.value, (int, float)):
253
- _raise_type_error(self.value, int, location_hint)
254
-
255
- return int(self.value)
262
+ return value
256
263
 
257
264
  def emit(self, param: Optional[AnonPositionalArg] = None) -> ArgValue:
258
265
  from classiq.model_expansions.closure import FunctionClosure
@@ -11,9 +11,7 @@ from classiq.interface.model.native_function_definition import NativeFunctionDef
11
11
  from classiq.interface.model.port_declaration import PortDeclaration
12
12
  from classiq.interface.model.quantum_function_declaration import PositionalArg
13
13
 
14
- from classiq.evaluators.classical_expression import (
15
- evaluate_classical_expression,
16
- )
14
+ from classiq.evaluators.classical_expression import evaluate_classical_expression
17
15
  from classiq.evaluators.parameter_types import (
18
16
  evaluate_type_in_quantum_symbol,
19
17
  )
@@ -33,9 +31,8 @@ from classiq.qmod.semantics.annotation.qstruct_annotator import QStructAnnotator
33
31
 
34
32
  def add_constants_to_scope(constants: list[Constant], scope: Scope) -> None:
35
33
  for constant in constants:
36
- scope[constant.name] = Evaluated(
37
- value=evaluate_classical_expression(constant.value, scope).value
38
- )
34
+ expr_val = evaluate_classical_expression(constant.value, scope).value
35
+ scope[constant.name] = Evaluated(value=expr_val)
39
36
 
40
37
 
41
38
  def add_functions_to_scope(
@@ -16,11 +16,15 @@ from sympy.logic.boolalg import BooleanAtom
16
16
  from sympy.printing.pycode import PythonCodePrinter
17
17
 
18
18
  from classiq.interface.exceptions import ClassiqInternalExpansionError
19
- from classiq.interface.generator.expressions.expression_types import ExpressionValue
19
+ from classiq.interface.generator.expressions.expression_types import (
20
+ ExpressionValue,
21
+ RuntimeConstant,
22
+ )
20
23
  from classiq.interface.generator.expressions.proxies.classical.any_classical_value import (
21
24
  AnyClassicalValue,
22
25
  )
23
26
 
27
+ from classiq.evaluators.qmod_annotated_expression import QmodAnnotatedExpression
24
28
  from classiq.evaluators.qmod_expression_visitors.sympy_wrappers import LogicalXor
25
29
 
26
30
 
@@ -48,7 +52,7 @@ def sympy_to_python(
48
52
  if value is None:
49
53
  value = False
50
54
 
51
- if not isinstance(value, get_args(ExpressionValue)):
55
+ if not isinstance(value, (*get_args(RuntimeConstant), QmodAnnotatedExpression)):
52
56
  raise ClassiqInternalExpansionError(
53
57
  f"Invalid evaluated expression {value} of type {type(value)}"
54
58
  )
@@ -1,26 +1,27 @@
1
- import ast
2
1
  import re
3
2
  from collections.abc import Mapping, Sequence
4
3
  from dataclasses import dataclass
5
- from functools import cmp_to_key
6
4
  from typing import TypeVar, cast
7
5
 
8
- from classiq.interface.exceptions import ClassiqInternalExpansionError
6
+ from classiq.interface.generator.expressions.atomic_expression_functions import (
7
+ CLASSICAL_ATTRIBUTES,
8
+ )
9
9
  from classiq.interface.generator.expressions.evaluated_expression import (
10
10
  EvaluatedExpression,
11
11
  )
12
12
  from classiq.interface.generator.expressions.expression import Expression
13
- from classiq.interface.generator.expressions.proxies.classical.any_classical_value import (
14
- AnyClassicalValue,
15
- )
16
13
  from classiq.interface.generator.visitor import NodeType
17
- from classiq.interface.model.handle_binding import HandleBinding
14
+ from classiq.interface.model.handle_binding import FieldHandleBinding, HandleBinding
18
15
  from classiq.interface.model.model_visitor import ModelTransformer
19
16
  from classiq.interface.model.quantum_expressions.quantum_expression import (
20
17
  QuantumExpressionOperation,
21
18
  )
22
19
 
23
- from classiq.model_expansions.visitors.variable_references import VarRefCollector
20
+ from classiq.evaluators.qmod_annotated_expression import QmodAnnotatedExpression
21
+ from classiq.evaluators.qmod_expression_visitors.qmod_expression_renamer import (
22
+ replace_expression_type_attrs,
23
+ replace_expression_vars,
24
+ )
24
25
 
25
26
  AST_NODE = TypeVar("AST_NODE", bound=NodeType)
26
27
 
@@ -39,31 +40,6 @@ def _handle_contains_handle(handle: HandleBinding, other_handle: HandleBinding)
39
40
  return 0
40
41
 
41
42
 
42
- class ExprNormalizer(ast.NodeTransformer):
43
- def visit_Call(self, node: ast.Call) -> ast.AST:
44
- if not isinstance(node.func, ast.Name):
45
- return self.generic_visit(node)
46
- if node.func.id == "get_field":
47
- if (
48
- len(node.args) != 2
49
- or not isinstance(node.args[1], ast.Constant)
50
- or not isinstance(node.args[1].value, str)
51
- ):
52
- raise ClassiqInternalExpansionError("Unexpected 'get_field' arguments")
53
- return ast.Attribute(
54
- value=self.visit(node.args[0]), attr=node.args[1].value
55
- )
56
- if node.func.id == "do_subscript":
57
- if len(node.args) != 2:
58
- raise ClassiqInternalExpansionError(
59
- "Unexpected 'do_subscript' arguments"
60
- )
61
- return ast.Subscript(
62
- value=self.visit(node.args[0]), slice=self.visit(node.args[1])
63
- )
64
- return self.generic_visit(node)
65
-
66
-
67
43
  @dataclass(frozen=True)
68
44
  class HandleRenaming:
69
45
  source_handle: HandleBinding
@@ -80,38 +56,33 @@ SymbolRenaming = Mapping[HandleBinding, Sequence[HandleRenaming]]
80
56
  def rewrite_expression(
81
57
  symbol_mapping: SymbolRenaming, expression: Expression
82
58
  ) -> Expression:
83
- normalized_expr = ExprNormalizer().visit(ast.parse(expression.expr))
84
- vrc = VarRefCollector(
85
- ignore_duplicated_handles=True, ignore_sympy_symbols=True, unevaluated=True
86
- )
87
- vrc.visit(normalized_expr)
88
-
89
- handle_names = {
90
- part.source_handle: part.target_var_handle
91
- for source, parts in symbol_mapping.items()
92
- if source.name in expression.expr
93
- for part in parts
59
+ if len(symbol_mapping) == 0:
60
+ return expression
61
+ expr_val = expression.value.value
62
+ if not isinstance(expr_val, QmodAnnotatedExpression):
63
+ return expression
64
+
65
+ type_attr_mapping = {
66
+ (source_handle.base_handle, source_attr): renaming.target_var_handle
67
+ for renamings in symbol_mapping.values()
68
+ for renaming in renamings
69
+ if isinstance(source_handle := renaming.source_handle, FieldHandleBinding)
70
+ and (source_attr := source_handle.field) in CLASSICAL_ATTRIBUTES
94
71
  }
95
- new_expr_str = ast.unparse(normalized_expr)
96
- sorted_handles = sorted(
97
- vrc.var_handles,
98
- key=cmp_to_key( # type:ignore[misc]
99
- lambda handle, other_handle: _handle_contains_handle(other_handle, handle)
100
- ),
101
- )
102
- for handle in sorted_handles:
103
- new_handle = handle.collapse()
104
- for handle_to_replace, replacement in handle_names.items():
105
- if new_handle.name == handle_to_replace.name:
106
- new_handle = new_handle.replace_prefix(handle_to_replace, replacement)
107
- new_expr_str = _replace_full_word(str(handle), str(new_handle), new_expr_str)
108
-
109
- new_expr = Expression(expr=new_expr_str)
110
- if not new_expr.is_evaluated():
111
- new_expr._evaluated_expr = EvaluatedExpression(
112
- value=AnyClassicalValue(new_expr_str)
113
- )
114
- return new_expr
72
+ expr_val = replace_expression_type_attrs(expr_val, type_attr_mapping)
73
+
74
+ var_mapping = {
75
+ source_handle: renaming.target_var_handle
76
+ for renamings in symbol_mapping.values()
77
+ for renaming in renamings
78
+ if not isinstance(source_handle := renaming.source_handle, FieldHandleBinding)
79
+ or source_handle.field not in CLASSICAL_ATTRIBUTES
80
+ }
81
+ expr_val = replace_expression_vars(expr_val, var_mapping)
82
+
83
+ renamed_expr = Expression(expr=str(expr_val))
84
+ renamed_expr._evaluated_expr = EvaluatedExpression(value=expr_val)
85
+ return renamed_expr
115
86
 
116
87
 
117
88
  class _ReplaceSplitVarsHandles(ModelTransformer):
@@ -1,4 +1,3 @@
1
- import ast
2
1
  import functools
3
2
  import itertools
4
3
  import warnings
@@ -37,7 +36,7 @@ from classiq.interface.model.quantum_function_call import QuantumFunctionCall
37
36
  from classiq.interface.model.within_apply_operation import WithinApply
38
37
  from classiq.interface.source_reference import SourceReference
39
38
 
40
- from classiq.model_expansions.visitors.variable_references import VarRefCollector
39
+ from classiq.evaluators.qmod_annotated_expression import QmodAnnotatedExpression
41
40
 
42
41
 
43
42
  def _inconsistent_type_modifier_error(
@@ -302,11 +301,12 @@ class TypeModifierValidation(ModelVisitor):
302
301
 
303
302
  @staticmethod
304
303
  def _extract_expr_vars(expr_op: QuantumExpressionOperation) -> list[str]:
305
- vrc = VarRefCollector(
306
- ignore_duplicated_handles=True, ignore_sympy_symbols=True, unevaluated=True
304
+ expr_val = expr_op.expression.value.value
305
+ if not isinstance(expr_val, QmodAnnotatedExpression):
306
+ return []
307
+ return list(
308
+ dict.fromkeys(var.name for var in expr_val.get_quantum_vars().values())
307
309
  )
308
- vrc.visit(ast.parse(expr_op.expression.expr))
309
- return [handle.name for handle in vrc.var_handles]
310
310
 
311
311
  def visit_ArithmeticOperation(self, arith: ArithmeticOperation) -> None:
312
312
  with self.source_reference_context(arith.source_ref):