classiq 0.61.0__py3-none-any.whl → 0.63.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 (83) hide show
  1. classiq/__init__.py +3 -0
  2. classiq/_internals/api_wrapper.py +6 -26
  3. classiq/_internals/client.py +1 -9
  4. classiq/applications/chemistry/chemistry_model_constructor.py +1 -1
  5. classiq/applications/combinatorial_helpers/combinatorial_problem_utils.py +26 -8
  6. classiq/applications/combinatorial_helpers/optimization_model.py +13 -2
  7. classiq/applications/combinatorial_helpers/pyomo_utils.py +143 -13
  8. classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +1 -1
  9. classiq/applications/combinatorial_optimization/combinatorial_problem.py +58 -23
  10. classiq/applications/grover/grover_model_constructor.py +1 -1
  11. classiq/applications/libraries/qmci_library.py +2 -1
  12. classiq/execution/execution_session.py +66 -96
  13. classiq/execution/jobs.py +12 -10
  14. classiq/interface/_version.py +1 -1
  15. classiq/interface/backend/backend_preferences.py +26 -5
  16. classiq/interface/backend/pydantic_backend.py +1 -1
  17. classiq/interface/backend/quantum_backend_providers.py +3 -1
  18. classiq/interface/chemistry/operator.py +0 -204
  19. classiq/interface/execution/primitives.py +1 -0
  20. classiq/interface/generator/compiler_keywords.py +4 -0
  21. classiq/interface/generator/copy.py +47 -0
  22. classiq/interface/generator/function_param_list_without_self_reference.py +2 -0
  23. classiq/interface/generator/functions/type_name.py +6 -0
  24. classiq/interface/generator/generated_circuit_data.py +22 -7
  25. classiq/interface/generator/model/model.py +3 -0
  26. classiq/interface/generator/model/preferences/preferences.py +14 -1
  27. classiq/interface/generator/quantum_function_call.py +4 -2
  28. classiq/interface/generator/types/compilation_metadata.py +2 -1
  29. classiq/interface/model/handle_binding.py +50 -5
  30. classiq/interface/model/quantum_type.py +16 -0
  31. classiq/interface/server/routes.py +1 -3
  32. classiq/model_expansions/capturing/captured_vars.py +114 -28
  33. classiq/model_expansions/closure.py +25 -65
  34. classiq/model_expansions/function_builder.py +19 -9
  35. classiq/model_expansions/generative_functions.py +16 -2
  36. classiq/model_expansions/interpreter.py +110 -66
  37. classiq/model_expansions/model_tables.py +4 -0
  38. classiq/model_expansions/quantum_operations/call_emitter.py +83 -20
  39. classiq/model_expansions/quantum_operations/classicalif.py +1 -1
  40. classiq/model_expansions/quantum_operations/control.py +3 -10
  41. classiq/model_expansions/quantum_operations/emitter.py +3 -4
  42. classiq/model_expansions/quantum_operations/quantum_assignment_operation.py +1 -2
  43. classiq/model_expansions/quantum_operations/quantum_function_call.py +1 -1
  44. classiq/model_expansions/quantum_operations/repeat.py +4 -3
  45. classiq/model_expansions/quantum_operations/shallow_emitter.py +9 -3
  46. classiq/model_expansions/scope.py +9 -13
  47. classiq/model_expansions/scope_initialization.py +34 -25
  48. classiq/model_expansions/transformers/var_splitter.py +57 -7
  49. classiq/open_library/__init__.py +4 -0
  50. classiq/open_library/functions/__init__.py +130 -0
  51. classiq/{qmod/builtins → open_library}/functions/amplitude_estimation.py +2 -2
  52. classiq/{qmod/builtins → open_library}/functions/discrete_sine_cosine_transform.py +6 -4
  53. classiq/{qmod/builtins → open_library}/functions/grover.py +2 -2
  54. classiq/{qmod/builtins → open_library}/functions/linear_pauli_rotation.py +1 -1
  55. classiq/{qmod/builtins → open_library}/functions/modular_exponentiation.py +2 -2
  56. classiq/{qmod/builtins → open_library}/functions/qpe.py +2 -2
  57. classiq/{qmod/builtins → open_library}/functions/state_preparation.py +6 -149
  58. classiq/{qmod/builtins → open_library}/functions/swap_test.py +1 -1
  59. classiq/open_library/functions/utility_functions.py +81 -0
  60. classiq/{qmod/builtins → open_library}/functions/variational.py +1 -1
  61. classiq/qmod/builtins/functions/__init__.py +4 -130
  62. classiq/qmod/builtins/functions/allocation.py +150 -0
  63. classiq/qmod/builtins/functions/arithmetic.py +0 -34
  64. classiq/qmod/builtins/functions/operators.py +0 -6
  65. classiq/qmod/builtins/operations.py +19 -80
  66. classiq/qmod/create_model_function.py +8 -162
  67. classiq/qmod/generative.py +0 -16
  68. classiq/qmod/model_state_container.py +7 -0
  69. classiq/qmod/native/pretty_printer.py +10 -11
  70. classiq/qmod/pretty_print/pretty_printer.py +1 -1
  71. classiq/qmod/python_classical_type.py +1 -5
  72. classiq/qmod/qfunc.py +11 -12
  73. classiq/qmod/qmod_variable.py +1 -3
  74. classiq/qmod/quantum_expandable.py +23 -1
  75. classiq/qmod/quantum_function.py +69 -7
  76. {classiq-0.61.0.dist-info → classiq-0.63.0.dist-info}/METADATA +2 -1
  77. {classiq-0.61.0.dist-info → classiq-0.63.0.dist-info}/RECORD +82 -78
  78. classiq/qmod/builtins/functions/utility_functions.py +0 -43
  79. /classiq/{qmod/builtins → open_library}/functions/hea.py +0 -0
  80. /classiq/{qmod/builtins → open_library}/functions/qaoa_penalty.py +0 -0
  81. /classiq/{qmod/builtins → open_library}/functions/qft_functions.py +0 -0
  82. /classiq/{qmod/builtins → open_library}/functions/qsvt.py +0 -0
  83. {classiq-0.61.0.dist-info → classiq-0.63.0.dist-info}/WHEEL +0 -0
