classiq 0.63.1__py3-none-any.whl → 0.65.1__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 (77) hide show
  1. classiq/_internals/api_wrapper.py +30 -0
  2. classiq/analyzer/url_utils.py +2 -3
  3. classiq/applications/chemistry/chemistry_model_constructor.py +8 -9
  4. classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +4 -6
  5. classiq/applications/combinatorial_optimization/combinatorial_problem.py +2 -5
  6. classiq/applications/finance/finance_model_constructor.py +7 -12
  7. classiq/applications/grover/grover_model_constructor.py +4 -6
  8. classiq/applications/qsvm/qsvm_model_constructor.py +6 -4
  9. classiq/execution/execution_session.py +14 -13
  10. classiq/interface/_version.py +1 -1
  11. classiq/interface/analyzer/result.py +1 -2
  12. classiq/interface/backend/backend_preferences.py +1 -9
  13. classiq/interface/executor/result.py +6 -3
  14. classiq/interface/generator/expressions/qmod_qarray_proxy.py +11 -13
  15. classiq/interface/generator/functions/type_name.py +6 -0
  16. classiq/interface/helpers/datastructures.py +26 -0
  17. classiq/interface/interface_version.py +1 -1
  18. classiq/interface/model/allocate.py +16 -0
  19. classiq/interface/model/handle_binding.py +11 -3
  20. classiq/interface/model/quantum_type.py +26 -0
  21. classiq/interface/model/statement_block.py +2 -0
  22. classiq/interface/server/routes.py +5 -0
  23. classiq/model_expansions/atomic_expression_functions_defs.py +6 -6
  24. classiq/model_expansions/capturing/captured_vars.py +27 -10
  25. classiq/model_expansions/evaluators/arg_type_match.py +4 -7
  26. classiq/model_expansions/evaluators/quantum_type_utils.py +25 -8
  27. classiq/model_expansions/expression_evaluator.py +8 -2
  28. classiq/model_expansions/function_builder.py +35 -11
  29. classiq/model_expansions/generative_functions.py +6 -4
  30. classiq/model_expansions/interpreters/__init__.py +0 -0
  31. classiq/model_expansions/interpreters/base_interpreter.py +263 -0
  32. classiq/model_expansions/interpreters/frontend_generative_interpreter.py +28 -0
  33. classiq/model_expansions/interpreters/generative_interpreter.py +249 -0
  34. classiq/model_expansions/model_tables.py +1 -92
  35. classiq/model_expansions/quantum_operations/__init__.py +0 -10
  36. classiq/model_expansions/quantum_operations/call_emitter.py +45 -93
  37. classiq/model_expansions/quantum_operations/declarative_call_emitter.py +87 -0
  38. classiq/model_expansions/quantum_operations/emitter.py +7 -2
  39. classiq/model_expansions/quantum_operations/quantum_function_call.py +11 -2
  40. classiq/model_expansions/quantum_operations/shallow_emitter.py +22 -3
  41. classiq/model_expansions/scope.py +15 -15
  42. classiq/model_expansions/scope_initialization.py +11 -5
  43. classiq/open_library/functions/discrete_sine_cosine_transform.py +8 -2
  44. classiq/open_library/functions/grover.py +1 -1
  45. classiq/open_library/functions/modular_exponentiation.py +8 -2
  46. classiq/open_library/functions/state_preparation.py +23 -13
  47. classiq/open_library/functions/swap_test.py +1 -2
  48. classiq/open_library/functions/variational.py +1 -2
  49. classiq/qmod/builtins/__init__.py +1 -1
  50. classiq/qmod/builtins/operations.py +51 -0
  51. classiq/qmod/declaration_inferrer.py +0 -3
  52. classiq/qmod/generative.py +4 -4
  53. classiq/qmod/native/pretty_printer.py +9 -1
  54. classiq/qmod/pretty_print/pretty_printer.py +12 -1
  55. classiq/qmod/qfunc.py +4 -2
  56. classiq/qmod/qmod_variable.py +55 -48
  57. classiq/qmod/quantum_function.py +7 -5
  58. classiq/qmod/semantics/annotation/__init__.py +0 -0
  59. classiq/qmod/semantics/annotation/call_annotation.py +92 -0
  60. classiq/qmod/semantics/lambdas.py +25 -0
  61. classiq/qmod/semantics/static_semantics_visitor.py +8 -46
  62. classiq/qmod/utilities.py +23 -1
  63. {classiq-0.63.1.dist-info → classiq-0.65.1.dist-info}/METADATA +1 -1
  64. {classiq-0.63.1.dist-info → classiq-0.65.1.dist-info}/RECORD +66 -67
  65. classiq/interface/helpers/dotdict.py +0 -18
  66. classiq/model_expansions/interpreter.py +0 -475
  67. classiq/model_expansions/quantum_operations/control.py +0 -290
  68. classiq/model_expansions/quantum_operations/expression_operation.py +0 -103
  69. classiq/model_expansions/quantum_operations/inplace_binary_operation.py +0 -535
  70. classiq/model_expansions/quantum_operations/invert.py +0 -36
  71. classiq/model_expansions/quantum_operations/phase.py +0 -187
  72. classiq/model_expansions/quantum_operations/power.py +0 -71
  73. classiq/model_expansions/quantum_operations/quantum_assignment_operation.py +0 -230
  74. classiq/model_expansions/quantum_operations/within_apply.py +0 -25
  75. classiq/qmod/semantics/annotation.py +0 -36
  76. /classiq/qmod/semantics/{qstruct_annotator.py → annotation/qstruct_annotator.py} +0 -0
  77. {classiq-0.63.1.dist-info → classiq-0.65.1.dist-info}/WHEEL +0 -0
