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
@@ -0,0 +1,249 @@
1
+ from functools import singledispatchmethod
2
+ from typing import Any
3
+
4
+ import numpy as np
5
+ from numpy.random import permutation
6
+
7
+ from classiq.interface.generator.expressions.expression import Expression
8
+ from classiq.interface.generator.functions.builtins.internal_operators import (
9
+ CONTROL_OPERATOR_NAME,
10
+ INVERT_OPERATOR_NAME,
11
+ WITHIN_APPLY_NAME,
12
+ )
13
+ from classiq.interface.model.allocate import Allocate
14
+ from classiq.interface.model.bind_operation import BindOperation
15
+ from classiq.interface.model.classical_if import ClassicalIf
16
+ from classiq.interface.model.control import Control
17
+ from classiq.interface.model.inplace_binary_operation import InplaceBinaryOperation
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
21
+ from classiq.interface.model.phase_operation import PhaseOperation
22
+ from classiq.interface.model.power import Power
23
+ from classiq.interface.model.quantum_expressions.quantum_expression import (
24
+ QuantumAssignmentOperation,
25
+ )
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
+ )
34
+ from classiq.interface.model.quantum_statement import QuantumStatement
35
+ from classiq.interface.model.repeat import Repeat
36
+ from classiq.interface.model.variable_declaration_statement import (
37
+ VariableDeclarationStatement,
38
+ )
39
+ from classiq.interface.model.within_apply_operation import WithinApply
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
48
+ from classiq.model_expansions.interpreters.base_interpreter import BaseInterpreter
49
+ from classiq.model_expansions.quantum_operations import (
50
+ BindEmitter,
51
+ ClassicalIfEmitter,
52
+ QuantumFunctionCallEmitter,
53
+ RepeatEmitter,
54
+ VariableDeclarationStatementEmitter,
55
+ )
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
65
+
66
+
67
+ class GenerativeInterpreter(BaseInterpreter):
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
+ )
112
+
113
+ @singledispatchmethod
114
+ def emit(self, statement: QuantumStatement) -> None: # type:ignore[override]
115
+ raise NotImplementedError(f"Cannot emit {statement!r}")
116
+
117
+ @emit.register
118
+ def _emit_quantum_function_call(self, call: QuantumFunctionCall) -> None:
119
+ self.emit_quantum_function_call(call)
120
+
121
+ def emit_quantum_function_call(self, call: QuantumFunctionCall) -> None:
122
+ QuantumFunctionCallEmitter(self).emit(call)
123
+
124
+ @emit.register
125
+ def emit_allocate(self, allocate: Allocate) -> None:
126
+ ShallowEmitter(self, "allocate", components=["size", "target"]).emit(allocate)
127
+
128
+ @emit.register
129
+ def emit_bind(self, bind: BindOperation) -> None:
130
+ BindEmitter(self).emit(bind)
131
+
132
+ @emit.register
133
+ def emit_quantum_assignment_operation(self, op: QuantumAssignmentOperation) -> None:
134
+ ShallowEmitter(
135
+ self, "assignment_operation", components=["expression", "result_var"]
136
+ ).emit(op)
137
+
138
+ @emit.register
139
+ def emit_inplace_binary_operation(self, op: InplaceBinaryOperation) -> None:
140
+ ShallowEmitter(
141
+ self, "inplace_binary_operation", components=["target", "value"]
142
+ ).emit(op)
143
+
144
+ @emit.register
145
+ def emit_variable_declaration(
146
+ self, variable_declaration: VariableDeclarationStatement
147
+ ) -> None:
148
+ VariableDeclarationStatementEmitter(self).emit(variable_declaration)
149
+
150
+ @emit.register
151
+ def emit_classical_if(self, classical_if: ClassicalIf) -> None:
152
+ ClassicalIfEmitter(self).emit(classical_if)
153
+
154
+ @emit.register
155
+ def emit_within_apply(self, within_apply: WithinApply) -> None:
156
+ ShallowEmitter(
157
+ self,
158
+ WITHIN_APPLY_NAME,
159
+ components=["within", "apply", "compute", "action"],
160
+ ).emit(within_apply)
161
+
162
+ @emit.register
163
+ def emit_invert(self, invert: Invert) -> None:
164
+ ShallowEmitter(self, INVERT_OPERATOR_NAME, components=["body"]).emit(invert)
165
+
166
+ @emit.register
167
+ def emit_repeat(self, repeat: Repeat) -> None:
168
+ RepeatEmitter(self).emit(repeat)
169
+
170
+ @emit.register
171
+ def emit_control(self, control: Control) -> None:
172
+ ShallowEmitter(
173
+ self,
174
+ CONTROL_OPERATOR_NAME,
175
+ components=["expression", "body", "else_block"],
176
+ ).emit(control)
177
+
178
+ @emit.register
179
+ def emit_power(self, power: Power) -> None:
180
+ ShallowEmitter(self, CONTROL_OPERATOR_NAME, components=["power", "body"]).emit(
181
+ power
182
+ )
183
+
184
+ @emit.register
185
+ def emit_phase(self, phase: PhaseOperation) -> None:
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
+ )
@@ -1,98 +1,7 @@
1
- from abc import ABC, abstractmethod
2
- from enum import IntEnum
3
- from typing import ClassVar, Generic, Optional, TypeVar
1
+ from typing import Optional
4
2
 
