classiq 0.64.0__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 (51) hide show
  1. classiq/_internals/api_wrapper.py +30 -0
  2. classiq/applications/chemistry/chemistry_model_constructor.py +8 -9
  3. classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +4 -6
  4. classiq/applications/combinatorial_optimization/combinatorial_problem.py +2 -5
  5. classiq/applications/finance/finance_model_constructor.py +7 -12
  6. classiq/applications/grover/grover_model_constructor.py +4 -6
  7. classiq/applications/qsvm/qsvm_model_constructor.py +6 -4
  8. classiq/execution/execution_session.py +14 -13
  9. classiq/interface/_version.py +1 -1
  10. classiq/interface/backend/backend_preferences.py +1 -9
  11. classiq/interface/generator/expressions/qmod_qarray_proxy.py +11 -13
  12. classiq/interface/generator/functions/type_name.py +6 -0
  13. classiq/interface/model/allocate.py +16 -0
  14. classiq/interface/model/quantum_type.py +26 -0
  15. classiq/interface/model/statement_block.py +2 -0
  16. classiq/interface/server/routes.py +1 -0
  17. classiq/model_expansions/evaluators/quantum_type_utils.py +10 -0
  18. classiq/model_expansions/function_builder.py +35 -11
  19. classiq/model_expansions/generative_functions.py +6 -4
  20. classiq/model_expansions/interpreters/base_interpreter.py +37 -138
  21. classiq/model_expansions/interpreters/frontend_generative_interpreter.py +28 -0
  22. classiq/model_expansions/interpreters/generative_interpreter.py +144 -3
  23. classiq/model_expansions/quantum_operations/call_emitter.py +43 -91
  24. classiq/model_expansions/quantum_operations/declarative_call_emitter.py +87 -0
  25. classiq/model_expansions/quantum_operations/emitter.py +5 -0
  26. classiq/model_expansions/quantum_operations/quantum_function_call.py +9 -0
  27. classiq/model_expansions/quantum_operations/shallow_emitter.py +20 -1
  28. classiq/model_expansions/scope.py +15 -15
  29. classiq/model_expansions/scope_initialization.py +3 -5
  30. classiq/open_library/functions/discrete_sine_cosine_transform.py +8 -2
  31. classiq/open_library/functions/grover.py +1 -1
  32. classiq/open_library/functions/modular_exponentiation.py +8 -2
  33. classiq/open_library/functions/state_preparation.py +23 -13
  34. classiq/open_library/functions/swap_test.py +1 -2
  35. classiq/open_library/functions/variational.py +1 -2
  36. classiq/qmod/builtins/__init__.py +1 -1
  37. classiq/qmod/builtins/operations.py +51 -0
  38. classiq/qmod/native/pretty_printer.py +9 -1
  39. classiq/qmod/pretty_print/pretty_printer.py +12 -1
  40. classiq/qmod/qmod_variable.py +38 -38
  41. classiq/qmod/quantum_function.py +4 -4
  42. classiq/qmod/semantics/annotation/__init__.py +0 -0
  43. classiq/qmod/semantics/annotation/call_annotation.py +92 -0
  44. classiq/qmod/semantics/lambdas.py +25 -0
  45. classiq/qmod/semantics/static_semantics_visitor.py +8 -46
  46. classiq/qmod/utilities.py +16 -0
  47. {classiq-0.64.0.dist-info → classiq-0.65.1.dist-info}/METADATA +1 -1
  48. {classiq-0.64.0.dist-info → classiq-0.65.1.dist-info}/RECORD +50 -45
  49. classiq/qmod/semantics/annotation.py +0 -36
  50. /classiq/qmod/semantics/{qstruct_annotator.py → annotation/qstruct_annotator.py} +0 -0
  51. {classiq-0.64.0.dist-info → classiq-0.65.1.dist-info}/WHEEL +0 -0
@@ -1,3 +1,4 @@
1
+ import ast
1
2
  from abc import abstractmethod
2
3
  from collections import defaultdict
3
4
  from collections.abc import Sequence
@@ -5,8 +6,7 @@ from contextlib import nullcontext
5
6
  from functools import singledispatchmethod