@@ -1,28 +1,21 @@
1
1
  import dataclasses
2
2
  import json
3
- import uuid
4
- from collections.abc import Collection, Iterator, Sequence
5
- from contextlib import contextmanager
3
+ from collections.abc import Collection, Sequence
6
4
  from dataclasses import dataclass, field
7
5
  from functools import singledispatch
8
- from symtable import Symbol
9
6
  from typing import Any, Optional
10
7
 
11
8
  from typing_extensions import Self
12
9
 
13
10
  from classiq.interface.exceptions import ClassiqInternalExpansionError
14
- from classiq.interface.generator.functions.builtins.internal_operators import (
15
- All_BUILTINS_OPERATORS,
16
- )
17
11
  from classiq.interface.model.port_declaration import PortDeclaration
18
12
  from classiq.interface.model.quantum_function_declaration import (
19
13
  NamedParamsQuantumFunctionDeclaration,
20
14
  PositionalArg,
21
- QuantumOperandDeclaration,
22
15
  )
16
+ from classiq.interface.model.quantum_lambda_function import QuantumCallable
23
17
  from classiq.interface.model.quantum_statement import QuantumStatement
24
18
 
25
- from classiq import ClassicalParameterDeclaration
26
19
  from classiq.model_expansions.capturing.captured_vars import CapturedVars
27
20
  from classiq.model_expansions.expression_renamer import ExpressionRenamer
28
21
  from classiq.model_expansions.scope import (
@@ -51,11 +44,6 @@ class Closure:
51
44
  if isinstance(param, PortDeclaration)
52
45
  }
53
46
 
54
- @contextmanager
55
- def freeze(self) -> Iterator[None]:
56
- with self.scope.freeze(), self.captured_vars.freeze():
57
- yield
58
-
59
47
 
60
48
  @dataclass(frozen=True)
61
49
  class GenerativeClosure(Closure):
@@ -79,15 +67,7 @@ class FunctionClosure(Closure):
79
67
  # The closure is changing across the interpreter flow so it's closure_id may change