@@ -34,6 +34,10 @@ if TYPE_CHECKING:
34
34
  from classiq.model_expansions.closure import FunctionClosure
35
35
 
36
36
 
37
+ ALREADY_ALLOCATED_MESSAGE = "Cannot allocate variable '{}', it is already initialized"
38
+ ALREADY_FREED_MESSAGE = "Cannot free variable '{}', it is already uninitialized"
39
+
40
+
37
41
  class PortDirection(StrEnum):
38
42
  Input = "input"
39
43
  Inout = "inout"
@@ -141,7 +145,14 @@ class CapturedVars:
141
145
  isinstance(captured_handle.handle, NestedHandleBinding)
142
146
  and captured_handle.direction != PortDirection.Inout
143
147
  ):
144
- raise ClassiqInternalExpansionError("Captured nested handles must be inout")
148
+ verb = (
149
+ "free"
150
+ if captured_handle.direction == PortDirection.Input
151
+ else "allocate"
152
+ )
153
+ raise ClassiqExpansionError(
154
+ f"Cannot partially {verb} variable {captured_handle.handle.name}"
155
+ )
145
156
 
146
157
  new_captured_handles = []
147
158
  for existing_captured_handle in self._captured_handles:
@@ -174,7 +185,9 @@ class CapturedVars:
174
185
  return captured_handle.change_direction(PortDirection.Inout)
175
186
  if captured_handle.direction == PortDirection.Outin:
176
187
  return captured_handle.change_direction(PortDirection.Input)
177
- raise ClassiqInternalExpansionError("Captured handle is already freed")
188
+ raise ClassiqExpansionError(
189
+ ALREADY_FREED_MESSAGE.format(captured_handle.handle)
190
+ )
178
191
  if existing_captured_handle.direction == PortDirection.Output:
179
192
  if captured_handle.direction == PortDirection.Input:
180
193
  return captured_handle.change_direction(PortDirection.Outin)
@@ -182,8 +195,8 @@ class CapturedVars:
182
195
  PortDirection.Output,
183
196
  PortDirection.Outin,
184
197
  ):
185
- raise ClassiqInternalExpansionError(
186
- "Captured handle is already allocated"
198
+ raise ClassiqExpansionError(
199
+ ALREADY_ALLOCATED_MESSAGE.format(captured_handle.handle)
187
200
  )
188
201
  return captured_handle.change_direction(PortDirection.Output)
189
202
  if existing_captured_handle.direction == PortDirection.Inout:
@@ -191,14 +204,16 @@ class CapturedVars:
191
204
  PortDirection.Output,
192
205
  PortDirection.Outin,
193
206
  ):
