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
@@ -1,29 +1,18 @@
1
1
  from collections.abc import Sequence
2
2
  from itertools import chain, combinations
3
3
  from typing import (
4
- Any,
5
4
  Generic,
6
5
  Optional,
7
6
  cast,
8
7
  )
9
8
  from uuid import UUID
10
9
 
11
- import sympy
12
-
13
10
  from classiq.interface.debug_info.debug_info import (
14
11
  FunctionDebugInfo,
15
12
  calculate_port_to_passed_variable_mapping,
13
+ new_function_debug_info_by_node,
16
14
  )
17
15
  from classiq.interface.exceptions import ClassiqExpansionError
18
- from classiq.interface.generator.expressions.proxies.classical.any_classical_value import (
19
- AnyClassicalValue,
20
- )
21
- from classiq.interface.generator.expressions.proxies.classical.classical_proxy import (
22
- ClassicalProxy,
23
- )
24
- from classiq.interface.generator.expressions.proxies.classical.qmod_struct_instance import (
25
- QmodStructInstance,
26
- )
27
16
  from classiq.interface.generator.functions.port_declaration import (
28
17
  PortDeclarationDirection,
29
18
  )
@@ -31,6 +20,7 @@ from classiq.interface.generator.functions.type_modifier import TypeModifier
31
20
  from classiq.interface.generator.types.compilation_metadata import CompilationMetadata
32
21
  from classiq.interface.helpers.backward_compatibility import zip_strict
33
22
  from classiq.interface.helpers.text_utils import are, readable_list, s
23
+ from classiq.interface.model.block import Block
34
24
  from classiq.interface.model.classical_parameter_declaration import (
35
25
  ClassicalParameterDeclaration,
36
26
  )
@@ -56,6 +46,7 @@ from classiq.evaluators.argument_types import (
56
46
  from classiq.evaluators.parameter_types import (
57
47
  evaluate_parameter_types_from_args,
58
48
  )
49
+ from classiq.evaluators.qmod_annotated_expression import QmodAnnotatedExpression
59
50
  from classiq.model_expansions.capturing.captured_vars import (
60
51
  INITIALIZED_VAR_MESSAGE,
61
52
  UNINITIALIZED_VAR_MESSAGE,
@@ -73,6 +64,7 @@ from classiq.model_expansions.quantum_operations.function_calls_cache import (
73
64
  get_func_call_cache_key,
74
65
  )
75
66
  from classiq.model_expansions.scope import (
67
+ ClassicalSymbol,
76
68
  Evaluated,
77
69
  QuantumSymbol,
78
70
  QuantumSymbolList,
@@ -111,18 +103,6 @@ def _validate_cloning(evaluated_args: list[Evaluated]) -> None:
111
103
  )
112
104
 
113
105
 
114
- def _is_symbolic(arg: Any) -> bool:
115
- if isinstance(arg, (ClassicalProxy, AnyClassicalValue)):
116
- return True
117
- if isinstance(arg, list):
118
- return any(_is_symbolic(item) for item in arg)
119
- if isinstance(arg, QmodStructInstance):
120
- return any(_is_symbolic(item) for item in arg.fields.values())
121
- if isinstance(arg, sympy.Basic):
122
- return len(arg.free_symbols) > 0
123
- return False
124
-
125
-
126
106
  def _validate_gen_args(
127
107
  function: FunctionClosure, evaluated_args: list[Evaluated]
128
108
  ) -> None:
@@ -132,7 +112,7 @@ def _validate_gen_args(
132
112
  if (
133
113
  isinstance(param, ClassicalParameterDeclaration)
134
114
  and not param.classical_type.is_purely_declarative
135
- and _is_symbolic(arg.value)
115
+ and isinstance(arg.value, QmodAnnotatedExpression)
136
116
  ):
137
117
  readable_expr = transform_expression(str(arg.value), {}, {}, one_line=True)
138
118
  raise ClassiqExpansionError(
@@ -141,6 +121,27 @@ def _validate_gen_args(
141
121
  )
142
122
 
143
123
 
124
+ def _validate_runtime_args(args: list[Evaluated], scope: Scope) -> None:
125
+ for arg in args:
126
+ arg_val = arg.value
127
+ if not isinstance(arg_val, QmodAnnotatedExpression):
128
+ continue
129
+ classical_vars = dict.fromkeys(
130
+ var.name for var in arg_val.get_classical_vars().values()
131
+ )
132
+ runtime_classical_vars = [
133
+ var
134
+ for var in classical_vars
135
+ if isinstance(scope[var].value, ClassicalSymbol)
136
+ ]
137
+ if len(runtime_classical_vars) > 0:
138
+ raise ClassiqExpansionError(
139
+ f"Passing runtime variable{s(runtime_classical_vars)} "
140
+ f"{readable_list(runtime_classical_vars, quote=True)} as function call "
141
+ f"arguments is not supported"
142
+ )
143
+
144
+
144
145
  class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], ModelRenamer):
145
146
  @staticmethod
146
147
  def _should_wrap(body: Sequence[QuantumStatement]) -> bool:
@@ -148,6 +149,11 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], ModelR
148
149
  # I'm sure there are better ways to handle it, but this is the simplest way to do it for now
149
150
  return any(isinstance(stmt, VariableDeclarationStatement) for stmt in body)
150
151
 
152
+ def _create_block_labeled_ref(self, label: str) -> FunctionDebugInfo:
153
+ bake_ref_node = Block(statements=[], label=label)
154
+ self._interpreter.add_to_debug_info(statement=bake_ref_node)
155
+ return new_function_debug_info_by_node(bake_ref_node)
156
+
151
157
  def _create_expanded_wrapping_function(
152
158
  self,
153
159
  name: str,
@@ -195,6 +201,7 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], ModelR
195
201
  self._validate_call_args(function.positional_arg_declarations, args)
196
202
  evaluated_args = [self._interpreter.evaluate(arg) for arg in args]
197
203
  _validate_cloning(evaluated_args)
204
+ _validate_runtime_args(evaluated_args, self._current_scope)
198
205
  new_declaration = self._prepare_fully_typed_declaration(
199
206
  function, evaluated_args
200
207
  )
@@ -385,7 +392,6 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], ModelR
385
392
  name=function.name,