80
68
  @property
81
69
  def closure_id(self) -> str:
82
- # builtins operators have side effects, so generate a unique id for than
83
- # may create a bugs. Therefore, with each call to closure_id a new id is
84
- # created
85
- if self.is_lambda or self.name in All_BUILTINS_OPERATORS:
86
- signature = str(uuid.uuid4())
87
- else:
88
- signature = _generate_closure_id(
89
- self.positional_arg_declarations, self.scope.data.values()
90
- )
70
+ signature = _generate_closure_id(self.scope.data.values())
91
71
  return f"{self.name}__{signature}"
92
72
 
93
73
  @property
@@ -142,70 +122,50 @@ class FunctionClosure(Closure):
142
122
  def set_depth(self, depth: int) -> Self:
143
123
  return dataclasses.replace(self, _depth=depth)
144
124
 
145
- def copy_scope(self) -> Self: # Remove when scoping is normal (CAD-24980)
125
+ def clone(self) -> Self:
146
126
  return dataclasses.replace(
147
- self, scope=Scope(self.scope.data, parent=self.scope.parent)
127
+ self,
128
+ scope=self.scope.clone(),
129
+ signature_scope=self.signature_scope.clone(),
130
+ captured_vars=self.captured_vars.clone(),
148
131
  )
149
132
 
133
+ def emit(self) -> QuantumCallable:
134
+ return self.name
135
+
150
136
 
151
137
  @dataclass(frozen=True)
152
138
  class GenerativeFunctionClosure(GenerativeClosure, FunctionClosure):
153
139
  pass
154
140
 
155
141
 
156
- def _generate_closure_id(
157
- args_declaration: Sequence[PositionalArg], evaluated_args: Collection[Evaluated]
158
- ) -> str:
159
- args_signature: dict = {}
160
- for arg_declara, eval_arg in zip(args_declaration, evaluated_args):
161
- args_signature |= _generate_arg_id(arg_declara, eval_arg)
142
+ def _generate_closure_id(evaluated_args: Collection[Evaluated]) -> str:
143
+ args_signature = [
144
+ _evaluated_arg_to_str(eval_arg.value) for eval_arg in evaluated_args
145
+ ]
162
146
  return json.dumps(args_signature)
163
147
 
164
148
 
165
- def _generate_arg_id(
166
- arg_declaration: PositionalArg, evaluated_arg: Evaluated
167
- ) -> dict[str, str]:
168
- arg_value = evaluated_arg.value
169
- arg_name = arg_declaration.name
170
- if isinstance(arg_declaration, ClassicalParameterDeclaration):
171
- return {arg_name: evaluated_classical_param_to_str(arg_value)}
172
- if isinstance(arg_declaration, PortDeclaration):
173
- return {arg_name: _evaluated_port_to_str(arg_value)}
174
- if isinstance(arg_declaration, QuantumOperandDeclaration):
175
- return {arg_name: _evaluated_operand_to_str(arg_value)}
176
-
177
-
149
+ @singledispatch
178
150
  def _evaluated_arg_to_str(arg: Any) -> str:
179
151
  if isinstance(arg, str):
180
152
  return arg
181
- return str(uuid.uuid4())
153
+ if isinstance(arg, QuantumSymbol):
154
+ return _evaluated_quantum_symbol_to_str(arg)
155
+ if isinstance(arg, FunctionClosure):
156
+ return _evaluated_one_operand_to_str(arg)
157
+ if isinstance(arg, list) and arg and isinstance(arg[0], FunctionClosure):
158
+ return _evaluated_operands_list_to_str(arg)
159
+ return evaluated_classical_param_to_str(arg)
182
160
 
183
161
 
184
- @singledispatch
185
- def _evaluated_port_to_str(arg: Any) -> str:
186
- return _evaluated_arg_to_str(arg)
187
-
188
-
189
- @_evaluated_port_to_str.register
190
162
  def _evaluated_quantum_symbol_to_str(port: QuantumSymbol) -> str:
191
163
  return port.quantum_type.model_dump_json(exclude_none=True, exclude={"name"})
192
164
 
193
165
 