5
3
  from classiq.interface.generator.expressions.handle_identifier import HandleIdentifier
6
4
  from classiq.interface.generator.functions.classical_type import QmodPyObject
7
- from classiq.interface.generator.types.enum_declaration import EnumDeclaration
8
- from classiq.interface.generator.types.qstruct_declaration import QStructDeclaration
9
- from classiq.interface.generator.types.struct_declaration import StructDeclaration
10
-
11
- from classiq.qmod.builtins.enums import BUILTIN_ENUM_DECLARATIONS
12
- from classiq.qmod.builtins.structs import BUILTIN_STRUCT_DECLARATIONS
13
- from classiq.qmod.model_state_container import QMODULE
14
-
15
- DeclarationType = TypeVar(
16
- "DeclarationType", EnumDeclaration, StructDeclaration, QStructDeclaration
17
- )
18
-
19
-
20
- class TypeTable(Generic[DeclarationType], ABC):
21
- def __init__(self, user_types: list[DeclarationType]) -> None:
22
- self._all_types: dict[str, DeclarationType] = self.builtins.copy()
23
- for t in user_types:
24
- assert t.name not in self._all_types # FIXME: issue user error (CAD-7856)
25
- self._all_types[t.name] = t
26
-
27
- @property
28
- @abstractmethod
29
- def builtins(self) -> dict[str, DeclarationType]:
30
- pass
31
-
32
- def __getitem__(self, key: str) -> DeclarationType:
33
- return self._all_types[key]
34
-
35
- def __setitem__(self, key: str, value: DeclarationType) -> None:
36
- self._all_types[key] = value
37
-
38
- def __contains__(self, key: str) -> bool:
39
- return key in self._all_types
40
-
41
- def all_types(self) -> dict[str, DeclarationType]:
42
- return self._all_types
43
-
44
-
45
- class EnumTable(TypeTable[EnumDeclaration]):
46
- def __init__(self, user_types: list[EnumDeclaration]) -> None:
47
- super().__init__(user_types)
48
-
49
- @property
50
- def builtins(self) -> dict[str, EnumDeclaration]:
51
- return BUILTIN_ENUM_DECLARATIONS
52
-
53
- @property
54
- def enums(self) -> dict[str, IntEnum]:
55
- return {
56
- enum_decl.name: enum_decl.create_enum()
57
- for enum_decl in self.all_types().values()
58
- }
59
-
60
-
61
- class StructTable(TypeTable[StructDeclaration]):
62
- @property
63
- def builtins(self) -> dict[str, StructDeclaration]:
64
- return BUILTIN_STRUCT_DECLARATIONS
65
-
66
-
67
- class QStructTable(TypeTable[QStructDeclaration]):
68
- @property
69
- def builtins(self) -> dict[str, QStructDeclaration]:
70
- return {}
71
-
72
-
73
- class SymbolTable:
74
- enum_table: ClassVar[EnumTable] = EnumTable([])
75
- type_table: ClassVar[StructTable] = StructTable([])
76
- qstruct_table: ClassVar[QStructTable] = QStructTable([])
77
-
78
- @classmethod
79
- def init_user_enums(cls, user_enums: list[EnumDeclaration]) -> None:
80
- cls.enum_table = EnumTable(user_enums)
81
- QMODULE.enum_decls = {enum_decl.name: enum_decl for enum_decl in user_enums}
82
-
83
- @classmethod
84
- def init_user_types(cls, user_types: list[StructDeclaration]) -> None:
85
- cls.type_table = StructTable(user_types)
86
- QMODULE.type_decls = {
87
- struct_decl.name: struct_decl for struct_decl in user_types
88
- }
89
-
90
- @classmethod
91
- def init_user_qstructs(cls, user_qstructs: list[QStructDeclaration]) -> None:
92
- cls.qstruct_table = QStructTable(user_qstructs)
93
- QMODULE.qstruct_decls = {
94
- qstruct_decl.name: qstruct_decl for qstruct_decl in user_qstructs
95
- }
96
5
 