6
7
  from typing import Any, Optional, cast
7
8
 
8
- import numpy as np
9
- from numpy.random import permutation
9
+ import sympy
10
10
  from pydantic import ValidationError
11
11
 
12
12
  from classiq.interface.exceptions import (
@@ -25,11 +25,7 @@ from classiq.interface.model.handle_binding import (
25
25
  )
26
26
  from classiq.interface.model.model import MAIN_FUNCTION_NAME, Model
27
27
  from classiq.interface.model.native_function_definition import NativeFunctionDefinition
28
- from classiq.interface.model.quantum_function_call import (
29
- QuantumFunctionCall,
30
- )
31
28
  from classiq.interface.model.quantum_function_declaration import (
32
- NamedParamsQuantumFunctionDeclaration,
33
29
  QuantumFunctionDeclaration,
34
30
  )
35
31
  from classiq.interface.model.quantum_lambda_function import (
@@ -41,8 +37,6 @@ from classiq.interface.model.quantum_statement import QuantumStatement
41
37
  from classiq.model_expansions.closure import (
42
38
  Closure,
43
39
  FunctionClosure,
44
- GenerativeClosure,
45
- GenerativeFunctionClosure,
46
40
  )
47
41
  from classiq.model_expansions.debug_flag import debug_mode
48
42
  from classiq.model_expansions.evaluators.classical_expression import (
@@ -54,32 +48,24 @@ from classiq.model_expansions.function_builder import (
54
48
  OperationBuilder,
55
49
  OperationContext,
56
50
  )
57
- from classiq.model_expansions.generative_functions import emit_generative_statements
58
51
  from classiq.model_expansions.scope import Evaluated, QuantumSymbol, Scope
59
52
  from classiq.model_expansions.scope_initialization import (
60
53
  add_constants_to_scope,
61
54
  add_entry_point_params_to_scope,
62
- add_functions_to_scope,
63
- add_generative_functions_to_scope,
64
55
  get_main_renamer,
65
56
  init_builtin_types,
66
57
  init_top_level_scope,
67
58
  )
68
59
  from classiq.model_expansions.utils.counted_name_allocator import CountedNameAllocator
60
+ from classiq.model_expansions.visitors.variable_references import VarRefCollector
69
61
  from classiq.qmod.builtins.enums import BUILTIN_ENUM_DECLARATIONS
70
- from classiq.qmod.builtins.functions import permute
71
62
  from classiq.qmod.builtins.structs import BUILTIN_STRUCT_DECLARATIONS
72
- from classiq.qmod.model_state_container import QMODULE, ModelStateContainer
73
- from classiq.qmod.quantum_function import GenerativeQFunc
63
+ from classiq.qmod.model_state_container import QMODULE
74
64
  from classiq.qmod.semantics.error_manager import ErrorManager
75
65
 
76
66
 
77
67
  class BaseInterpreter:
78
- def __init__(
79
- self,
80
- model: Model,
81
- generative_functions: Optional[list[GenerativeQFunc]] = None,
82
- ) -> None:
68
+ def __init__(self, model: Model) -> None:
83
69
  self._model = model
84
70
  self._top_level_scope = Scope()
85
71
  self._counted_name_allocator = CountedNameAllocator()
@@ -89,7 +75,7 @@ class BaseInterpreter:
89
75
  self._expanded_functions: dict[str, NativeFunctionDefinition] = {}
90
76
 
91
77
  init_builtin_types()
92
- init_top_level_scope(model, generative_functions or [], self._top_level_scope)
78
+ init_top_level_scope(model, self._top_level_scope)
93
79
  self._functions_compilation_metadata: dict[str, CompilationMetadata] = dict(
94
80
  self._model.functions_compilation_metadata
95
81
  )
@@ -99,33 +85,13 @@ class BaseInterpreter:
99
85
  self._counted_name_allocator = CountedNameAllocator()
100
86
  self._error_manager: ErrorManager = ErrorManager()
101
87
 
102
- @property
103
- def is_shallow(self) -> bool:
104
- return False
105
-
106
88
  def get_main_renamer(self) -> Optional[ExpressionRenamer]:
107
89
  return get_main_renamer(self._get_function_declarations())
108
90
 
109
91
  def _expand_main_func(self) -> None:
110
- main_func = self._top_level_scope[MAIN_FUNCTION_NAME].value
111
- closure_constructor: Any
112
- if isinstance(main_func, GenerativeFunctionClosure):
113
- closure_constructor = GenerativeFunctionClosure
114
- extra_args = {
115
- "generative_blocks": {"body": main_func.generative_blocks["body"]}
116
- }
117
- else:
118
- closure_constructor = FunctionClosure
119
- extra_args = {"body": main_func.body}
120
- main_closure = closure_constructor.create(
121
- name=main_func.name,
122
- positional_arg_declarations=main_func.positional_arg_declarations,
123
- scope=Scope(parent=self._top_level_scope),
124
- expr_renamer=self.get_main_renamer(),
125
- _depth=0,
126
- **extra_args,
92
+ main_closure = self._get_main_closure(
93
+ self._top_level_scope[MAIN_FUNCTION_NAME].value
127
94
  )
128
-
129
95
  add_entry_point_params_to_scope(
130
96
  main_closure.positional_arg_declarations, main_closure
131
97
  )
@@ -134,6 +100,16 @@ class BaseInterpreter:
134
100
  self._builder.create_definition(cast(FunctionContext, context))
135
101
  )
136
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
+
137
113
  def expand(self) -> Model:
138
114
  try:
139
115
  with self._error_manager.call("main"):
@@ -178,49 +154,28 @@ class BaseInterpreter:
178
154
 
179
155
  @evaluate.register
180
156
  def evaluate_classical_expression(self, expression: Expression) -> Evaluated:
181
- return evaluate_classical_expression(expression, self._builder.current_scope)
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
182
168
 
183
169
  @evaluate.register
184
170
  def evaluate_identifier(self, identifier: str) -> Evaluated:
185
171
  return self._builder.current_scope[identifier]
186
172
 
187
173
  @evaluate.register
188
- def evaluate_lambda(self, function: QuantumLambdaFunction) -> Evaluated:
189
- renamed_params = [
190
- param.rename(function.pos_rename_params[idx])
191
- for idx, param in enumerate(function.func_decl.positional_arg_declarations)
192
- ]
193
- func_decl = NamedParamsQuantumFunctionDeclaration(
194
- name=self._counted_name_allocator.allocate(
195
- function.func_decl.name or "<lambda>"
196
- ),
197
- positional_arg_declarations=renamed_params,
198
- )
199
-
200
- closure_class: type[FunctionClosure]
201
- extra_args: dict[str, Any]
202
- if function.is_generative():
203
- closure_class = GenerativeFunctionClosure
204
- extra_args = {
205
- "generative_blocks": {
206
- "body": GenerativeQFunc(function.py_callable, func_decl),
207
- }
208
- }
209
- else:
210
- closure_class = FunctionClosure
211
- extra_args = {}
174
+ def _evaluate_lambda(self, function: QuantumLambdaFunction) -> Evaluated:
175
+ return self.evaluate_lambda(function)
212
176
 
213
- return Evaluated(
214
- value=closure_class.create(
215
- name=func_decl.name,
216
- positional_arg_declarations=func_decl.positional_arg_declarations,
217
- body=function.body,
218
- scope=Scope(parent=self._builder.current_scope),
219
- is_lambda=True,
220
- **extra_args,
221
- ),
222
- defining_function=self._builder.current_function,
223
- )
177
+ def evaluate_lambda(self, function: QuantumLambdaFunction) -> Evaluated:
178
+ raise NotImplementedError
224
179
 
225
180
  @evaluate.register
226
181
  def evaluate_handle_binding(self, handle_binding: HandleBinding) -> Evaluated:
@@ -285,34 +240,14 @@ class BaseInterpreter:
285
240
  ):
286
241
  captured_vars = self._top_level_scope[func_def.name].value.captured_vars
287
242
  operation.captured_vars.update(captured_vars)
288
- elif isinstance(operation, FunctionClosure) and operation.name == "permute":
289
- # special expansion since permute is generative
290
- self._expand_permute()
291
- elif isinstance(operation, GenerativeClosure):
292
- args = [
293
- self.evaluate(param.name)
294
- for param in operation.positional_arg_declarations
295
- ]
296
- emit_generative_statements(self, operation, args)
297
243
  else:
298
- for block, block_body in operation.blocks.items():
299
- self._expand_block(block_body, block)
244
+ self._expand_body(operation)
300
245
 
301
246
  return context
302
247
 
303
- def _expand_permute(self) -> None:
304
- functions = self.evaluate("functions").as_type(list)
305
- functions_permutation = permutation(np.array(range(len(functions))))
306
- calls: list[QuantumFunctionCall] = []
307
- for function_index in functions_permutation:
308
- permute_call = QuantumFunctionCall(
309
- function=OperandIdentifier(
310
- name="functions", index=Expression(expr=f"{function_index}")
311
- )
312
- )
313
- permute_call.set_func_decl(permute.func_decl)
314
- calls.append(permute_call)
315
- self._expand_block(calls, "body")
248
+ def _expand_body(self, operation: Closure) -> None:
249
+ for block, block_body in operation.blocks.items():
250
+ self._expand_block(block_body, block)
316
251
 
317
252
  def _get_function_declarations(self) -> Sequence[QuantumFunctionDeclaration]:
318
253
  return [
@@ -326,39 +261,3 @@ class BaseInterpreter:
326
261
 
327
262
  def add_constant(self, constant: Constant) -> None:
328
263
  add_constants_to_scope([constant], self._top_level_scope)
329
-
330
- def update_declarative_functions(
331
- self,
332
- functions: dict[str, NativeFunctionDefinition],
333
- qmodule: ModelStateContainer,
334
- ) -> None:
335
- add_functions_to_scope(list(functions.values()), self._top_level_scope)
336
- for dec_func_name in functions:
337
- if dec_func_name in qmodule.functions_compilation_metadata:
338
- self._functions_compilation_metadata[dec_func_name] = (
339
- qmodule.functions_compilation_metadata[dec_func_name]
340
- )
341
-
342
- def update_generative_functions(
343
- self, generative_functions: dict[str, GenerativeQFunc]
344
- ) -> None:
345
- add_generative_functions_to_scope(
346
- list(generative_functions.values()), self._top_level_scope
347
- )
348
- for name, gen_func in generative_functions.items():
349
- if gen_func.compilation_metadata is not None:
350
- self._functions_compilation_metadata[name] = (
351
- gen_func.compilation_metadata
352
- )
353
-
354
- def add_purely_declarative_function(self, function: FunctionClosure) -> None:
355
- functions_to_add = [function.name] + QMODULE.function_dependencies[
356
- function.name
357
- ]
358
- for func in functions_to_add:
359
- if func not in self._expanded_functions and func in QMODULE.native_defs:
360
- self._expanded_functions[func] = QMODULE.native_defs[func]
361
- if func in QMODULE.functions_compilation_metadata:
362
- self._expanded_functions_compilation_metadata[func] = (
363
- QMODULE.functions_compilation_metadata[func]
364
- )
@@ -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)
@@ -1,21 +1,36 @@
1
1
  from functools import singledispatchmethod
2
+ from typing import Any
2
3
 
4
+ import numpy as np
5
+ from numpy.random import permutation
6
+
7
+ from classiq.interface.generator.expressions.expression import Expression
3
8
  from classiq.interface.generator.functions.builtins.internal_operators import (
4
9
  CONTROL_OPERATOR_NAME,
5
10
  INVERT_OPERATOR_NAME,
6
11
  WITHIN_APPLY_NAME,
7
12
  )
13
+ from classiq.interface.model.allocate import Allocate
8
14
  from classiq.interface.model.bind_operation import BindOperation
9
15
  from classiq.interface.model.classical_if import ClassicalIf
10
16
  from classiq.interface.model.control import Control
11
17
  from classiq.interface.model.inplace_binary_operation import InplaceBinaryOperation
12
18
  from classiq.interface.model.invert import Invert
19
+ from classiq.interface.model.model import Model
20
+ from classiq.interface.model.native_function_definition import NativeFunctionDefinition
13
21
  from classiq.interface.model.phase_operation import PhaseOperation
14
22
  from classiq.interface.model.power import Power
15
23
  from classiq.interface.model.quantum_expressions.quantum_expression import (
16
24
  QuantumAssignmentOperation,
17
25
  )
18
26
  from classiq.interface.model.quantum_function_call import QuantumFunctionCall
27
+ from classiq.interface.model.quantum_function_declaration import (
28
+ NamedParamsQuantumFunctionDeclaration,
29
+ )
30
+ from classiq.interface.model.quantum_lambda_function import (
31
+ OperandIdentifier,
32
+ QuantumLambdaFunction,
33
+ )
19
34
  from classiq.interface.model.quantum_statement import QuantumStatement
20
35
  from classiq.interface.model.repeat import Repeat
21
36
  from classiq.interface.model.variable_declaration_statement import (
@@ -23,6 +38,13 @@ from classiq.interface.model.variable_declaration_statement import (
23
38
  )
24
39
  from classiq.interface.model.within_apply_operation import WithinApply
25
40
 
41
+ from classiq.model_expansions.closure import (
42
+ Closure,
43
+ FunctionClosure,
44
+ GenerativeClosure,
45
+ GenerativeFunctionClosure,
46
+ )
47
+ from classiq.model_expansions.generative_functions import emit_generative_statements
26
48
  from classiq.model_expansions.interpreters.base_interpreter import BaseInterpreter
27
49
  from classiq.model_expansions.quantum_operations import (
28
50
  BindEmitter,
@@ -32,21 +54,77 @@ from classiq.model_expansions.quantum_operations import (
32
54
  VariableDeclarationStatementEmitter,
33
55
  )
34
56
  from classiq.model_expansions.quantum_operations.shallow_emitter import ShallowEmitter
57
+ from classiq.model_expansions.scope import Evaluated, Scope
58
+ from classiq.model_expansions.scope_initialization import (
59
+ add_functions_to_scope,
60
+ add_generative_functions_to_scope,
61
+ )
62
+ from classiq.qmod.builtins.functions import permute
63
+ from classiq.qmod.model_state_container import QMODULE, ModelStateContainer
64
+ from classiq.qmod.quantum_function import GenerativeQFunc
35
65
 
36
66
 
37
67
  class GenerativeInterpreter(BaseInterpreter):
38
- @property
39
- def is_shallow(self) -> bool:
40
- return True
68
+ def __init__(
69
+ self,
70
+ model: Model,
71
+ generative_functions: list[GenerativeQFunc],
72
+ ) -> None:
73
+ super().__init__(model)
74
+ add_generative_functions_to_scope(generative_functions, self._top_level_scope)
75
+
76
+ def evaluate_lambda(self, function: QuantumLambdaFunction) -> Evaluated:
77
+ renamed_params = [
78
+ param.rename(function.pos_rename_params[idx])
79
+ for idx, param in enumerate(function.func_decl.positional_arg_declarations)
80
+ ]
81
+ func_decl = NamedParamsQuantumFunctionDeclaration(
82
+ name=self._counted_name_allocator.allocate(
83
+ function.func_decl.name or "<lambda>"
84
+ ),
85
+ positional_arg_declarations=renamed_params,
86
+ )
87
+
88
+ closure_class: type[FunctionClosure]
89
+ extra_args: dict[str, Any]
90
+ if function.is_generative():
91
+ closure_class = GenerativeFunctionClosure
92
+ extra_args = {
93
+ "generative_blocks": {
94
+ "body": GenerativeQFunc(function.py_callable, func_decl),
95
+ }
96
+ }
97
+ else:
98
+ closure_class = FunctionClosure
99
+ extra_args = {}
100
+
101
+ return Evaluated(
102
+ value=closure_class.create(
103
+ name=func_decl.name,
104
+ positional_arg_declarations=func_decl.positional_arg_declarations,
105
+ body=function.body,
106
+ scope=Scope(parent=self._builder.current_scope),
107
+ is_lambda=True,
108
+ **extra_args,
109
+ ),
110
+ defining_function=self._builder.current_function,
111
+ )
41
112
 
42
113
  @singledispatchmethod
43
114
  def emit(self, statement: QuantumStatement) -> None: # type:ignore[override]
44
115
  raise NotImplementedError(f"Cannot emit {statement!r}")
45
116
 
46
117
  @emit.register
118
+ def _emit_quantum_function_call(self, call: QuantumFunctionCall) -> None:
119
+ self.emit_quantum_function_call(call)
120
+
47
121
  def emit_quantum_function_call(self, call: QuantumFunctionCall) -> None:
48
122
  QuantumFunctionCallEmitter(self).emit(call)
49
123
 
124
+ @emit.register
125
+ def emit_allocate(self, allocate: Allocate) -> None:
126
+ ShallowEmitter(self, "allocate", components=["size", "target"]).emit(allocate)
127
+
50
128
  @emit.register
51
129
  def emit_bind(self, bind: BindOperation) -> None:
52
130
  BindEmitter(self).emit(bind)
@@ -106,3 +184,66 @@ class GenerativeInterpreter(BaseInterpreter):
106
184
  @emit.register
107
185
  def emit_phase(self, phase: PhaseOperation) -> None:
108
186
  ShallowEmitter(self, "phase", components=["expression", "theta"]).emit(phase)
187
+
188
+ def _expand_body(self, operation: Closure) -> None:
189
+ if isinstance(operation, FunctionClosure) and operation.name == "permute":
190
+ # special expansion since permute is generative
191
+ self._expand_permute()
192
+ elif isinstance(operation, GenerativeClosure):
193
+ args = [
194
+ self.evaluate(param.name)
195
+ for param in operation.positional_arg_declarations
196
+ ]
197
+ emit_generative_statements(self, operation, args)
198
+ else:
199
+ super()._expand_body(operation)
200
+
201
+ def _expand_permute(self) -> None:
202
+ functions = self.evaluate("functions").as_type(list)
203
+ functions_permutation = permutation(np.array(range(len(functions))))
204
+ calls: list[QuantumFunctionCall] = []
205
+ for function_index in functions_permutation:
206
+ permute_call = QuantumFunctionCall(
207
+ function=OperandIdentifier(
208
+ name="functions", index=Expression(expr=f"{function_index}")
209
+ )
210
+ )
211
+ permute_call.set_func_decl(permute.func_decl)
212
+ calls.append(permute_call)
213
+ self._expand_block(calls, "body")
214
+
215
+ def update_generative_functions(
216
+ self, generative_functions: dict[str, GenerativeQFunc]
217
+ ) -> None:
218
+ add_generative_functions_to_scope(
219
+ list(generative_functions.values()), self._top_level_scope
220
+ )
221
+ for name, gen_func in generative_functions.items():
222
+ if gen_func.compilation_metadata is not None:
223
+ self._functions_compilation_metadata[name] = (
224
+ gen_func.compilation_metadata
225
+ )
226
+
227
+ def update_declarative_functions(
228
+ self,
229
+ functions: dict[str, NativeFunctionDefinition],
230
+ qmodule: ModelStateContainer,
231
+ ) -> None:
232
+ add_functions_to_scope(list(functions.values()), self._top_level_scope)
233
+ for dec_func_name in functions:
234
+ if dec_func_name in qmodule.functions_compilation_metadata:
235
+ self._functions_compilation_metadata[dec_func_name] = (
236
+ qmodule.functions_compilation_metadata[dec_func_name]
237
+ )
238
+
239
+ def add_purely_declarative_function(self, function: FunctionClosure) -> None:
240
+ functions_to_add = [function.name] + QMODULE.function_dependencies[
241
+ function.name
242
+ ]
243
+ for func in functions_to_add:
244
+ if func not in self._expanded_functions and func in QMODULE.native_defs:
245
+ self._expanded_functions[func] = QMODULE.native_defs[func]
246
+ if func in QMODULE.functions_compilation_metadata:
247
+ self._expanded_functions_compilation_metadata[func] = (
248
+ QMODULE.functions_compilation_metadata[func]
249
+ )
@@ -8,9 +8,6 @@ from typing import (
8
8
  )
9
9
 
10
10
  from classiq.interface.debug_info.debug_info import FunctionDebugInfo
11
- from classiq.interface.generator.functions.port_declaration import (
12
- PortDeclarationDirection,
13
- )
14
11
  from classiq.interface.generator.generated_circuit_data import OperationLevel
15
12
  from classiq.interface.model.classical_parameter_declaration import (
16
13
  ClassicalParameterDeclaration,
@@ -23,10 +20,6 @@ from classiq.interface.model.quantum_function_declaration import (
23
20
  NamedParamsQuantumFunctionDeclaration,
24
21
  PositionalArg,
25
22
  )
26
- from classiq.interface.model.quantum_lambda_function import (
27
- OperandIdentifier,
28
- QuantumLambdaFunction,
29
- )
30
23
  from classiq.interface.model.quantum_statement import QuantumStatement
31
24
  from classiq.interface.model.variable_declaration_statement import (
32
25
  VariableDeclarationStatement,
@@ -35,7 +28,7 @@ from classiq.interface.model.variable_declaration_statement import (
35
28
  from classiq.model_expansions.capturing.captured_vars import (
36
29
  validate_args_are_not_propagated,
37
30
  )
38
- from classiq.model_expansions.closure import FunctionClosure, GenerativeClosure
31
+ from classiq.model_expansions.closure import FunctionClosure
39
32
  from classiq.model_expansions.evaluators.argument_types import (
40
33
  add_information_from_output_arguments,
41
34
  )
@@ -52,7 +45,6 @@ from classiq.model_expansions.quantum_operations.emitter import (
52
45
  from classiq.model_expansions.scope import Evaluated, QuantumSymbol, Scope
53
46
  from classiq.model_expansions.transformers.var_splitter import VarSplitter
54
47
  from classiq.qmod.builtins.functions import allocate, free
55
- from classiq.qmod.model_state_container import QMODULE
56
48
 
57
49
  if TYPE_CHECKING:
58
50
  from classiq.model_expansions.interpreters.base_interpreter import BaseInterpreter
@@ -105,45 +97,16 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
105
97
  function, evaluated_args
106
98
  )
107
99
  new_positional_arg_decls = new_declaration.positional_arg_declarations
108
- is_atomic = function.is_atomic
109
- if (
110
- self._interpreter.is_shallow
111
- and self._is_function_purely_declarative(function)
112
- and self._are_args_purely_declarative(args)
113
- ):
100
+ if not self.should_expand_function(function, evaluated_args):
114
101
  is_atomic = True
115
- self._interpreter.add_purely_declarative_function(function)
116
- new_function_name = function.name
117
- if not is_atomic: # perform monomorphization per interpreted parameters set
118
- self._add_params_to_scope(
119
- new_positional_arg_decls, evaluated_args, function
102
+ new_declaration = self._expanded_functions_by_name.get(
103
+ function.name, new_declaration
120
104
  )
121
- context = self._expand_operation(
122
- function.with_new_declaration(new_declaration)
105
+ else:
106
+ is_atomic = False
107
+ new_declaration = self._expand_function(
108
+ evaluated_args, new_declaration, function
123
109
  )
124
- function_context = cast(FunctionContext, context)
125
- closure_id = function_context.closure.closure_id
126
- function_def = self._expanded_functions.get(closure_id)
127
- if function_def is None:
128
- function_def = self._create_function_definition(function_context)
129
- self._expanded_functions[closure_id] = function_def
130
- self._top_level_scope[function_def.name] = Evaluated(
131
- value=function_context.closure.with_new_declaration(function_def)
132
- )
133
- compilation_metadata = self._functions_compilation_metadata.get(
134
- function.name
135
- )
136
- if compilation_metadata is not None:
137
- self._expanded_functions_compilation_metadata[function_def.name] = (
138
- compilation_metadata
139
- )
140
- else:
141
- self._expanded_functions_compilation_metadata[
142
- function_def.name
143
- ].occurrences_number += 1
144
-
145
- new_declaration = function_def
146
- new_function_name = function_def.name
147
110
 
148
111
  new_positional_args = self._get_new_positional_args(
149
112
  evaluated_args, is_atomic, new_positional_arg_decls
@@ -154,8 +117,7 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
154
117
  validate_args_are_not_propagated(new_positional_args, captured_args)
155
118
  new_positional_args.extend(captured_args)
156
119
  new_call = QuantumFunctionCall(
157
- function=new_function_name,
158
- positional_args=new_positional_args,
120
+ function=new_declaration.name, positional_args=new_positional_args
159
121
  )
160
122
  is_allocate_or_free = (
161
123
  new_call.func_name == allocate.func_decl.name
@@ -186,6 +148,40 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
186
148
  new_call.set_func_decl(new_declaration)
187
149
  return new_call
188
150
 
151
+ def should_expand_function(
152
+ self, function: FunctionClosure, args: list[Evaluated]
153
+ ) -> bool:
154
+ return not function.is_atomic
155
+
156
+ def _expand_function(
157
+ self,
158
+ args: list[Evaluated],
159
+ decl: NamedParamsQuantumFunctionDeclaration,
160
+ function: FunctionClosure,
161
+ ) -> NamedParamsQuantumFunctionDeclaration:
162
+ self._add_params_to_scope(decl.positional_arg_declarations, args, function)
163
+ context = self._expand_operation(function.with_new_declaration(decl))
164
+ function_context = cast(FunctionContext, context)
165
+ closure_id = function_context.closure.closure_id
166
+ if closure_id in self._expanded_functions:
167
+ function_def = self._expanded_functions[closure_id]
168
+ self._expanded_functions_compilation_metadata[
169
+ function_def.name
170
+ ].occurrences_number += 1
171
+ return function_def
172
+
173
+ function_def = self._create_function_definition(function_context)
174
+ self._expanded_functions[closure_id] = function_def
175
+ self._top_level_scope[function_def.name] = Evaluated(
176
+ value=function_context.closure.with_new_declaration(function_def)
177
+ )
178
+ compilation_metadata = self._functions_compilation_metadata.get(function.name)
179
+ if compilation_metadata is not None:
180
+ self._expanded_functions_compilation_metadata[function_def.name] = (
181
+ compilation_metadata
182
+ )
183
+ return function_def
184
+
189
185
  def _create_function_definition(
190
186
  self, function_context: FunctionContext
191
187
  ) -> NativeFunctionDefinition:
@@ -268,47 +264,3 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
268
264
  evaluated_args,
269
265
  ),
270
266
  )
271
-
272
- def _is_function_purely_declarative(self, function: FunctionClosure) -> bool:
273
- if function.name not in QMODULE.native_defs:
274
- return False
275
-
276
- if isinstance(function, GenerativeClosure):
277
- return False
278
-
279
- if any(
280
- not param.quantum_type.is_instantiated
281
- for param in function.positional_arg_declarations
282
- if isinstance(param, PortDeclaration)
283
- and param.direction == PortDeclarationDirection.Output
284
- ):
285
- return False
286
-
287
- dependencies = QMODULE.function_dependencies[function.name]
288
- return self._are_identifiers_purely_declarative(dependencies)
289
-
290
- def _are_args_purely_declarative(self, args: list[ArgValue]) -> bool:
291
- if any(
292
- isinstance(arg, (OperandIdentifier, QuantumLambdaFunction))
293
- or (
294
- isinstance(arg, list)
295
- and any(
296
- isinstance(item, (OperandIdentifier, QuantumLambdaFunction))
297
- for item in arg
298
- )
299
- )
300
- for arg in args
301
- ):
302
- return False
303
-
304
- dependencies = [arg for arg in args if isinstance(arg, str)]
305
- dependencies += [
306
- cast(str, item) for arg in args if isinstance(arg, list) for item in arg
307
- ]
308
- return self._are_identifiers_purely_declarative(dependencies)
309
-
310
- def _are_identifiers_purely_declarative(self, dependencies: list[str]) -> bool:
311
- return not any(
312
- isinstance(self._current_scope[dep].value, GenerativeClosure)
313
- for dep in dependencies
314
- )