386
393
  positional_arg_declarations=evaluate_parameter_types_from_args(
387
394
  function,
388
- function.signature_scope,
389
395
  evaluated_args,
390
396
  ),
391
397
  )
@@ -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
- 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
- )
48
+ from classiq.model_expansions.scope import ClassicalSymbol, QuantumSymbol, Scope
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,49 @@ 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
+ (
244
+ var,
245
+ (
246
+ get_proxy_type(proxy)
247
+ if isinstance(proxy, ClassicalProxy)
248
+ else proxy.classical_type
249
+ ),
250
+ )
251
+ for var in classical_vars
252
+ if var in self._current_scope
253
+ and isinstance(
254
+ proxy := self._current_scope[var].value,
255
+ (ClassicalProxy, ClassicalSymbol),
256
+ )
257
+ ]
274
258
 
275
259
  def _get_quantum_type_attributes_in_expression(
276
260
  self, expr: Expression
277
261
  ) -> 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
- )
262
+ expr_val = expr.value.value
263
+ if not isinstance(expr_val, QmodAnnotatedExpression):
264
+ return []
265
+ return [
266
+ FieldHandleBinding(base_handle=type_attr.value, field=type_attr.attr)
267
+ for type_attr in expr_val.get_quantum_type_attributes().values()
268
+ ]
@@ -1,38 +1,93 @@
1
- from typing import TYPE_CHECKING
1
+ from typing import TYPE_CHECKING, Optional
2
2
 
3
+ from classiq.interface.exceptions import (
4
+ ClassiqExpansionError,
5
+ ClassiqInternalExpansionError,
6
+ )
3
7
  from classiq.interface.generator.expressions.expression import Expression
4
8
  from classiq.interface.generator.functions.port_declaration import (
5
9
  PortDeclarationDirection,
6
10
  )
11
+ from classiq.interface.helpers.text_utils import readable_list, s
7
12
  from classiq.interface.model.quantum_statement import QuantumOperation
8
13
 
