classiq 0.74.0__py3-none-any.whl → 0.76.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.
Files changed (96) hide show
  1. classiq/_internals/api_wrapper.py +36 -0
  2. classiq/analyzer/show_interactive_hack.py +58 -2
  3. classiq/applications/chemistry/chemistry_model_constructor.py +8 -1
  4. classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +2 -0
  5. classiq/applications/combinatorial_optimization/combinatorial_problem.py +4 -4
  6. classiq/applications/qnn/gradients/quantum_gradient.py +3 -5
  7. classiq/applications/qnn/gradients/simple_quantum_gradient.py +2 -2
  8. classiq/applications/qnn/qlayer.py +23 -19
  9. classiq/applications/qnn/types.py +1 -4
  10. classiq/execution/__init__.py +3 -0
  11. classiq/execution/execution_session.py +3 -16
  12. classiq/execution/qnn.py +2 -2
  13. classiq/execution/user_budgets.py +38 -0
  14. classiq/executor.py +7 -19
  15. classiq/interface/_version.py +1 -1
  16. classiq/interface/debug_info/debug_info.py +18 -13
  17. classiq/interface/executor/user_budget.py +56 -0
  18. classiq/interface/generator/application_apis/finance_declarations.py +3 -0
  19. classiq/interface/generator/expressions/atomic_expression_functions.py +3 -0
  20. classiq/interface/generator/expressions/proxies/classical/any_classical_value.py +30 -124
  21. classiq/interface/generator/expressions/proxies/classical/classical_array_proxy.py +46 -22
  22. classiq/interface/generator/expressions/proxies/classical/qmod_struct_instance.py +7 -0
  23. classiq/interface/generator/expressions/proxies/classical/utils.py +14 -13
  24. classiq/interface/generator/expressions/proxies/quantum/qmod_qscalar_proxy.py +9 -2
  25. classiq/interface/generator/expressions/proxies/quantum/qmod_sized_proxy.py +4 -1
  26. classiq/interface/generator/expressions/sympy_supported_expressions.py +1 -0
  27. classiq/interface/generator/functions/classical_type.py +36 -1
  28. classiq/interface/generator/functions/type_name.py +32 -5
  29. classiq/interface/generator/functions/type_qualifier.py +15 -0
  30. classiq/interface/generator/generated_circuit_data.py +11 -25
  31. classiq/interface/generator/model/preferences/preferences.py +7 -0
  32. classiq/interface/generator/quantum_program.py +5 -19
  33. classiq/interface/generator/synthesis_metadata/synthesis_execution_data.py +10 -13
  34. classiq/interface/helpers/backward_compatibility.py +9 -0
  35. classiq/interface/helpers/datastructures.py +6 -0
  36. classiq/interface/helpers/versioned_model.py +12 -0
  37. classiq/interface/interface_version.py +1 -1
  38. classiq/interface/model/handle_binding.py +12 -0
  39. classiq/interface/model/port_declaration.py +1 -2
  40. classiq/interface/model/quantum_lambda_function.py +2 -1
  41. classiq/interface/model/statement_block.py +9 -1
  42. classiq/interface/model/within_apply_operation.py +12 -0
  43. classiq/interface/server/routes.py +6 -0
  44. classiq/model_expansions/atomic_expression_functions_defs.py +82 -23
  45. classiq/model_expansions/capturing/captured_vars.py +2 -0
  46. classiq/model_expansions/closure.py +18 -0
  47. classiq/model_expansions/evaluators/argument_types.py +6 -5
  48. classiq/model_expansions/evaluators/classical_type_inference.py +17 -6
  49. classiq/model_expansions/evaluators/parameter_types.py +26 -13
  50. classiq/model_expansions/evaluators/type_type_match.py +2 -2
  51. classiq/model_expansions/expression_evaluator.py +1 -1
  52. classiq/model_expansions/generative_functions.py +66 -33
  53. classiq/model_expansions/interpreters/base_interpreter.py +27 -19
  54. classiq/model_expansions/interpreters/frontend_generative_interpreter.py +26 -0
  55. classiq/model_expansions/interpreters/generative_interpreter.py +25 -1
  56. classiq/model_expansions/quantum_operations/allocate.py +27 -11
  57. classiq/model_expansions/quantum_operations/assignment_result_processor.py +220 -19
  58. classiq/model_expansions/quantum_operations/bind.py +54 -30
  59. classiq/model_expansions/quantum_operations/block_evaluator.py +42 -0
  60. classiq/model_expansions/quantum_operations/call_emitter.py +14 -12
  61. classiq/model_expansions/quantum_operations/composite_emitter.py +1 -1
  62. classiq/model_expansions/quantum_operations/declarative_call_emitter.py +23 -9
  63. classiq/model_expansions/quantum_operations/emitter.py +21 -8
  64. classiq/model_expansions/quantum_operations/expression_evaluator.py +1 -0
  65. classiq/model_expansions/quantum_operations/handle_evaluator.py +1 -0
  66. classiq/model_expansions/quantum_operations/quantum_function_call.py +4 -3
  67. classiq/model_expansions/scope.py +10 -7
  68. classiq/model_expansions/sympy_conversion/arithmetics.py +18 -0
  69. classiq/model_expansions/sympy_conversion/expression_to_sympy.py +2 -0
  70. classiq/model_expansions/sympy_conversion/sympy_to_python.py +10 -1
  71. classiq/model_expansions/transformers/model_renamer.py +48 -8
  72. classiq/model_expansions/utils/handles_collector.py +1 -1
  73. classiq/model_expansions/visitors/symbolic_param_inference.py +197 -0
  74. classiq/model_expansions/visitors/variable_references.py +45 -9
  75. classiq/qmod/builtins/functions/allocation.py +2 -2
  76. classiq/qmod/builtins/functions/arithmetic.py +14 -12
  77. classiq/qmod/builtins/functions/standard_gates.py +23 -23
  78. classiq/qmod/declaration_inferrer.py +19 -7
  79. classiq/qmod/generative.py +9 -1
  80. classiq/qmod/native/expression_to_qmod.py +4 -0
  81. classiq/qmod/native/pretty_printer.py +8 -3
  82. classiq/qmod/pretty_print/pretty_printer.py +1 -1
  83. classiq/qmod/python_classical_type.py +4 -5
  84. classiq/qmod/qmod_constant.py +15 -7
  85. classiq/qmod/qmod_variable.py +30 -2
  86. classiq/qmod/quantum_function.py +19 -6
  87. classiq/qmod/semantics/lambdas.py +6 -2
  88. classiq/qmod/semantics/validation/main_validation.py +17 -4
  89. classiq/qmod/symbolic.py +8 -19
  90. classiq/qmod/symbolic_expr.py +34 -2
  91. classiq/qmod/write_qmod.py +5 -1
  92. classiq/synthesis.py +17 -31
  93. classiq/visualization.py +35 -0
  94. {classiq-0.74.0.dist-info → classiq-0.76.0.dist-info}/METADATA +1 -1
  95. {classiq-0.74.0.dist-info → classiq-0.76.0.dist-info}/RECORD +96 -91
  96. {classiq-0.74.0.dist-info → classiq-0.76.0.dist-info}/WHEEL +1 -1