97
6
 
98
7
  class HandleTable:
@@ -1,14 +1,5 @@
1
1
  from classiq.model_expansions.quantum_operations.bind import BindEmitter
2
2
  from classiq.model_expansions.quantum_operations.classicalif import ClassicalIfEmitter
3
- from classiq.model_expansions.quantum_operations.control import ControlEmitter
4
- from classiq.model_expansions.quantum_operations.inplace_binary_operation import (
5
- InplaceBinaryOperationEmitter,
6
- )
7
- from classiq.model_expansions.quantum_operations.invert import InvertEmitter
8
- from classiq.model_expansions.quantum_operations.power import PowerEmitter
9
- from classiq.model_expansions.quantum_operations.quantum_assignment_operation import (
10
- QuantumAssignmentOperationEmitter,
11
- )
12
3
  from classiq.model_expansions.quantum_operations.quantum_function_call import (
13
4
  QuantumFunctionCallEmitter,
14
5
  )
@@ -16,4 +7,3 @@ from classiq.model_expansions.quantum_operations.repeat import RepeatEmitter
16
7
  from classiq.model_expansions.quantum_operations.variable_decleration import (
17
8
  VariableDeclarationStatementEmitter,
18
9
  )
19
- from classiq.model_expansions.quantum_operations.within_apply import WithinApplyEmitter
@@ -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,14 +45,13 @@ 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
- from classiq.model_expansions.interpreter import Interpreter
50
+ from classiq.model_expansions.interpreters.base_interpreter import BaseInterpreter
59
51
 
60
52
 
61
53
  class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSplitter):
62
- def __init__(self, interpreter: "Interpreter") -> None:
54
+ def __init__(self, interpreter: "BaseInterpreter") -> None:
63
55
  Emitter.__init__(self, interpreter)
64
56
  VarSplitter.__init__(self, interpreter._builder.current_scope)
65
57
 