194
- @_evaluated_port_to_str.register
195
- def _evaluated_symbol_to_str(port: Symbol) -> str:
196
- return repr(port)
197
-
198
-
199
- @singledispatch
200
- def _evaluated_operand_to_str(arg: Any) -> str:
201
- return _evaluated_arg_to_str(arg)
202
-
203
-
204
- @_evaluated_operand_to_str.register
205
166
  def _evaluated_one_operand_to_str(operand: FunctionClosure) -> str:
206
167
  return operand.closure_id
207
168
 
208
169
 
209
- @_evaluated_operand_to_str.register
210
- def _evaluated_list_operands_to_str(operands: list) -> str:
211
- return json.dumps([_evaluated_operand_to_str(ope) for ope in operands])
170
+ def _evaluated_operands_list_to_str(arg: list[FunctionClosure]) -> str:
171
+ return json.dumps([_evaluated_one_operand_to_str(ope) for ope in arg])
@@ -31,6 +31,7 @@ from classiq.model_expansions.capturing.captured_vars import (
31
31
  )
32
32
  from classiq.model_expansions.closure import Closure, FunctionClosure
33
33
  from classiq.model_expansions.scope import Scope
34
+ from classiq.model_expansions.utils.counted_name_allocator import CountedNameAllocator
34
35
 
35
36
  ClosureType = TypeVar("ClosureType", bound=Closure)
36
37
 
@@ -82,16 +83,23 @@ class FunctionContext(OperationContext[FunctionClosure]):
82
83
 
83
84
 
84
85
  class OperationBuilder:
85
- def __init__(self, functions_scope: Scope) -> None:
86
+ def __init__(
87
+ self, functions_scope: Scope, counted_name_allocator: CountedNameAllocator
88
+ ) -> None:
86
89
  self._operations: list[OperationContext] = []
87
90
  self._blocks: list[str] = []
88
91
  self._functions_scope = functions_scope
89
92
  self._current_source_ref: Optional[SourceReference] = None
93
+ self._counted_name_allocator = counted_name_allocator
90
94
 
91
95
  @property
92
96
  def current_operation(self) -> Closure:
93
97
  return self._operations[-1].closure
94
98
 
99
+ @property
100
+ def current_scope(self) -> Scope:
101
+ return self.current_operation.scope
102
+
95
103
  @property
96
104
  def current_function(self) -> FunctionClosure:
97
105
  return self._get_last_function(self._operations)
@@ -137,7 +145,8 @@ class OperationBuilder:
137
145
  validate_captured_directions(
138
146
  captured_vars.filter_vars(
139
147
  self.current_function, self.current_block.variable_declarations
140
- )
148
+ ),
149
+ report_outin=False,
141
150
  )
142
151
  self.current_operation.captured_vars.update(
143
152
  captured_vars.filter_vars(self.current_function)
@@ -193,13 +202,14 @@ class OperationBuilder:
193
202
  ) -> NativeFunctionDefinition:
194
203
  name = function_context.name
195
204
  if name != MAIN_FUNCTION_NAME:
196
- idx = 0
197
- new_name = name
198
- while idx == 0 or new_name in self._functions_scope:
199
- new_name = f"{name}_{LAMBDA_KEYWORD + '_0_0_' if function_context.is_lambda else ''}{EXPANDED_KEYWORD}_{idx}"
200
- idx += 1
201
- name = new_name
202
-
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")
203
213
  new_parameters: list[PortDeclaration] = [
204
214
  param
205
215
  for param in function_context.positional_arg_declarations
@@ -105,7 +105,21 @@ class _InterpreterExpandable(QFunc):
105
105
  positional_arg_declarations=current_operation.positional_arg_declarations,
106
106
  body=self._interpreter._builder._current_statements + [stmt],
107
107
  )
108
- resolve_function_calls(dummy_function, self._get_function_declarations())
108
+ declarative_functions = {
109
+ name: func
110
+ for name, func in self._qmodule.native_defs.items()
111
+ if name not in self._interpreter._top_level_scope
112
+ }
113
+ self._interpreter.update_declarative_functions(
114
+ declarative_functions, self._qmodule
115
+ )
116
+ self._interpreter.update_generative_functions(
117
+ self._qmodule.generative_functions
118
+ )
119
+ func_decls = self._get_function_declarations()
120
+ for dec_func in declarative_functions.values():
121
+ resolve_function_calls(dec_func, func_decls)
122
+ resolve_function_calls(dummy_function, func_decls)
109
123
  stmt = dummy_function.body[-1]