194
- raise ClassiqInternalExpansionError(
195
- "Captured handle is already allocated"
207
+ raise ClassiqExpansionError(
208
+ ALREADY_ALLOCATED_MESSAGE.format(captured_handle.handle)
196
209
  )
197
210
  elif captured_handle.direction in (
198
211
  PortDirection.Input,
199
212
  PortDirection.Inout,
200
213
  ):
201
- raise ClassiqInternalExpansionError("Captured handle is already freed")
214
+ raise ClassiqExpansionError(
215
+ ALREADY_FREED_MESSAGE.format(captured_handle.handle)
216
+ )
202
217
  return captured_handle
203
218
 
204
219
  def _intersect_handles(
@@ -211,7 +226,9 @@ class CapturedVars:
211
226
  PortDirection.Input,
212
227
  PortDirection.Outin,
213
228
  ):
214
- raise ClassiqInternalExpansionError("Captured handle is already freed")
229
+ raise ClassiqExpansionError(
230
+ ALREADY_FREED_MESSAGE.format(captured_handle.handle)
231
+ )
215
232
  return existing_captured_handle
216
233
 
217
234
  if existing_captured_handle.handle in captured_handle.handle:
@@ -219,8 +236,8 @@ class CapturedVars:
219
236
  PortDirection.Output,
220
237
  PortDirection.Outin,
221
238
  ):