@@ -1,6 +1,9 @@
1
1
  from typing import Any, Union
2
2
 
3
3
  from classiq.interface.exceptions import ClassiqExpansionError
4
+ from classiq.interface.generator.expressions.proxies.classical.any_classical_value import (
5
+ AnyClassicalValue,
6
+ )
4
7
  from classiq.interface.generator.expressions.proxies.classical.classical_array_proxy import (
5
8
  ClassicalArrayProxy,
6
9
  )
@@ -17,6 +20,7 @@ from classiq.interface.generator.functions.classical_type import (
17
20
  )
18
21
  from classiq.interface.generator.functions.type_name import TypeName
19
22
  from classiq.interface.generator.types.struct_declaration import StructDeclaration
23
+ from classiq.interface.helpers.backward_compatibility import zip_strict
20
24
 
21
25
 
22
26
  def infer_classical_type(val: Any, classical_type: ClassicalType) -> ClassicalType:
@@ -33,7 +37,7 @@ def _infer_classical_struct_type(val: Any, classical_type: TypeName) -> Classica
33
37
  decl = val.struct_declaration
34
38
  new_fields = {
35
39
  field_name: infer_classical_type(field_val, field_type)
36
- for (field_name, field_val), field_type in zip(
40
+ for (field_name, field_val), field_type in zip_strict(
37
41
  val.fields.items(),
38
42
  decl.variables.values(),
39
43
  strict=True,
@@ -49,13 +53,20 @@ def _infer_classical_struct_type(val: Any, classical_type: TypeName) -> Classica
49
53
  def _infer_classical_array_type(
50
54
  val: Any, classical_type: Union[ClassicalArray, ClassicalList]
51
55
  ) -> ClassicalType:
52
- if isinstance(val, list):
53
- val_length = len(val)
54
- elif isinstance(val, ClassicalArrayProxy):
56
+ if isinstance(val, ClassicalArrayProxy):
55
57
  val_length = val.length
58
+ elif isinstance(val, list):
59
+ val_length = len(val)
60
+ elif isinstance(val, AnyClassicalValue):
61
+ return classical_type
56
62
  else:
57
63
  raise ClassiqExpansionError(f"Array expected, got {str(val)!r}")
58
- if isinstance(classical_type, ClassicalArray) and val_length != classical_type.size:
64
+ if (
65
+ isinstance(classical_type, ClassicalArray)
66
+ and isinstance(val_length, int)
67
+ and isinstance(classical_type.size, int)
68
+ and val_length != classical_type.size
69
+ ):
59
70
  raise ClassiqExpansionError(
60
71
  f"Type mismatch: Argument has {val_length} items but "
61
72
  f"{classical_type.size} expected"
@@ -63,7 +74,7 @@ def _infer_classical_array_type(
63
74
  return ClassicalArray(
64
75
  element_type=(
65
76
  infer_classical_type(val[0], classical_type.element_type)
66
- if val_length > 0
77
+ if not isinstance(val_length, int) or val_length > 0
67
78
  else classical_type.element_type
68
79
  ),
69
80
  size=val_length,
@@ -1,10 +1,15 @@
1
- from typing import TypeVar, Union
1
+ from typing import Optional, TypeVar, Union
2
+
3
+ import sympy
2
4
 
3
5
  from classiq.interface.exceptions import (
4
6
  ClassiqExpansionError,
5
7
  ClassiqInternalExpansionError,
6
8
  )
7
9
  from classiq.interface.generator.expressions.expression import Expression
10
+ from classiq.interface.generator.expressions.proxies.classical.any_classical_value import (
11
+ AnyClassicalValue,
12
+ )
8
13
  from classiq.interface.generator.functions.concrete_types import ConcreteQuantumType
9
14
  from classiq.interface.generator.functions.port_declaration import (
10
15
  PortDeclarationDirection,
@@ -178,7 +183,8 @@ def _evaluate_qarray_in_quantum_symbol(
178
183
  "length",
179
184
  param_name,
180
185
  )
181
- set_length(type_to_update, new_length)
186
+ if new_length is not None:
187
+ set_length(type_to_update, new_length)
182
188
  return type_to_update
183
189
 
184
190
 
@@ -194,7 +200,8 @@ def _evaluate_qnum_in_quantum_symbol(
194
200
  "sign",
195
201
  param_name,
196
202
  )
197
- type_to_update.is_signed = Expression(expr=str(new_is_sign))
203
+ if new_is_sign is not None:
204
+ type_to_update.is_signed = Expression(expr=str(new_is_sign))
198
205
  if type_to_update.fraction_digits is not None:
199
206
  new_fraction_digits = _eval_expr(
200
207
  type_to_update.fraction_digits,
@@ -204,7 +211,8 @@ def _evaluate_qnum_in_quantum_symbol(
204
211
  "fraction digits",
205
212
  param_name,
206
213
  )
207
- type_to_update.fraction_digits = Expression(expr=str(new_fraction_digits))
214
+ if new_fraction_digits is not None:
215
+ type_to_update.fraction_digits = Expression(expr=str(new_fraction_digits))
208
216
  if type_to_update.size is not None:
209
217
  new_size = _eval_expr(
210
218
  type_to_update.size,
@@ -214,7 +222,8 @@ def _evaluate_qnum_in_quantum_symbol(
214
222
  "size",
215
223
  param_name,
216
224
  )
217
- set_size(type_to_update, new_size, param_name)
225
+ if new_size is not None:
226
+ set_size(type_to_update, new_size, param_name)
218
227
  return type_to_update
219
228
 
220
229
 
@@ -228,17 +237,21 @@ def _eval_expr(
228
237
  type_name: str,
229
238
  attr_name: str,
230
239
  param_name: str,
231
- ) -> _EXPR_TYPE:
240
+ ) -> Optional[_EXPR_TYPE]:
232
241
  val = evaluate_classical_expression(expression, scope).value
233
242
  if expected_type is int and isinstance(val, float):
234
243
  val = int(val)
235
- if not isinstance(val, expected_type):
236
- raise ClassiqExpansionError(
237
- f"When inferring the type of parameter {param_name!r}: "
238
- f"{type_name} {attr_name} must be {expected_type.__name__}, got "
239
- f"{str(val)!r}"
240
- )
241
- return val
244
+ if isinstance(val, expected_type):
245
+ return val
246
+ if isinstance(val, AnyClassicalValue) or (
247
+ isinstance(val, sympy.Basic) and len(val.free_symbols) > 0
248
+ ):
249
+ return None
250
+ raise ClassiqExpansionError(
251
+ f"When inferring the type of parameter {param_name!r}: "
252
+ f"{type_name} {attr_name} must be {expected_type.__name__}, got "
253
+ f"{str(val)!r}"
254
+ )
242
255
 
243
256
 
244
257
  def _evaluate_qstruct_in_quantum_symbol(
@@ -85,7 +85,7 @@ def _check_classical_type_match(
85
85
  ) -> None:
86
86
  if (
87
87
  not isinstance(op_param, AnonClassicalParameterDeclaration)
88
- or decl_param.classical_type.set_generative()
89
- != op_param.classical_type.set_generative()
88
+ or decl_param.classical_type.clear_flags()
89
+ != op_param.classical_type.clear_flags()
90
90
  ):
91
91
  raise ClassiqExpansionError(error_message)
@@ -73,7 +73,7 @@ def evaluate(
73
73
  try:
74
74
  sympify_result = sympify(sympy_expr, locals=model_locals)
75
75
  except (TypeError, IndexError) as e:
76
- raise ClassiqExpansionError(str(e)) from None
76
+ raise ClassiqExpansionError(str(e)) from e
77
77
  except AttributeError as e:
78
78
  if isinstance(e.obj, EnumMeta):
79
79
  raise ClassiqExpansionError(
@@ -16,7 +16,7 @@ from classiq.interface.generator.expressions.proxies.classical.utils import (
16
16
  get_proxy_type,
17
17
  )
18
18
  from classiq.interface.generator.functions.type_name import Struct
19
- from classiq.interface.helpers.datastructures import get_sdk_compatible_python_object
19
+ from classiq.interface.helpers.datastructures import LenList
20
20
  from classiq.interface.helpers.pydantic_model_helpers import nameables_to_dict
21
21
  from classiq.interface.model.native_function_definition import NativeFunctionDefinition
22
22
  from classiq.interface.model.port_declaration import PortDeclaration
@@ -31,16 +31,18 @@ from classiq.model_expansions.closure import (
31
31
  FunctionClosure,
32
32
  GenerativeClosure,
33
33
  )
34
- from classiq.model_expansions.scope import Evaluated, QuantumSymbol
34
+ from classiq.model_expansions.scope import Evaluated
35
35
  from classiq.qmod.generative import generative_mode_context, set_frontend_interpreter
36
36
  from classiq.qmod.model_state_container import QMODULE
37
37
  from classiq.qmod.qmod_parameter import CParamStruct, create_param
38
- from classiq.qmod.qmod_variable import _create_qvar_for_qtype
38
+ from classiq.qmod.qmod_variable import QScalar, _create_qvar_for_qtype
39
39
  from classiq.qmod.quantum_expandable import (
40
40
  QTerminalCallable,
41
41
  )
42
42
  from classiq.qmod.quantum_function import QFunc
43
43
  from classiq.qmod.semantics.annotation.call_annotation import resolve_function_calls
44
+ from classiq.qmod.symbolic_expr import SymbolicExpr, SymbolicSubscriptAndField
45
+ from classiq.qmod.utilities import qmod_val_to_expr_str
44
46
 
45
47
  if TYPE_CHECKING:
46
48
  from classiq.model_expansions.interpreters.generative_interpreter import (
@@ -65,33 +67,61 @@ def _unwrap_traceback_frame(e: Exception) -> Exception:
65
67
  return e.with_traceback(back_tb)
66
68
 
67
69
 
68
- def translate_ast_arg_to_python_qmod(param: PositionalArg, evaluated: Evaluated) -> Any:
69
- if isinstance(param, PortDeclaration):
70
- quantum_symbol = evaluated.as_type(QuantumSymbol)
71
- return _create_qvar_for_qtype(
72
- quantum_symbol.quantum_type, quantum_symbol.handle
70
+ class SymbolicList(LenList):
71
+ def __getitem__(self, index: Any) -> Any:
72
+ if isinstance(index, (QScalar, SymbolicExpr)) or (
73
+ isinstance(index, slice)
74
+ and any(
75
+ isinstance(slice_part, (QScalar, SymbolicExpr))
76
+ for slice_part in (index.start, index.stop, index.step)
77
+ )
78
+ ):
79
+ return SymbolicSubscriptAndField(
80
+ qmod_val_to_expr_str(self), is_quantum=False
81
+ )[index]
82
+ try:
83
+ return super().__getitem__(index)
84
+ except (IndexError, TypeError) as e:
85
+ raise _unwrap_traceback_frame(e) from None
86
+
87
+
88
+ def translate_classical_ast_arg_to_python_qmod(value: Any) -> Any:
89
+ if isinstance(value, QmodStructInstance):
90
+ return QmodStructInstance(
91
+ value.struct_declaration,
92
+ {
93
+ field_name: translate_classical_ast_arg_to_python_qmod(field_value)
94
+ for field_name, field_value in value.fields.items()
95
+ },
96
+ )
97
+ if isinstance(value, list):
98
+ return SymbolicList(
99
+ [translate_classical_ast_arg_to_python_qmod(item) for item in value]
73
100
  )
101
+ if isinstance(value, ClassicalProxy):
102
+ return create_param(str(value.handle), get_proxy_type(value), QMODULE)
103
+
104
+ return value
105
+
106
+
107
+ def translate_ast_arg_to_python_qmod(param: PositionalArg, value: Any) -> Any:
108
+ if isinstance(param, PortDeclaration):
109
+ return _create_qvar_for_qtype(value.quantum_type, value.handle)
74
110
  if isinstance(param, QuantumOperandDeclaration):
75
111
  if not param.is_list or not param.is_generative:
76
112
  return QTerminalCallable(param)
77
113
  inner_decl = param.model_copy(update={"is_list": False})
78
- func_list: list[FunctionClosure] = evaluated.as_type(list)
79
- return [
80
- QTerminalCallable(inner_decl, index_=idx) for idx in range(len(func_list))
81
- ]
82
- classical_value = evaluated.value
83
- if isinstance(classical_value, QmodStructInstance):
114
+ return [QTerminalCallable(inner_decl, index_=idx) for idx in range(len(value))]
115
+ if (
116
+ isinstance(value, QmodStructInstance)
117
+ and param.classical_type.is_purely_declarative
118
+ ):
119
+ classical_type = Struct(name=value.struct_declaration.name)
120
+ classical_type.set_classical_struct_decl(value.struct_declaration)
84
121
  return CParamStruct(
85
- expr=param.name,
86
- struct_type=Struct(name=classical_value.struct_declaration.name),
87
- qmodule=QMODULE,
88
- )
89
- if isinstance(classical_value, ClassicalProxy):
90
- return create_param(
91
- str(classical_value.handle), get_proxy_type(classical_value), QMODULE
122
+ expr=param.name, struct_type=classical_type, qmodule=QMODULE
92
123
  )
93
-
94
- return get_sdk_compatible_python_object(classical_value)
124
+ return translate_classical_ast_arg_to_python_qmod(value)
95
125
 
96
126
 
97
127
  class _InterpreterExpandable(QFunc):
@@ -111,15 +141,18 @@ class _InterpreterExpandable(QFunc):
111
141
  for name, func in self._qmodule.native_defs.items()
112
142
  if name not in self._interpreter._top_level_scope
113
143
  }
144
+ generative_functions = self._qmodule.generative_functions
114
145
  self._interpreter.update_declarative_functions(
115
146
  declarative_functions, self._qmodule
116
147
  )
117
- self._interpreter.update_generative_functions(
118
- self._qmodule.generative_functions
119
- )
148
+ self._interpreter.update_generative_functions(generative_functions)
120
149
  func_decls = self._get_function_declarations()
121
150
  for dec_func in declarative_functions.values():
122
151
  resolve_function_calls(dec_func, func_decls)
152
+ self._interpreter.infer_symbolic_parameters(
153
+ list(declarative_functions.values()),
154
+ [func.func_decl for func in generative_functions.values()],
155
+ )
123
156
  resolve_function_calls(dummy_function, func_decls)
124
157
  stmt = dummy_function.body[-1]
125
158
  with generative_mode_context(False):
@@ -134,14 +167,14 @@ class _InterpreterExpandable(QFunc):
134
167
  name=name,
135
168
  positional_arg_declarations=value.positional_arg_declarations,
136
169
  )
137
- elif (
138
- isinstance(value, list)
139
- and len(value) > 0
140
- and isinstance(value[0], FunctionClosure)
141
- ):
170
+ continue
171
+ op_param = self._interpreter._builder.current_function.parameters_dict.get(
172
+ name
173
+ )
174
+ if isinstance(op_param, QuantumOperandDeclaration):
142
175
  scope_func_decls[name] = QuantumFunctionDeclaration(
143
176
  name=name,
144
- positional_arg_declarations=value[0].positional_arg_declarations,
177
+ positional_arg_declarations=op_param.positional_arg_declarations,
145
178
  )
146
179
  return (
147
180
  nameables_to_dict(self._interpreter._get_function_declarations())
@@ -155,7 +188,7 @@ def emit_generative_statements(
155
188
  args: list[Evaluated],
156
189
  ) -> None:
157
190
  python_qmod_args = [
158
- translate_ast_arg_to_python_qmod(param, arg)
191
+ translate_ast_arg_to_python_qmod(param, arg.value)
159
192
  for param, arg in zip(operation.positional_arg_declarations, args)
160
193
  ]
161
194
  with _InterpreterExpandable(interpreter):
@@ -6,17 +6,20 @@ from contextlib import nullcontext
6
6
  from functools import singledispatchmethod
7
7
  from typing import Any, cast
8
8
 
9
- import sympy
10
9
  from pydantic import ValidationError
11
10
 
12
- from classiq.interface.debug_info.debug_info import FunctionDebugInfo
11
+ from classiq.interface.debug_info.debug_info import (
12
+ new_function_debug_info_by_node,
13
+ )
13
14
  from classiq.interface.exceptions import (
14
15
  ClassiqError,
15
16
  ClassiqExpansionError,
16
17
  ClassiqInternalExpansionError,
17
18
  )
19
+ from classiq.interface.generator.expressions.atomic_expression_functions import (
20
+ CLASSICAL_ATTRIBUTES,
21
+ )
18
22
  from classiq.interface.generator.expressions.expression import Expression
19
- from classiq.interface.generator.generated_circuit_data import OperationLevel
20
23
  from classiq.interface.generator.types.compilation_metadata import CompilationMetadata
21
24
  from classiq.interface.model.handle_binding import (
22
25
  FieldHandleBinding,
@@ -56,11 +59,13 @@ from classiq.model_expansions.scope_initialization import (
56
59
  )
57
60
  from classiq.model_expansions.utils.counted_name_allocator import CountedNameAllocator
58
61
  from classiq.model_expansions.visitors.variable_references import VarRefCollector
62
+ from classiq.qmod.builtins.constants import __all__ as builtin_constants
59
63
  from classiq.qmod.builtins.enums import BUILTIN_ENUM_DECLARATIONS
60
64
  from classiq.qmod.builtins.structs import BUILTIN_STRUCT_DECLARATIONS
61
65
  from classiq.qmod.model_state_container import QMODULE
62
66
  from classiq.qmod.semantics.error_manager import ErrorManager
63
67
  from classiq.qmod.semantics.validation.model_validation import validate_model
68
+ from classiq.qmod.utilities import qmod_val_to_expr_str
64
69
 
65
70
 
66
71
  class BaseInterpreter:
@@ -125,7 +130,11 @@ class BaseInterpreter:
125
130
  classical_execution_code=self._model.classical_execution_code,
126
131
  execution_preferences=self._model.execution_preferences,
127
132
  functions=list(self._expanded_functions.values()),
128
- constants=self._model.constants,
133
+ constants=[
134
+ const
135
+ for name, const in QMODULE.constants.items()
136
+ if name not in builtin_constants
137
+ ],
129
138
  enums=[
130
139
  enum_decl
131
140
  for name, enum_decl in QMODULE.enum_decls.items()
@@ -156,14 +165,17 @@ class BaseInterpreter:
156
165
  @evaluate.register
157
166
  def evaluate_classical_expression(self, expression: Expression) -> Evaluated:
158
167
  expr = evaluate_classical_expression(expression, self._builder.current_scope)
159
- if not isinstance(expr.value, sympy.Basic):
160
- return expr
161
168
  vrc = VarRefCollector(ignore_duplicated_handles=True, unevaluated=True)
162
- vrc.visit(ast.parse(str(expr.value)))
169
+ vrc.visit(ast.parse(qmod_val_to_expr_str(expr.value)))
163
170
  for handle in vrc.var_handles:
164
171
  if handle.name in self._builder.current_scope and isinstance(
165
- self._builder.current_scope[handle.name], QuantumSymbol
172
+ self._builder.current_scope[handle.name].value, QuantumSymbol
166
173
  ):
174
+ if (
175
+ isinstance(handle, FieldHandleBinding)
176
+ and handle.field in CLASSICAL_ATTRIBUTES
177
+ ):
178
+ handle = handle.base_handle
167
179
  self.evaluate(handle)
168
180
  return expr
169
181
 
@@ -237,20 +249,16 @@ class BaseInterpreter:
237
249
  if source_ref is not None
238
250
  else nullcontext()
239
251
  )
240
- if statement.uuid not in self._model.debug_info:
241
- self._model.debug_info[statement.uuid] = FunctionDebugInfo(
242
- name="",
243
- parameters={},
244
- level=OperationLevel.QMOD_STATEMENT,
245
- statement_type=None,
246
- is_allocate_or_free=False,
247
- is_inverse=False,
248
- port_to_passed_variable_map={},
249
- node=statement._as_back_ref(),
250
- )
252
+ self.add_to_debug_info(statement)
251
253
  with error_context, self._builder.source_ref_context(source_ref):
252
254
  self.emit(statement)
253
255
 
256
+ def add_to_debug_info(self, statement: QuantumStatement) -> None:
257
+ if statement.uuid not in self._model.debug_info:
258
+ self._model.debug_info[statement.uuid] = new_function_debug_info_by_node(
259
+ statement # type: ignore[arg-type]
260
+ )
261
+
254
262
  def _expand_operation(self, operation: Closure) -> OperationContext:
255
263
  with self._builder.operation_context(operation) as context:
256
264
  if isinstance(operation, FunctionClosure) and (
@@ -4,21 +4,47 @@ import os
4
4
  from pydantic import ValidationError
5
5
 
6
6
  from classiq.interface.exceptions import ClassiqError
7
+ from classiq.interface.model.allocate import Allocate
8
+ from classiq.interface.model.bind_operation import BindOperation
9
+ from classiq.interface.model.native_function_definition import NativeFunctionDefinition
7
10
  from classiq.interface.model.quantum_function_call import QuantumFunctionCall
11
+ from classiq.interface.model.quantum_function_declaration import (
12
+ NamedParamsQuantumFunctionDeclaration,
13
+ )
8
14
  from classiq.interface.source_reference import SourceReference
9
15
 
10
16
  from classiq.model_expansions.closure import FunctionClosure, GenerativeFunctionClosure
11
17
  from classiq.model_expansions.interpreters.generative_interpreter import (
12
18
  GenerativeInterpreter,
13
19
  )
20
+ from classiq.model_expansions.quantum_operations import BindEmitter
21
+ from classiq.model_expansions.quantum_operations.allocate import AllocateEmitter
14
22
  from classiq.model_expansions.quantum_operations.quantum_function_call import (
15
23
  DeclarativeQuantumFunctionCallEmitter,
16
24
  )
17
25
  from classiq.model_expansions.scope import Scope
26
+ from classiq.model_expansions.visitors.symbolic_param_inference import (
27
+ SymbolicParamInference,
28
+ )
18
29
  from classiq.qmod.model_state_container import QMODULE
19
30
 
20
31
 
21
32
  class FrontendGenerativeInterpreter(GenerativeInterpreter):
33
+ def infer_symbolic_parameters(
34
+ self,
35
+ functions: list[NativeFunctionDefinition],
36
+ additional_signatures: (
37
+ list[NamedParamsQuantumFunctionDeclaration] | None
38
+ ) = None,
39
+ ) -> None:
40
+ SymbolicParamInference(functions, additional_signatures).infer()
41
+
42
+ def emit_allocate(self, allocate: Allocate) -> None:
43
+ AllocateEmitter(self, allow_symbolic_size=True).emit(allocate)
44
+
45
+ def emit_bind(self, bind: BindOperation) -> None:
46
+ BindEmitter(self, allow_symbolic_size=True).emit(bind)
47
+
22
48
  def emit_quantum_function_call(self, call: QuantumFunctionCall) -> None:
23
49
  DeclarativeQuantumFunctionCallEmitter(self).emit(call)
24
50
 
@@ -61,7 +61,11 @@ from classiq.model_expansions.quantum_operations.allocate import AllocateEmitter
61
61
  from classiq.model_expansions.quantum_operations.assignment_result_processor import (
62
62
  AssignmentResultProcessor,
63
63
  )
64
- from classiq.model_expansions.quantum_operations.block_evaluator import BlockEvaluator
64
+ from classiq.model_expansions.quantum_operations.block_evaluator import (
65
+ BlockEvaluator,
66
+ IfElimination,
67
+ RepeatElimination,
68
+ )
65
69
  from classiq.model_expansions.quantum_operations.composite_emitter import (
66
70
  CompositeEmitter,
67
71
  )
@@ -93,6 +97,18 @@ class GenerativeInterpreter(BaseInterpreter):
93
97
  add_generative_functions_to_scope(
94
98
  generative_functions, self._top_level_scope, override_atomic=True
95
99
  )
100
+ self.infer_symbolic_parameters(
101
+ model.functions, [gen_func.func_decl for gen_func in generative_functions]
102
+ )
103
+
104
+ def infer_symbolic_parameters(
105
+ self,
106
+ functions: list[NativeFunctionDefinition],
107
+ additional_signatures: (
108
+ list[NamedParamsQuantumFunctionDeclaration] | None
109
+ ) = None,
110
+ ) -> None:
111
+ pass
96
112
 
97
113
  def evaluate_lambda(self, function: QuantumLambdaFunction) -> Evaluated:
98
114
  func_decl = NamedParamsQuantumFunctionDeclaration(
@@ -140,10 +156,16 @@ class GenerativeInterpreter(BaseInterpreter):
140
156
  QuantumFunctionCallEmitter(self).emit(call)
141
157
 
142
158
  @emit.register
159
+ def _emit_allocate(self, allocate: Allocate) -> None:
160
+ return self.emit_allocate(allocate)
161
+
143
162
  def emit_allocate(self, allocate: Allocate) -> None:
144
163
  AllocateEmitter(self).emit(allocate)
145
164
 
146
165
  @emit.register
166
+ def _emit_bind(self, bind: BindOperation) -> None:
167
+ self.emit_bind(bind)
168
+
147
169
  def emit_bind(self, bind: BindOperation) -> None:
148
170
  BindEmitter(self).emit(bind)
149
171
 
@@ -198,6 +220,7 @@ class GenerativeInterpreter(BaseInterpreter):
198
220
  self,
199
221
  [
200
222
  ExpressionEvaluator(self, "condition"),
223
+ IfElimination(self),
201
224
  BlockEvaluator(
202
225
  self,
203
226
  CLASSICAL_IF_OPERATOR_NAME,
@@ -231,6 +254,7 @@ class GenerativeInterpreter(BaseInterpreter):
231
254
  self,
232
255
  [
233
256
  ExpressionEvaluator(self, "count"),
257
+ RepeatElimination(self),
234
258
  RepeatBlockEvaluator(self, REPEAT_OPERATOR_NAME, "body"),
235
259
  ],
236
260
  ).emit(repeat)
@@ -1,7 +1,13 @@
1
+ from typing import TYPE_CHECKING
2
+
3
+ import sympy
4
+
1
5
  from classiq.interface.debug_info.debug_info import FunctionDebugInfo
2
6
  from classiq.interface.exceptions import ClassiqValueError
3
7
  from classiq.interface.generator.expressions.expression import Expression
4
- from classiq.interface.generator.generated_circuit_data import OperationLevel
8
+ from classiq.interface.generator.expressions.proxies.classical.any_classical_value import (
9
+ AnyClassicalValue,
10
+ )
5
11
  from classiq.interface.model.allocate import Allocate
6
12
  from classiq.interface.model.handle_binding import NestedHandleBinding
7
13
  from classiq.interface.model.quantum_type import QuantumBitvector
@@ -10,8 +16,17 @@ from classiq.model_expansions.evaluators.quantum_type_utils import copy_type_inf
10
16
  from classiq.model_expansions.quantum_operations.emitter import Emitter
11
17
  from classiq.model_expansions.scope import QuantumSymbol
12
18
 
19
+ if TYPE_CHECKING:
20
+ from classiq.model_expansions.interpreters.base_interpreter import BaseInterpreter
21
+
13
22
 
14
23
  class AllocateEmitter(Emitter[Allocate]):
24
+ def __init__(
25
+ self, interpreter: "BaseInterpreter", allow_symbolic_size: bool = False
26
+ ) -> None:
27
+ super().__init__(interpreter)
28
+ self._allow_symbolic_size = allow_symbolic_size
29
+
15
30
  def emit(self, allocate: Allocate, /) -> bool:
16
31
  target: QuantumSymbol = self._interpreter.evaluate(allocate.target).as_type(
17
32
  QuantumSymbol
@@ -22,10 +37,10 @@ class AllocateEmitter(Emitter[Allocate]):
22
37
  f"Cannot allocate partial quantum variable {str(target.handle)!r}"
23
38
  )
24
39
 
25
- size = self._get_var_size(target, allocate.size)
40
+ size_expr = self._get_var_size(target, allocate.size)
26
41
  allocate = allocate.model_copy(
27
42
  update=dict(
28
- size=Expression(expr=str(size)),
43
+ size=Expression(expr=size_expr),
29
44
  target=target.handle,
30
45
  back_ref=allocate.uuid,
31
46
  )
@@ -34,27 +49,31 @@ class AllocateEmitter(Emitter[Allocate]):
34
49
  self.emit_statement(allocate)
35
50
  return True
36
51
 
37
- def _get_var_size(self, target: QuantumSymbol, size: Expression | None) -> int:
52
+ def _get_var_size(self, target: QuantumSymbol, size: Expression | None) -> str:
38
53
  if size is None:
39
54
  if not target.quantum_type.is_evaluated:
40
55
  raise ClassiqValueError(
41
56
  f"Could not infer the size of variable {str(target.handle)!r}"
42
57
  )
43
- return target.quantum_type.size_in_bits
58
+ return str(target.quantum_type.size_in_bits)
44
59
 
45
60
  size_value = self._interpreter.evaluate(size).value
61
+ if self._allow_symbolic_size and isinstance(
62
+ size_value, (sympy.Basic, AnyClassicalValue)
63
+ ):
64
+ return str(size_value)
46
65
  if not isinstance(size_value, (int, float)):
47
66
  raise ClassiqValueError(
48
67
  f"The number of allocated qubits must be an integer. Got "
49
68
  f"{str(size_value)!r}"
50
69
  )
51
- size_value = int(size_value)
70
+ size_expr = str(size_value)
52
71
  copy_type_information(
53
- QuantumBitvector(length=Expression(expr=str(size_value))),
72
+ QuantumBitvector(length=Expression(expr=size_expr)),
54
73
  target.quantum_type,
55
74
  str(target.handle),
56
75
  )
57
- return size_value
76
+ return size_expr
58
77
 
59
78
  def _register_debug_info(self, allocate: Allocate) -> None:
60
79
  if (
@@ -67,9 +86,6 @@ class AllocateEmitter(Emitter[Allocate]):
67
86
  parameters["num_qubits"] = allocate.size.expr
68
87
  self._debug_info[allocate.uuid] = FunctionDebugInfo(
69
88
  name="allocate",
70
- parameters=parameters,
71
- level=OperationLevel.QMOD_STATEMENT,
72
- is_allocate_or_free=True,
73
89
  port_to_passed_variable_map={"ARG": str(allocate.target)},
74
90
  node=allocate._as_back_ref(),
75
91
  )