110
124
  with generative_mode_context(False):
111
125
  self._interpreter.emit_statement(stmt)
@@ -116,7 +130,7 @@ class _InterpreterExpandable(QFunc):
116
130
  name=name,
117
131
  positional_arg_declarations=evaluated.value.positional_arg_declarations,
118
132
  )
119
- for name, evaluated in self._interpreter._current_scope.items()
133
+ for name, evaluated in self._interpreter._builder.current_scope.items()
120
134
  if isinstance(evaluated, Evaluated)
121
135
  and isinstance(evaluated.value, FunctionClosure)
122
136
  } | nameables_to_dict(self._interpreter._get_function_declarations())
@@ -1,6 +1,6 @@
1
- import copy
2
- from collections.abc import Iterator, Sequence
3
- from contextlib import contextmanager, nullcontext
1
+ from collections import defaultdict
2
+ from collections.abc import Sequence
3
+ from contextlib import nullcontext
4
4
  from functools import singledispatchmethod
5
5
  from typing import Any, Optional, cast
6
6
 
@@ -32,7 +32,7 @@ from classiq.interface.model.handle_binding import (
32
32
  )
33
33
  from classiq.interface.model.inplace_binary_operation import InplaceBinaryOperation
34
34
  from classiq.interface.model.invert import Invert
35
- from classiq.interface.model.model import Model
35
+ from classiq.interface.model.model import MAIN_FUNCTION_NAME, Model
36
36
  from classiq.interface.model.native_function_definition import NativeFunctionDefinition
37
37
  from classiq.interface.model.phase_operation import PhaseOperation
38
38
  from classiq.interface.model.power import Power
@@ -93,11 +93,14 @@ from classiq.model_expansions.scope import Evaluated, QuantumSymbol, Scope
93
93
  from classiq.model_expansions.scope_initialization import (
94
94
  add_constants_to_scope,
95
95
  add_entry_point_params_to_scope,
96
+ add_functions_to_scope,
97
+ add_generative_functions_to_scope,
96
98
  get_main_renamer,
97
99
  init_top_level_scope,
98
100
  )
99
101
  from classiq.model_expansions.utils.counted_name_allocator import CountedNameAllocator
100
102
  from classiq.qmod.builtins.functions import permute
103
+ from classiq.qmod.model_state_container import QMODULE, ModelStateContainer
101
104
  from classiq.qmod.quantum_function import GenerativeQFunc
102
105
  from classiq.qmod.semantics.error_manager import ErrorManager
103
106
 
@@ -107,51 +110,52 @@ class Interpreter:
107
110
  self,
108
111
  model: Model,
109
112
  generative_functions: Optional[list[GenerativeQFunc]] = None,
110
- is_frontend: bool = False,
113
+ is_normalizer: bool = False,
114
+ is_shallow: bool = False,
111
115
  ) -> None:
112
- self._is_frontend = is_frontend
116
+ self._is_normalizer = is_normalizer
117
+ self._is_shallow = is_shallow
113
118
  self._model = model
114
- self._current_scope = Scope()
115
- self._top_level_scope = self._current_scope
116
- self._builder = OperationBuilder(self._top_level_scope)
119
+ self._top_level_scope = Scope()
120
+ self._counted_name_allocator = CountedNameAllocator()
121
+ self._builder = OperationBuilder(
122
+ self._top_level_scope, self._counted_name_allocator
123
+ )
117
124
  self._expanded_functions: dict[str, NativeFunctionDefinition] = {}
118
125
 
126
+ init_top_level_scope(model, generative_functions or [], self._top_level_scope)
119
127
  self._main_renamer: Optional[ExpressionRenamer] = (
120
- get_main_renamer(self._model.functions) if self._is_frontend else None
128
+ get_main_renamer(self._get_function_declarations())
129
+ if self._is_normalizer
130
+ else None
131
+ )
132
+ self._functions_compilation_metadata: dict[str, CompilationMetadata] = dict(
133
+ self._model.functions_compilation_metadata
121
134
  )