@@ -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
- )
@@ -0,0 +1,87 @@
1
+ from itertools import chain
2
+ from typing import TYPE_CHECKING, Generic
3
+
4
+ from classiq.interface.generator.functions.port_declaration import (
5
+ PortDeclarationDirection,
6
+ )
7
+ from classiq.interface.model.port_declaration import PortDeclaration
8
+
9
+ from classiq.model_expansions.closure import FunctionClosure, GenerativeClosure
10
+ from classiq.model_expansions.quantum_operations.call_emitter import CallEmitter
11
+ from classiq.model_expansions.quantum_operations.emitter import QuantumStatementT
12
+ from classiq.model_expansions.scope import Evaluated
13
+ from classiq.qmod.model_state_container import QMODULE
14
+
15
+ if TYPE_CHECKING:
16
+ from classiq.model_expansions.interpreters.generative_interpreter import (
17
+ GenerativeInterpreter,
18
+ )
19
+
20
+
21
+ class DeclarativeCallEmitter(
22
+ Generic[QuantumStatementT], CallEmitter[QuantumStatementT]
23
+ ):
24
+ _interpreter: "GenerativeInterpreter"
25
+
26
+ def __init__(self, interpreter: "GenerativeInterpreter") -> None:
27
+ super().__init__(interpreter)
28
+
29
+ def should_expand_function(
30
+ self, function: FunctionClosure, args: list[Evaluated]
31
+ ) -> bool:
32
+ if not super().should_expand_function(function, args):
33
+ return False
34
+
35
+ if self._is_function_purely_declarative(
36
+ function
37
+ ) and self._are_args_purely_declarative(args):
38
+ self._interpreter.add_purely_declarative_function(function)
39
+ return False
40
+
41
+ return True
42
+
43
+ def _is_function_purely_declarative(self, function: FunctionClosure) -> bool:
44
+ if function.name not in QMODULE.native_defs:
45
+ return False
46
+
47
+ if isinstance(function, GenerativeClosure):
48
+ return False
49
+
50
+ if any(
51
+ not param.quantum_type.is_instantiated
52
+ for param in function.positional_arg_declarations
53
+ if isinstance(param, PortDeclaration)
54
+ and param.direction == PortDeclarationDirection.Output
55
+ ):
56
+ return False
57
+
58
+ dependencies = QMODULE.function_dependencies[function.name]
59
+ return self._are_identifiers_purely_declarative(dependencies)
60
+
61
+ def _are_args_purely_declarative(self, args: list[Evaluated]) -> bool:
62
+ values = [arg.value for arg in args]
63
+ function_inputs: list[FunctionClosure] = list(
64
+ chain.from_iterable(
65
+ (
66
+ [arg]
67
+ if isinstance(arg, FunctionClosure)
68
+ else (
69
+ arg
70
+ if isinstance(arg, list)
71
+ and any(isinstance(item, FunctionClosure) for item in arg)
72
+ else []
73
+ )
74
+ )
75
+ for arg in values
76
+ )
77
+ )
78
+ if any(func.is_lambda for func in function_inputs):
79
+ return False
80
+ dependencies = [func.name for func in function_inputs if not func.is_lambda]
81
+ return self._are_identifiers_purely_declarative(dependencies)
82
+
83
+ def _are_identifiers_purely_declarative(self, dependencies: list[str]) -> bool:
84
+ return not any(
85
+ isinstance(self._current_scope[dep].value, GenerativeClosure)
86
+ for dep in dependencies
87
+ )
@@ -19,6 +19,7 @@ from classiq.interface.generator.expressions.expression import Expression
19
19
  from classiq.interface.generator.functions.port_declaration import (
20
20
  PortDeclarationDirection,
21
21
  )
22
+ from classiq.interface.helpers.pydantic_model_helpers import nameables_to_dict
22
23
  from classiq.interface.model.handle_binding import HandleBinding
23
24
  from classiq.interface.model.native_function_definition import NativeFunctionDefinition
24
25
  from classiq.interface.model.quantum_function_declaration import (
@@ -40,13 +41,13 @@ from classiq.model_expansions.visitors.variable_references import VarRefCollecto
40
41
  from classiq.qmod.quantum_function import GenerativeQFunc
41
42
 
42
43
  if TYPE_CHECKING:
43
- from classiq.model_expansions.interpreter import Interpreter
44
+ from classiq.model_expansions.interpreters.base_interpreter import BaseInterpreter
44
45
 
45
46
  QuantumStatementT = TypeVar("QuantumStatementT", bound=QuantumStatement)
46
47
 
47
48
 
48
49
  class Emitter(Generic[QuantumStatementT]):
49
- def __init__(self, interpreter: "Interpreter") -> None:
50
+ def __init__(self, interpreter: "BaseInterpreter") -> None:
50
51
  self._interpreter = interpreter
51
52
 
52
53
  self._machine_precision = self._interpreter._model.preferences.machine_precision
@@ -80,6 +81,10 @@ class Emitter(Generic[QuantumStatementT]):
80
81
  def _expanded_functions(self) -> dict[str, NativeFunctionDefinition]:
81
82
  return self._interpreter._expanded_functions
82
83
 
84
+ @property
85
+ def _expanded_functions_by_name(self) -> dict[str, NativeFunctionDefinition]:
86
+ return nameables_to_dict(list(self._interpreter._expanded_functions.values()))
87
+
83
88
  @property
84
89
  def _counted_name_allocator(self) -> CountedNameAllocator:
85
90
  return self._interpreter._counted_name_allocator