9
14
  from classiq.model_expansions.quantum_operations.emitter import Emitter
15
+ from classiq.model_expansions.scope import ClassicalSymbol
10
16
 
11
17
  if TYPE_CHECKING:
12
18
  from classiq.model_expansions.interpreters.base_interpreter import BaseInterpreter
13
19
 
14
20
 
15
21
  class ExpressionEvaluator(Emitter[QuantumOperation]):
16
- def __init__(self, interpreter: "BaseInterpreter", expression_name: str) -> None:
22
+ def __init__(
23
+ self,
24
+ interpreter: "BaseInterpreter",
25
+ expression_name: str,
26
+ *,
27
+ readable_expression_name: Optional[str] = None,
28
+ simplify: bool = False,
29
+ treat_qnum_as_float: bool = False,
30
+ allow_link_time_vars: bool = True,
31
+ allow_runtime_vars: bool = True,
32
+ ) -> None:
17
33
  super().__init__(interpreter)
18
34
  self._expression_name = expression_name
35
+ self._simplify = simplify
36
+ self._treat_qnum_as_float = treat_qnum_as_float
37
+ self._allow_link_time_vars = allow_link_time_vars
38
+ self._allow_runtime_vars = allow_runtime_vars
39
+ if (
40
+ not allow_link_time_vars or not allow_runtime_vars
41
+ ) and readable_expression_name is None:
42
+ raise ClassiqInternalExpansionError
43
+ self._readable_expression_name = readable_expression_name
19
44
 
20
45
  def emit(self, op: QuantumOperation, /) -> bool:
21
46
  expression = getattr(op, self._expression_name)
22
47
  if not isinstance(expression, Expression) or expression.is_evaluated():
23
48
  return False
24
49
  evaluated_expression = self._evaluate_expression(
25
- expression, preserve_bool_ops=True
50
+ expression,
51
+ simplify=self._simplify,
52
+ treat_qnum_as_float=self._treat_qnum_as_float,
26
53
  )
27
54
  for symbol in self._get_symbols_in_expression(evaluated_expression):
28
55
  self._capture_handle(symbol.handle, PortDeclarationDirection.Inout)
29
- for var_name, var_type in self._get_classical_vars_in_expression(
30
- evaluated_expression
31
- ):
32
- self._capture_classical_var(var_name, var_type)
56
+ self._process_classical_parameters(evaluated_expression)
33
57
  op = op.model_copy(
34
58
  update={self._expression_name: evaluated_expression, "back_ref": op.uuid}
35
59
  )
36
60
  self._interpreter.add_to_debug_info(op)
37
61
  self._interpreter.emit(op)
38
62
  return True
63
+
64
+ def _process_classical_parameters(self, evaluated_expression: Expression) -> None:
65
+ link_time_vars: list[str] = []
66
+ runtime_vars: list[str] = []
67
+ for var_name, var_type in self._get_classical_vars_in_expression(
68
+ evaluated_expression
69
+ ):
70
+ if isinstance(self._current_scope[var_name].value, ClassicalSymbol):
71
+ runtime_vars.append(var_name)
72
+ else:
73
+ link_time_vars.append(var_name)
74
+ self._capture_classical_var(var_name, var_type)
75
+ if not self._allow_link_time_vars and len(link_time_vars) > 0:
76
+ link_time_message = f"link-time variable{s(link_time_vars)} {readable_list(link_time_vars, quote=True)}"
77
+ else:
78
+ link_time_message = None
79
+ if not self._allow_runtime_vars and len(runtime_vars) > 0:
80
+ runtime_message = f"runtime variable{s(runtime_vars)} {readable_list(runtime_vars, quote=True)}"
81
+ else:
82
+ runtime_message = None
83
+ if link_time_message is None:
84
+ error_message = runtime_message
85
+ elif runtime_message is None:
86
+ error_message = link_time_message
87
+ else:
88
+ error_message = f"{link_time_message} and {runtime_message}"
89
+ if error_message is not None:
90
+ raise ClassiqExpansionError(
91
+ f"The {self._readable_expression_name} cannot receive "
92
+ f"{error_message}"
93
+ )
@@ -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