122
-
123
- if generative_functions is None:
124
- generative_functions = []
125
- self._generative_functions = generative_functions
126
- init_top_level_scope(model, generative_functions, self._current_scope)
127
135
  self._expanded_functions_compilation_metadata: dict[
128
136
  str, CompilationMetadata
129
- ] = dict()
137
+ ] = defaultdict(CompilationMetadata)
130
138
  self._counted_name_allocator = CountedNameAllocator()
131
139
  self._error_manager: ErrorManager = ErrorManager()
132
140
 
133
- @contextmanager
134
- def _scope_guard(self, scope: Scope) -> Iterator[None]:
135
- """This manager restores both `scope` and `self._current_scope` to their previous values."""
136
- scope_data = copy.copy(scope.data)
137
- prev_context_scope_data = copy.copy(self._current_scope.data)
138
-
139
- prev_context_scope = self._current_scope
140
- self._current_scope = scope
141
- yield
142
- prev_context_scope.data = prev_context_scope_data
143
- self._current_scope = prev_context_scope
144
-
145
- scope.data = scope_data
146
-
147
141
  def _expand_main_func(self) -> None:
148
- main_closure = FunctionClosure.create(
149
- name=self._model.main_func.name,
150
- positional_arg_declarations=self._model.main_func.positional_arg_declarations,
151
- body=self._model.main_func.body,
152
- scope=Scope(parent=self._current_scope),
142
+ main_func = self._top_level_scope[MAIN_FUNCTION_NAME].value
143
+ closure_constructor: Any
144
+ if isinstance(main_func, GenerativeFunctionClosure):
145
+ closure_constructor = GenerativeFunctionClosure
146
+ extra_args = {
147
+ "generative_blocks": {"body": main_func.generative_blocks["body"]}
148
+ }
149
+ else:
150
+ closure_constructor = FunctionClosure
151
+ extra_args = {"body": main_func.body}
152
+ main_closure = closure_constructor.create(
153
+ name=main_func.name,
154
+ positional_arg_declarations=main_func.positional_arg_declarations,
155
+ scope=Scope(parent=self._top_level_scope),
153
156
  expr_renamer=self._main_renamer,
154
157
  _depth=0,
158
+ **extra_args,
155
159
  )
156
160
 
157
161
  add_entry_point_params_to_scope(
@@ -185,9 +189,9 @@ class Interpreter:
185
189
  execution_preferences=self._model.execution_preferences,
186
190
  functions=list(self._expanded_functions.values()),
187
191
  constants=self._model.constants,
188
- enums=self._model.enums,
189
- types=self._model.types,
190
- qstructs=self._model.qstructs,
192
+ enums=list(QMODULE.enum_decls.values()),
193
+ types=list(QMODULE.type_decls.values()),
194
+ qstructs=list(QMODULE.qstruct_decls.values()),
191
195
  debug_info=self._model.debug_info,
192
196
  functions_compilation_metadata=self._expanded_functions_compilation_metadata,
193
197
  )
@@ -198,11 +202,11 @@ class Interpreter:
198
202
 
199
203
  @evaluate.register
200
204
  def evaluate_classical_expression(self, expression: Expression) -> Evaluated:
201
- return evaluate_classical_expression(expression, self._current_scope)
205
+ return evaluate_classical_expression(expression, self._builder.current_scope)
202
206
 
203
207
  @evaluate.register
204
208
  def evaluate_identifier(self, identifier: str) -> Evaluated:
205
- return self._current_scope[identifier]
209
+ return self._builder.current_scope[identifier]
206
210
 
207
211
  @evaluate.register
208
212
  def evaluate_lambda(self, function: QuantumLambdaFunction) -> Evaluated:
@@ -211,7 +215,9 @@ class Interpreter:
211
215
  for idx, param in enumerate(function.func_decl.positional_arg_declarations)
212
216
  ]