222
- raise ClassiqInternalExpansionError(
223
- "Captured handle is already allocated"
239
+ raise ClassiqExpansionError(
240
+ ALREADY_ALLOCATED_MESSAGE.format(captured_handle.handle)
224
241
  )
225
242
  return captured_handle
226
243
 
@@ -25,8 +25,8 @@ from classiq.interface.model.quantum_function_declaration import (
25
25
 
26
26
  from classiq.model_expansions.closure import FunctionClosure
27
27
  from classiq.model_expansions.evaluators.type_type_match import check_signature_match
28
- from classiq.model_expansions.model_tables import SymbolTable
29
28
  from classiq.model_expansions.scope import Evaluated, QuantumSymbol
29
+ from classiq.qmod.model_state_container import QMODULE
30
30
  from classiq.qmod.qmod_parameter import CInt, get_qmod_type
31
31
 
32
32
 
@@ -122,11 +122,11 @@ def _check_classical_type_match(
122
122
  type_name = _resolve_type_name(classical_type)
123
123
  type_is_struct = (
124
124
  isinstance(classical_type, TypeName)
125
- and classical_type.name in SymbolTable.type_table
125
+ and classical_type.name in QMODULE.type_decls
126
126
  )
127
127
  type_is_enum = (
128
128
  isinstance(classical_type, TypeName)
129
- and classical_type.name in SymbolTable.enum_table
129
+ and classical_type.name in QMODULE.enum_decls
130
130
  )
131
131
  arg_is_qvar = isinstance(argument, QmodSizedProxy)
132
132
  arg_is_builtin = argument.__class__.__module__ == "builtins"
@@ -153,9 +153,6 @@ def _resolve_type_name(classical_type: ConcreteClassicalType) -> str:
153
153
  type_name = get_qmod_type(classical_type).__name__
154
154
  if not isinstance(classical_type, TypeName):
155
155
  return type_name
156
- if (
157
- type_name not in SymbolTable.type_table
158
- and type_name not in SymbolTable.enum_table
159
- ):
156
+ if type_name not in QMODULE.type_decls and type_name not in QMODULE.enum_decls:
160
157
  raise ClassiqExpansionError(f"Undefined type {type_name}")
161
158
  return type_name
@@ -1,3 +1,4 @@
1
+ from collections.abc import Sequence
1
2
  from typing import Optional
2
3
 
3
4
  from classiq.interface.exceptions import (
@@ -10,10 +11,14 @@ from classiq.interface.generator.functions.type_name import (
10
11
  TypeName,
11
12
  )
12
13
  from classiq.interface.model.bind_operation import BindOperation
14
+ from classiq.interface.model.inplace_binary_operation import BinaryOperation
15
+ from classiq.interface.model.port_declaration import PortDeclaration
16
+ from classiq.interface.model.quantum_function_declaration import PositionalArg
13
17
  from classiq.interface.model.quantum_type import (
14
18
  QuantumBit,
15
19
  QuantumBitvector,
16
20
  QuantumNumeric,
21
+ QuantumScalar,
17
22
  QuantumType,
18
23
  )
19
24
 
@@ -209,14 +214,26 @@ def validate_bind_targets(bind: BindOperation, scope: Scope) -> None:
209
214
  )
210
215
 
211
216
 
212
- def validate_inplace_binary_op_vars(
213
- value_var: QuantumSymbol, target_var: QuantumSymbol, operation_name: str
214
- ) -> None:
215
- if not isinstance(value_var.quantum_type, QuantumNumeric):
217
+ def get_inplace_op_scalar_as_numeric(
218
+ var: QuantumSymbol, operation: BinaryOperation, var_kind: str
219
+ ) -> QuantumNumeric:
220
+ if not isinstance(var.quantum_type, QuantumScalar):
216
221
  raise ClassiqExpansionError(
217
- f"Cannot perform `{operation_name}` operation with non numeric value {value_var.handle}"
222
+ f"Cannot perform inplace {operation.name.lower()} with non-scalar {var_kind} {var.handle}"
218
223
  )
219
- if not isinstance(target_var.quantum_type, QuantumNumeric):
220
- raise ClassiqExpansionError(
221
- f"Cannot perform `{operation_name}` operation with non numeric target {target_var.handle}"
224
+ if isinstance(var.quantum_type, QuantumNumeric):
225
+ return var.quantum_type
226
+ if isinstance(var.quantum_type, QuantumBit):
227
+ return QuantumNumeric(
228
+ size=Expression(expr="1"),
229
+ is_signed=Expression(expr="False"),
230
+ fraction_digits=Expression(expr="0"),
222
231
  )
232
+ raise ClassiqInternalExpansionError(f"Unexpected scalar type {var.quantum_type}")
233
+
234
+
235
+ def is_signature_monomorphic(params: Sequence[PositionalArg]) -> bool:
236
+ return all(
237
+ isinstance(param, PortDeclaration) and param.quantum_type.is_evaluated
238
+ for param in params
239
+ )
@@ -24,12 +24,13 @@ from classiq.model_expansions.atomic_expression_functions_defs import (
24
24
  ATOMIC_EXPRESSION_FUNCTIONS,
25
25
  qmod_val_to_python,
26
26
  )
27
- from classiq.model_expansions.model_tables import SymbolTable
28
27
  from classiq.model_expansions.sympy_conversion.expression_to_sympy import (
29
28
  translate_to_sympy,
30
29
  )
31
30
  from classiq.model_expansions.sympy_conversion.sympy_to_python import sympy_to_python
32
31
  from classiq.qmod import symbolic
32
+ from classiq.qmod.builtins.enums import BUILTIN_ENUM_DECLARATIONS
33
+ from classiq.qmod.model_state_container import QMODULE
33
34
 
34
35
 
35
36
  def evaluate_constants(constants: list[Constant]) -> dict[str, EvaluatedExpression]:
@@ -56,7 +57,12 @@ def evaluate(
56
57
  ) -> EvaluatedExpression:
57
58
  model_locals: dict[str, ExpressionValue] = {}
58
59
  model_locals.update(ATOMIC_EXPRESSION_FUNCTIONS)
59
- model_locals.update(SymbolTable.enum_table.enums)
60
+ model_locals.update(
61
+ {
62
+ enum_decl.name: enum_decl.create_enum()
63
+ for enum_decl in (QMODULE.enum_decls | BUILTIN_ENUM_DECLARATIONS).values()
64
+ }
65
+ )
60
66
  # locals override builtin-functions
61
67
  model_locals.update({name: expr.value for name, expr in locals_dict.items()})
62
68
  uninitialized_locals = uninitialized_locals or set()
@@ -29,7 +29,14 @@ from classiq.model_expansions.capturing.captured_vars import (
29
29
  CapturedVars,
30
30
  validate_captured_directions,
31
31
  )
32
- from classiq.model_expansions.closure import Closure, FunctionClosure
32
+ from classiq.model_expansions.closure import (
33
+ Closure,
34
+ FunctionClosure,
35
+ GenerativeFunctionClosure,
36
+ )
37
+ from classiq.model_expansions.evaluators.quantum_type_utils import (
38
+ is_signature_monomorphic,
39
+ )
33
40
  from classiq.model_expansions.scope import Scope
34
41
  from classiq.model_expansions.utils.counted_name_allocator import CountedNameAllocator
35
42
 
@@ -200,16 +207,7 @@ class OperationBuilder:
200
207
  def create_definition(
201
208
  self, function_context: FunctionContext
202
209
  ) -> NativeFunctionDefinition:
203
- name = function_context.name
204
- if name != MAIN_FUNCTION_NAME:
205
- for _ in self.current_scope:
206
- name = self._counted_name_allocator.allocate(
207
- f"{name}_{LAMBDA_KEYWORD + '_0_0_' if function_context.is_lambda else ''}{EXPANDED_KEYWORD}"
208
- )
209
- if name not in self.current_scope:
210
- break
211
- else:
212
- raise ClassiqInternalExpansionError("Could not allocate function name")
210
+ name = self._get_expanded_function_name(function_context)
213
211
  new_parameters: list[PortDeclaration] = [
214
212
  param
215
213
  for param in function_context.positional_arg_declarations
@@ -221,3 +219,29 @@ class OperationBuilder:
221
219
  body=function_context.body,
222
220
  positional_arg_declarations=new_parameters,
223
221
  )
222
+
223
+ def _get_expanded_function_name(self, function_context: FunctionContext) -> str:
224
+ name = function_context.name
225
+
226
+ if name == MAIN_FUNCTION_NAME:
227
+ return name
228
+
229
+ if name in self.current_scope:
230
+ orig_func = self.current_scope[name].value
231
+ if (
232
+ isinstance(orig_func, FunctionClosure)
233
+ and not isinstance(orig_func, GenerativeFunctionClosure)
234
+ and is_signature_monomorphic(orig_func.positional_arg_declarations)
235
+ ):
236
+ return name
237
+
238
+ for _ in self.current_scope:
239
+ name = self._counted_name_allocator.allocate(
240
+ f"{name}_{LAMBDA_KEYWORD + '_0_0_' if function_context.is_lambda else ''}{EXPANDED_KEYWORD}"
241
+ )
242
+ if name not in self.current_scope:
243
+ break
244
+ else:
245
+ raise ClassiqInternalExpansionError("Could not allocate function name")
246
+
247
+ return name
@@ -30,11 +30,13 @@ from classiq.qmod.quantum_expandable import (
30
30
  QTerminalCallable,
31
31
  )
32
32
  from classiq.qmod.quantum_function import QFunc
33
- from classiq.qmod.semantics.static_semantics_visitor import resolve_function_calls
33
+ from classiq.qmod.semantics.annotation.call_annotation import resolve_function_calls
34
34
  from classiq.qmod.symbolic_expr import SymbolicExpr
35
35
 
36
36
  if TYPE_CHECKING:
37
- from classiq.model_expansions.interpreter import Interpreter
37
+ from classiq.model_expansions.interpreters.generative_interpreter import (
38
+ GenerativeInterpreter,
39
+ )
38
40
 
39
41
 
40
42
  class LenList(list):
@@ -94,7 +96,7 @@ def translate_ast_arg_to_python_qmod(param: PositionalArg, evaluated: Evaluated)
94
96
 
95
97
 
96
98
  class _InterpreterExpandable(QFunc):
97
- def __init__(self, interpreter: "Interpreter"):
99
+ def __init__(self, interpreter: "GenerativeInterpreter"):
98
100
  super().__init__(lambda: None)
99
101
  self._interpreter = interpreter
100
102
 
@@ -137,7 +139,7 @@ class _InterpreterExpandable(QFunc):
137
139
 
138
140
 
139
141
  def emit_generative_statements(
140
- interpreter: "Interpreter",
142
+ interpreter: "GenerativeInterpreter",
141
143
  operation: GenerativeClosure,
142
144
  args: list[Evaluated],
143
145
  ) -> None:
File without changes
@@ -0,0 +1,263 @@
1
+ import ast
2
+ from abc import abstractmethod
3
+ from collections import defaultdict
4
+ from collections.abc import Sequence
5
+ from contextlib import nullcontext
6
+ from functools import singledispatchmethod
7
+ from typing import Any, Optional, cast
8
+
9
+ import sympy
10
+ from pydantic import ValidationError
11
+
12
+ from classiq.interface.exceptions import (
13
+ ClassiqError,
14
+ ClassiqExpansionError,
15
+ ClassiqInternalExpansionError,
16
+ )
17
+ from classiq.interface.generator.constant import Constant
18
+ from classiq.interface.generator.expressions.expression import Expression
19
+ from classiq.interface.generator.types.compilation_metadata import CompilationMetadata
20
+ from classiq.interface.model.handle_binding import (
21
+ FieldHandleBinding,
22
+ HandleBinding,
23
+ SlicedHandleBinding,
24
+ SubscriptHandleBinding,
25
+ )
26
+ from classiq.interface.model.model import MAIN_FUNCTION_NAME, Model
27
+ from classiq.interface.model.native_function_definition import NativeFunctionDefinition
28
+ from classiq.interface.model.quantum_function_declaration import (
29
+ QuantumFunctionDeclaration,
30
+ )
31
+ from classiq.interface.model.quantum_lambda_function import (
32
+ OperandIdentifier,
33
+ QuantumLambdaFunction,
34
+ )
35
+ from classiq.interface.model.quantum_statement import QuantumStatement
36
+
37
+ from classiq.model_expansions.closure import (
38
+ Closure,
39
+ FunctionClosure,
40
+ )
41
+ from classiq.model_expansions.debug_flag import debug_mode
42
+ from classiq.model_expansions.evaluators.classical_expression import (
43
+ evaluate_classical_expression,
44
+ )
45
+ from classiq.model_expansions.expression_renamer import ExpressionRenamer
46
+ from classiq.model_expansions.function_builder import (
47
+ FunctionContext,
48
+ OperationBuilder,
49
+ OperationContext,
50
+ )
51
+ from classiq.model_expansions.scope import Evaluated, QuantumSymbol, Scope
52
+ from classiq.model_expansions.scope_initialization import (
53
+ add_constants_to_scope,
54
+ add_entry_point_params_to_scope,
55
+ get_main_renamer,
56
+ init_builtin_types,
57
+ init_top_level_scope,
58
+ )
59
+ from classiq.model_expansions.utils.counted_name_allocator import CountedNameAllocator
60
+ from classiq.model_expansions.visitors.variable_references import VarRefCollector
61
+ from classiq.qmod.builtins.enums import BUILTIN_ENUM_DECLARATIONS
62
+ from classiq.qmod.builtins.structs import BUILTIN_STRUCT_DECLARATIONS
63
+ from classiq.qmod.model_state_container import QMODULE
64
+ from classiq.qmod.semantics.error_manager import ErrorManager
65
+
66
+
67
+ class BaseInterpreter:
68
+ def __init__(self, model: Model) -> None:
69
+ self._model = model
70
+ self._top_level_scope = Scope()
71
+ self._counted_name_allocator = CountedNameAllocator()
72
+ self._builder = OperationBuilder(
73
+ self._top_level_scope, self._counted_name_allocator
74
+ )
75
+ self._expanded_functions: dict[str, NativeFunctionDefinition] = {}
76
+
77
+ init_builtin_types()
78
+ init_top_level_scope(model, self._top_level_scope)
79
+ self._functions_compilation_metadata: dict[str, CompilationMetadata] = dict(
80
+ self._model.functions_compilation_metadata
81
+ )
82
+ self._expanded_functions_compilation_metadata: dict[
83
+ str, CompilationMetadata
84
+ ] = defaultdict(CompilationMetadata)
85
+ self._counted_name_allocator = CountedNameAllocator()
86
+ self._error_manager: ErrorManager = ErrorManager()
87
+
88
+ def get_main_renamer(self) -> Optional[ExpressionRenamer]:
89
+ return get_main_renamer(self._get_function_declarations())
90
+
91
+ def _expand_main_func(self) -> None:
92
+ main_closure = self._get_main_closure(
93
+ self._top_level_scope[MAIN_FUNCTION_NAME].value
94
+ )
95
+ add_entry_point_params_to_scope(
96
+ main_closure.positional_arg_declarations, main_closure
97
+ )
98
+ context = self._expand_operation(main_closure)
99
+ self._expanded_functions[main_closure.closure_id] = (
100
+ self._builder.create_definition(cast(FunctionContext, context))
101
+ )
102
+
103
+ def _get_main_closure(self, main_func: FunctionClosure) -> FunctionClosure:
104
+ return FunctionClosure.create(
105
+ name=main_func.name,
106
+ positional_arg_declarations=main_func.positional_arg_declarations,
107
+ scope=Scope(parent=self._top_level_scope),
108
+ expr_renamer=self.get_main_renamer(),
109
+ _depth=0,
110
+ body=main_func.body,
111
+ )
112
+
113
+ def expand(self) -> Model:
114
+ try:
115
+ with self._error_manager.call("main"):
116
+ self._expand_main_func()
117
+ except Exception as e:
118
+ if isinstance(e, ClassiqInternalExpansionError) or debug_mode.get():
119
+ raise e
120
+ if not isinstance(e, (ClassiqError, ValidationError)):
121
+ raise ClassiqInternalExpansionError(str(e)) from None
122
+ prefix = ""
123
+ if not isinstance(e, ClassiqExpansionError):
124
+ prefix = f"{type(e).__name__}: "
125
+ self._error_manager.add_error(f"{prefix}{e}")
126
+ finally:
127
+ self._error_manager.report_errors(ClassiqExpansionError)
128
+
129
+ return Model(
130
+ constraints=self._model.constraints,
131
+ preferences=self._model.preferences,
132
+ classical_execution_code=self._model.classical_execution_code,
133
+ execution_preferences=self._model.execution_preferences,
134
+ functions=list(self._expanded_functions.values()),
135
+ constants=self._model.constants,
136
+ enums=[
137
+ enum_decl
138
+ for name, enum_decl in QMODULE.enum_decls.items()
139
+ if name not in BUILTIN_ENUM_DECLARATIONS
140
+ ],
141
+ types=[
142
+ struct_decl
143
+ for name, struct_decl in QMODULE.type_decls.items()
144
+ if name not in BUILTIN_STRUCT_DECLARATIONS
145
+ ],
146
+ qstructs=list(QMODULE.qstruct_decls.values()),
147
+ debug_info=self._model.debug_info,
148
+ functions_compilation_metadata=self._expanded_functions_compilation_metadata,
149
+ )
150
+
151
+ @singledispatchmethod
152
+ def evaluate(self, expression: Any) -> Evaluated:
153
+ raise NotImplementedError(f"Cannot evaluate {expression!r}")
154
+
155
+ @evaluate.register
156
+ def evaluate_classical_expression(self, expression: Expression) -> Evaluated:
157
+ expr = evaluate_classical_expression(expression, self._builder.current_scope)
158
+ if not isinstance(expr.value, sympy.Basic):
159
+ return expr
160
+ vrc = VarRefCollector(ignore_duplicated_handles=True)
161
+ vrc.visit(ast.parse(str(expr.value)))
162
+ for handle in vrc.var_handles:
163
+ if handle.name in self._builder.current_scope and isinstance(
164
+ self._builder.current_scope[handle.name], QuantumSymbol
165
+ ):
166
+ self.evaluate(handle)
167
+ return expr
168
+
169
+ @evaluate.register
170
+ def evaluate_identifier(self, identifier: str) -> Evaluated:
171
+ return self._builder.current_scope[identifier]
172
+
173
+ @evaluate.register
174
+ def _evaluate_lambda(self, function: QuantumLambdaFunction) -> Evaluated:
175
+ return self.evaluate_lambda(function)
176
+
177
+ def evaluate_lambda(self, function: QuantumLambdaFunction) -> Evaluated:
178
+ raise NotImplementedError
179
+
180
+ @evaluate.register
181
+ def evaluate_handle_binding(self, handle_binding: HandleBinding) -> Evaluated:
182
+ return self.evaluate(handle_binding.name)
183
+
184
+ @evaluate.register
185
+ def evaluate_sliced_handle_binding(
186
+ self, sliced_handle_binding: SlicedHandleBinding
187
+ ) -> Evaluated:
188
+ quantum_variable = self.evaluate(sliced_handle_binding.base_handle).as_type(
189
+ QuantumSymbol
190
+ )
191
+ start = self.evaluate(sliced_handle_binding.start).as_type(int)
192
+ end = self.evaluate(sliced_handle_binding.end).as_type(int)
193
+ return Evaluated(value=quantum_variable[start:end])
194
+
195
+ @evaluate.register
196
+ def evaluate_list(self, value: list) -> Evaluated:
197
+ return Evaluated(value=[self.evaluate(arg).value for arg in value])
198
+
199
+ @evaluate.register
200
+ def evaluate_subscript_handle(self, subscript: SubscriptHandleBinding) -> Evaluated:
201
+ base_value = self.evaluate(subscript.base_handle)
202
+ index_value = self.evaluate(subscript.index).as_type(int)
203
+ return Evaluated(value=base_value.value[index_value])
204
+
205
+ @evaluate.register
206
+ def evaluate_subscript_operand(self, subscript: OperandIdentifier) -> Evaluated:
207
+ base_value = self.evaluate(subscript.name)
208
+ index_value = self.evaluate(subscript.index).as_type(int)
209
+ return Evaluated(value=base_value.value[index_value])
210
+
211
+ @evaluate.register
212
+ def evaluate_field_access(self, field_access: FieldHandleBinding) -> Evaluated:
213
+ base_value = self.evaluate(field_access.base_handle)
214
+ return Evaluated(value=base_value.value.fields[field_access.field])
215
+
216
+ @abstractmethod
217
+ def emit(self, statement: QuantumStatement) -> None:
218
+ pass
219
+
220
+ def _expand_block(self, block: Sequence[QuantumStatement], block_name: str) -> None:
221
+ with self._builder.block_context(block_name):
222
+ for statement in block:
223
+ self.emit_statement(statement)
224
+
225
+ def emit_statement(self, statement: QuantumStatement) -> None:
226
+ source_ref = statement.source_ref
227
+ error_context = (
228
+ self._error_manager.node_context(statement)
229
+ if source_ref is not None
230
+ else nullcontext()
231
+ )
232
+ with error_context, self._builder.source_ref_context(source_ref):
233
+ self.emit(statement)
234
+
235
+ def _expand_operation(self, operation: Closure) -> OperationContext:
236
+ with self._builder.operation_context(operation) as context:
237
+ if isinstance(operation, FunctionClosure) and (
238
+ (func_def := self._expanded_functions.get(operation.closure_id))
239
+ is not None
240
+ ):
241
+ captured_vars = self._top_level_scope[func_def.name].value.captured_vars
242
+ operation.captured_vars.update(captured_vars)
243
+ else:
244
+ self._expand_body(operation)
245
+
246
+ return context
247
+
248
+ def _expand_body(self, operation: Closure) -> None:
249
+ for block, block_body in operation.blocks.items():
250
+ self._expand_block(block_body, block)
251
+
252
+ def _get_function_declarations(self) -> Sequence[QuantumFunctionDeclaration]:
253
+ return [
254
+ QuantumFunctionDeclaration(
255
+ name=func_closure.name,
256
+ positional_arg_declarations=func_closure.positional_arg_declarations,
257
+ )
258
+ for func in self._top_level_scope.values()
259
+ if isinstance(func_closure := func.value, FunctionClosure)
260
+ ]
261
+
262
+ def add_constant(self, constant: Constant) -> None:
263
+ add_constants_to_scope([constant], self._top_level_scope)
@@ -0,0 +1,28 @@
1
+ from classiq.interface.model.quantum_function_call import QuantumFunctionCall
2
+
3
+ from classiq.model_expansions.closure import FunctionClosure, GenerativeFunctionClosure
4
+ from classiq.model_expansions.interpreters.generative_interpreter import (
5
+ GenerativeInterpreter,
6
+ )
7
+ from classiq.model_expansions.quantum_operations.quantum_function_call import (
8
+ DeclarativeQuantumFunctionCallEmitter,
9
+ )
10
+ from classiq.model_expansions.scope import Scope
11
+
12
+
13
+ class FrontendGenerativeInterpreter(GenerativeInterpreter):
14
+ def emit_quantum_function_call(self, call: QuantumFunctionCall) -> None:
15
+ DeclarativeQuantumFunctionCallEmitter(self).emit(call)
16
+
17
+ def _get_main_closure(self, main_func: FunctionClosure) -> FunctionClosure:
18
+ if isinstance(main_func, GenerativeFunctionClosure):
19
+ return GenerativeFunctionClosure.create(
20
+ name=main_func.name,
21
+ positional_arg_declarations=main_func.positional_arg_declarations,
22
+ scope=Scope(parent=self._top_level_scope),
23
+ expr_renamer=self.get_main_renamer(),
24
+ _depth=0,
25
+ generative_blocks={"body": main_func.generative_blocks["body"]},
26
+ )
27
+
28
+ return super()._get_main_closure(main_func)