213
217
  func_decl = NamedParamsQuantumFunctionDeclaration(
214
- name=function.func_decl.name or "<lambda>",
218
+ name=self._counted_name_allocator.allocate(
219
+ function.func_decl.name or "<lambda>"
220
+ ),
215
221
  positional_arg_declarations=renamed_params,
216
222
  )
217
223
 
@@ -233,7 +239,7 @@ class Interpreter:
233
239
  name=func_decl.name,
234
240
  positional_arg_declarations=func_decl.positional_arg_declarations,
235
241
  body=function.body,
236
- scope=Scope(parent=self._current_scope),
242
+ scope=Scope(parent=self._builder.current_scope),
237
243
  is_lambda=True,
238
244
  **extra_args,
239
245
  ),
@@ -290,7 +296,7 @@ class Interpreter:
290
296
 
291
297
  @emit.register
292
298
  def emit_quantum_assignment_operation(self, op: QuantumAssignmentOperation) -> None:
293
- if self._is_frontend:
299
+ if self._is_normalizer:
294
300
  ShallowEmitter(
295
301
  self, "assignment_operation", components=["expression", "result_var"]
296
302
  ).emit(op)
@@ -299,7 +305,7 @@ class Interpreter:
299
305
 
300
306
  @emit.register
301
307
  def emit_inplace_binary_operation(self, op: InplaceBinaryOperation) -> None:
302
- if self._is_frontend:
308
+ if self._is_normalizer:
303
309
  ShallowEmitter(
304
310
  self, "inplace_binary_operation", components=["target", "value"]
305
311
  ).emit(op)
@@ -318,7 +324,7 @@ class Interpreter:
318
324
 
319
325
  @emit.register
320
326
  def emit_within_apply(self, within_apply: WithinApply) -> None:
321
- if self._is_frontend:
327
+ if self._is_normalizer:
322
328
  ShallowEmitter(
323
329
  self,
324
330
  WITHIN_APPLY_NAME,
@@ -329,7 +335,7 @@ class Interpreter:
329
335
 
330
336
  @emit.register
331
337
  def emit_invert(self, invert: Invert) -> None:
332
- if self._is_frontend:
338
+ if self._is_normalizer:
333
339
  ShallowEmitter(self, INVERT_OPERATOR_NAME, components=["body"]).emit(invert)
334
340
  else:
335
341
  InvertEmitter(self).emit(invert)
@@ -340,7 +346,7 @@ class Interpreter:
340
346
 
341
347
  @emit.register
342
348
  def emit_control(self, control: Control) -> None:
343
- if self._is_frontend:
349
+ if self._is_normalizer:
344
350
  ShallowEmitter(
345
351
  self,
346
352
  CONTROL_OPERATOR_NAME,
@@ -351,7 +357,7 @@ class Interpreter:
351
357
 
352
358
  @emit.register
353
359
  def emit_power(self, power: Power) -> None:
354
- if self._is_frontend:
360
+ if self._is_normalizer:
355
361
  ShallowEmitter(
356
362
  self, CONTROL_OPERATOR_NAME, components=["power", "body"]
357
363
  ).emit(power)
@@ -360,7 +366,7 @@ class Interpreter:
360
366
 
361
367
  @emit.register
362
368
  def emit_phase(self, phase: PhaseOperation) -> None:
363
- if self._is_frontend:
369
+ if self._is_normalizer:
364
370
  ShallowEmitter(self, "phase", components=["expression", "theta"]).emit(
365
371
  phase
366
372
  )
@@ -385,24 +391,23 @@ class Interpreter:
385
391
  def _expand_operation(self, operation: Closure) -> OperationContext:
386
392
  with self._builder.operation_context(operation) as context:
387
393
  if isinstance(operation, FunctionClosure) and (
388
- self._expanded_functions.get(operation.closure_id) is not None
394
+ (func_def := self._expanded_functions.get(operation.closure_id))
395
+ is not None
389
396
  ):
390
- pass
397
+ captured_vars = self._top_level_scope[func_def.name].value.captured_vars
398
+ operation.captured_vars.update(captured_vars)
391
399
  elif isinstance(operation, FunctionClosure) and operation.name == "permute":
392
400
  # special expansion since permute is generative
393
- with self._scope_guard(operation.scope):
394
- self._expand_permute()
401
+ self._expand_permute()
395
402
  elif isinstance(operation, GenerativeClosure):
396
- with self._scope_guard(operation.scope):
397
- args = [
398
- self.evaluate(param.name)
399
- for param in operation.positional_arg_declarations
400
- ]
401
- emit_generative_statements(self, operation, args)
403
+ args = [
404
+ self.evaluate(param.name)
405
+ for param in operation.positional_arg_declarations
406
+ ]
407
+ emit_generative_statements(self, operation, args)
402
408
  else:
403
409
  for block, block_body in operation.blocks.items():
404
- with self._scope_guard(operation.scope):
405
- self._expand_block(block_body, block)
410
+ self._expand_block(block_body, block)
406
411
 
407
412
  return context
408
413
 
@@ -421,11 +426,50 @@ class Interpreter:
421
426
  self._expand_block(calls, "body")
422
427
 
423
428
  def _get_function_declarations(self) -> Sequence[QuantumFunctionDeclaration]:
424
- return (
425
- self._model.functions
426
- + [gen_func.func_decl for gen_func in self._generative_functions]
427
- + list(self._expanded_functions.values())
428
- )
429
+ return [
430
+ QuantumFunctionDeclaration(
431
+ name=func_closure.name,
432
+ positional_arg_declarations=func_closure.positional_arg_declarations,
433
+ )
434
+ for func in self._top_level_scope.values()
435
+ if isinstance(func_closure := func.value, FunctionClosure)
436
+ ]
429
437
 
430
438
  def add_constant(self, constant: Constant) -> None:
431
439
  add_constants_to_scope([constant], self._top_level_scope)
440
+
441
+ def update_declarative_functions(
442
+ self,
443
+ functions: dict[str, NativeFunctionDefinition],
444
+ qmodule: ModelStateContainer,
445
+ ) -> None:
446
+ add_functions_to_scope(list(functions.values()), self._top_level_scope)
447
+ for dec_func_name in functions:
448
+ if dec_func_name in qmodule.functions_compilation_metadata:
449
+ self._functions_compilation_metadata[dec_func_name] = (
450
+ qmodule.functions_compilation_metadata[dec_func_name]
451
+ )
452
+
453
+ def update_generative_functions(
454
+ self, generative_functions: dict[str, GenerativeQFunc]
455
+ ) -> None:
456
+ add_generative_functions_to_scope(
457
+ list(generative_functions.values()), self._top_level_scope
458
+ )
459
+ for name, gen_func in generative_functions.items():
460
+ if gen_func.compilation_metadata is not None:
461
+ self._functions_compilation_metadata[name] = (
462
+ gen_func.compilation_metadata
463
+ )
464
+
465
+ def add_purely_declarative_function(self, function: FunctionClosure) -> None:
466
+ functions_to_add = [function.name] + QMODULE.function_dependencies[
467
+ function.name
468
+ ]
469
+ for func in functions_to_add:
470
+ if func not in self._expanded_functions and func in QMODULE.native_defs:
471
+ self._expanded_functions[func] = QMODULE.native_defs[func]
472
+ if func in QMODULE.functions_compilation_metadata:
473
+ self._expanded_functions_compilation_metadata[func] = (
474
+ QMODULE.functions_compilation_metadata[func]
475
+ )
@@ -78,10 +78,14 @@ class SymbolTable:
78
78
  @classmethod
79
79
  def init_user_enums(cls, user_enums: list[EnumDeclaration]) -> None:
80
80
  cls.enum_table = EnumTable(user_enums)
81
+ QMODULE.enum_decls = {enum_decl.name: enum_decl for enum_decl in user_enums}
81
82
 
82
83
  @classmethod
83
84
  def init_user_types(cls, user_types: list[StructDeclaration]) -> None:
84
85
  cls.type_table = StructTable(user_types)
86
+ QMODULE.type_decls = {
87
+ struct_decl.name: struct_decl for struct_decl in user_types
88
+ }
85
89
 
86
90
  @classmethod
87
91
  def init_user_qstructs(cls, user_qstructs: list[QStructDeclaration]) -> None: