classiq 0.77.0__py3-none-any.whl → 0.78.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.
- classiq/applications/iqae/__init__.py +0 -0
- classiq/applications/iqae/iqae.py +207 -0
- classiq/execution/__init__.py +1 -1
- classiq/interface/_version.py +1 -1
- classiq/interface/applications/iqae/__init__.py +0 -0
- classiq/interface/applications/iqae/generic_iqae.py +222 -0
- classiq/interface/applications/iqae/iqae_result.py +45 -0
- classiq/interface/debug_info/debug_info.py +3 -0
- classiq/interface/executor/execution_result.py +1 -1
- classiq/interface/executor/user_budget.py +1 -1
- classiq/interface/generator/expressions/proxies/classical/any_classical_value.py +9 -3
- classiq/interface/generator/generated_circuit_data.py +18 -7
- classiq/interface/ide/visual_model.py +2 -0
- classiq/model_expansions/closure.py +1 -58
- classiq/model_expansions/evaluators/argument_types.py +1 -2
- classiq/model_expansions/function_builder.py +0 -45
- classiq/model_expansions/interpreters/base_interpreter.py +12 -14
- classiq/model_expansions/interpreters/frontend_generative_interpreter.py +0 -17
- classiq/model_expansions/quantum_operations/call_emitter.py +71 -32
- classiq/model_expansions/quantum_operations/emitter.py +6 -1
- classiq/model_expansions/quantum_operations/function_calls_cache.py +84 -0
- classiq/model_expansions/scope.py +20 -10
- classiq/model_expansions/transformers/type_qualifier_inference.py +183 -0
- classiq/model_expansions/utils/text_utils.py +4 -2
- classiq/model_expansions/visitors/symbolic_param_inference.py +1 -12
- classiq/qmod/builtins/classical_execution_primitives.py +1 -1
- classiq/qmod/qmod_variable.py +7 -4
- classiq/qmod/utilities.py +4 -0
- {classiq-0.77.0.dist-info → classiq-0.78.0.dist-info}/METADATA +1 -1
- {classiq-0.77.0.dist-info → classiq-0.78.0.dist-info}/RECORD +31 -25
- classiq/interface/executor/iqae_result.py +0 -17
- {classiq-0.77.0.dist-info → classiq-0.78.0.dist-info}/WHEEL +0 -0
@@ -1,22 +1,11 @@
|
|
1
1
|
import dataclasses
|
2
|
-
import
|
3
|
-
from collections.abc import Collection, Sequence
|
2
|
+
from collections.abc import Sequence
|
4
3
|
from dataclasses import dataclass, field
|
5
|
-
from functools import singledispatch
|
6
4
|
from typing import Any, Optional
|
7
5
|
|
8
6
|
from typing_extensions import Self
|
9
7
|
|
10
8
|
from classiq.interface.exceptions import ClassiqInternalExpansionError
|
11
|
-
from classiq.interface.generator.expressions.proxies.classical.classical_proxy import (
|
12
|
-
ClassicalProxy,
|
13
|
-
)
|
14
|
-
from classiq.interface.generator.expressions.proxies.classical.classical_struct_proxy import (
|
15
|
-
ClassicalStructProxy,
|
16
|
-
)
|
17
|
-
from classiq.interface.generator.expressions.proxies.classical.utils import (
|
18
|
-
get_proxy_type,
|
19
|
-
)
|
20
9
|
from classiq.interface.helpers.pydantic_model_helpers import nameables_to_dict
|
21
10
|
from classiq.interface.model.quantum_function_declaration import (
|
22
11
|
NamedParamsQuantumFunctionDeclaration,
|
@@ -27,10 +16,7 @@ from classiq.interface.model.quantum_statement import QuantumStatement
|
|
27
16
|
|
28
17
|
from classiq.model_expansions.capturing.captured_vars import CapturedVars
|
29
18
|
from classiq.model_expansions.scope import (
|
30
|
-
Evaluated,
|
31
|
-
QuantumSymbol,
|
32
19
|
Scope,
|
33
|
-
evaluated_to_str as evaluated_classical_param_to_str,
|
34
20
|
)
|
35
21
|
from classiq.qmod.builtins.functions import permute
|
36
22
|
from classiq.qmod.quantum_function import GenerativeQFunc
|
@@ -67,13 +53,6 @@ class FunctionClosure(Closure):
|
|
67
53
|
raise ClassiqInternalExpansionError
|
68
54
|
return self._depth
|
69
55
|
|
70
|
-
# creates a unique id for the function closure based on the arguments values.
|
71
|
-
# The closure is changing across the interpreter flow so it's closure_id may change
|
72
|
-
@property
|
73
|
-
def closure_id(self) -> str:
|
74
|
-
signature = _generate_closure_id(self.scope.data.values())
|
75
|
-
return f"{self.name}__{signature}"
|
76
|
-
|
77
56
|
@property
|
78
57
|
def body(self) -> Sequence[QuantumStatement]:
|
79
58
|
if self.name == permute.func_decl.name:
|
@@ -134,39 +113,3 @@ class FunctionClosure(Closure):
|
|
134
113
|
@dataclass(frozen=True)
|
135
114
|
class GenerativeFunctionClosure(GenerativeClosure, FunctionClosure):
|
136
115
|
pass
|
137
|
-
|
138
|
-
|
139
|
-
def _generate_closure_id(evaluated_args: Collection[Evaluated]) -> str:
|
140
|
-
args_signature = [
|
141
|
-
_evaluated_arg_to_str(eval_arg.value) for eval_arg in evaluated_args
|
142
|
-
]
|
143
|
-
return json.dumps(args_signature)
|
144
|
-
|
145
|
-
|
146
|
-
@singledispatch
|
147
|
-
def _evaluated_arg_to_str(arg: Any) -> str:
|
148
|
-
if isinstance(arg, str):
|
149
|
-
return arg
|
150
|
-
if isinstance(arg, QuantumSymbol):
|
151
|
-
return _evaluated_quantum_symbol_to_str(arg)
|
152
|
-
if isinstance(arg, FunctionClosure):
|
153
|
-
return _evaluated_one_operand_to_str(arg)
|
154
|
-
if isinstance(arg, list) and arg and isinstance(arg[0], FunctionClosure):
|
155
|
-
return _evaluated_operands_list_to_str(arg)
|
156
|
-
if isinstance(arg, ClassicalProxy):
|
157
|
-
if isinstance(arg, ClassicalStructProxy):
|
158
|
-
return repr(arg.struct_declaration)
|
159
|
-
return repr(get_proxy_type(arg))
|
160
|
-
return evaluated_classical_param_to_str(arg)
|
161
|
-
|
162
|
-
|
163
|
-
def _evaluated_quantum_symbol_to_str(port: QuantumSymbol) -> str:
|
164
|
-
return port.quantum_type.model_dump_json(exclude_none=True, exclude={"name"})
|
165
|
-
|
166
|
-
|
167
|
-
def _evaluated_one_operand_to_str(operand: FunctionClosure) -> str:
|
168
|
-
return operand.closure_id
|
169
|
-
|
170
|
-
|
171
|
-
def _evaluated_operands_list_to_str(arg: list[FunctionClosure]) -> str:
|
172
|
-
return json.dumps([_evaluated_one_operand_to_str(ope) for ope in arg])
|
@@ -13,7 +13,7 @@ from classiq.model_expansions.scope import Evaluated, QuantumVariable
|
|
13
13
|
def add_information_from_output_arguments(
|
14
14
|
parameters: Sequence[AnonPositionalArg],
|
15
15
|
args: list[Evaluated],
|
16
|
-
) ->
|
16
|
+
) -> None:
|
17
17
|
"""
|
18
18
|
This function propagates the quantum type information from the output arguments
|
19
19
|
to the arguments that were passed to it.
|
@@ -40,4 +40,3 @@ def add_information_from_output_arguments(
|
|
40
40
|
argument_as_quantum_symbol.quantum_type,
|
41
41
|
str(argument_as_quantum_symbol),
|
42
42
|
)
|
43
|
-
return args
|
@@ -11,15 +11,12 @@ from classiq.interface.generator.compiler_keywords import (
|
|
11
11
|
from classiq.interface.generator.functions.builtins.internal_operators import (
|
12
12
|
WITHIN_APPLY_NAME,
|
13
13
|
)
|
14
|
-
from classiq.interface.generator.functions.type_qualifier import TypeQualifier
|
15
14
|
from classiq.interface.model.model import MAIN_FUNCTION_NAME
|
16
15
|
from classiq.interface.model.native_function_definition import (
|
17
16
|
NativeFunctionDefinition,
|
18
17
|
)
|
19
|
-
from classiq.interface.model.port_declaration import PortDeclaration
|
20
18
|
from classiq.interface.model.quantum_function_declaration import (
|
21
19
|
PositionalArg,
|
22
|
-
QuantumOperandDeclaration,
|
23
20
|
)
|
24
21
|
from classiq.interface.model.quantum_statement import QuantumStatement
|
25
22
|
from classiq.interface.model.variable_declaration_statement import (
|
@@ -215,7 +212,6 @@ class OperationBuilder:
|
|
215
212
|
self, function_context: FunctionContext, params: Sequence[PositionalArg]
|
216
213
|
) -> NativeFunctionDefinition:
|
217
214
|
name = self._get_expanded_function_name(function_context)
|
218
|
-
self._override_type_qualifier(function_context, params)
|
219
215
|
|
220
216
|
return NativeFunctionDefinition(
|
221
217
|
name=name,
|
@@ -248,44 +244,3 @@ class OperationBuilder:
|
|
248
244
|
raise ClassiqInternalExpansionError("Could not allocate function name")
|
249
245
|
|
250
246
|
return name
|
251
|
-
|
252
|
-
def _override_type_qualifier(
|
253
|
-
self, function_context: FunctionContext, params: Sequence[PositionalArg]
|
254
|
-
) -> None:
|
255
|
-
"""
|
256
|
-
The type qualifier can be changed according to the operand passed to the
|
257
|
-
function. For example,
|
258
|
-
apply_to_all(X, q) --> q will be QFree after expansion
|
259
|
-
apply_to_all(H, q) --> q will be Quantum after expansion
|
260
|
-
This also holds for the intermediate lambda created during the expansion.
|
261
|
-
|
262
|
-
We don't override the type qualifier if it's explicitly specified (QFree or
|
263
|
-
Const), neither in the function declaration nor in the operand declaration.
|
264
|
-
"""
|
265
|
-
|
266
|
-
if function_context.is_lambda:
|
267
|
-
self._update_type_qualifiers(params)
|
268
|
-
return
|
269
|
-
|
270
|
-
orig_name = function_context.name
|
271
|
-
if orig_name == MAIN_FUNCTION_NAME or orig_name not in self.current_scope:
|
272
|
-
return
|
273
|
-
|
274
|
-
orig_func = self.current_scope[orig_name].value
|
275
|
-
if not any(
|
276
|
-
isinstance(param_decl, QuantumOperandDeclaration)
|
277
|
-
for param_decl in orig_func.positional_arg_declarations
|
278
|
-
):
|
279
|
-
return
|
280
|
-
|
281
|
-
self._update_type_qualifiers(params)
|
282
|
-
|
283
|
-
@staticmethod
|
284
|
-
def _update_type_qualifiers(params: Sequence[PositionalArg]) -> None:
|
285
|
-
# only override the qualifier if it's unspecified (not QFree or Const)
|
286
|
-
for param in params:
|
287
|
-
if (
|
288
|
-
isinstance(param, PortDeclaration)
|
289
|
-
and param.type_qualifier is TypeQualifier.Quantum
|
290
|
-
):
|
291
|
-
param.type_qualifier = TypeQualifier.Inferred
|
@@ -104,10 +104,8 @@ class BaseInterpreter:
|
|
104
104
|
main_closure.positional_arg_declarations, main_closure
|
105
105
|
)
|
106
106
|
context = self._expand_operation(main_closure)
|
107
|
-
self._expanded_functions[main_closure.
|
108
|
-
|
109
|
-
cast(FunctionContext, context), main_closure.positional_arg_declarations
|
110
|
-
)
|
107
|
+
self._expanded_functions[main_closure.name] = self._builder.create_definition(
|
108
|
+
cast(FunctionContext, context), main_closure.positional_arg_declarations
|
111
109
|
)
|
112
110
|
|
113
111
|
def _get_main_closure(self, main_func: FunctionClosure) -> FunctionClosure:
|
@@ -275,19 +273,19 @@ class BaseInterpreter:
|
|
275
273
|
|
276
274
|
def _expand_operation(self, operation: Closure) -> OperationContext:
|
277
275
|
with self._builder.operation_context(operation) as context:
|
278
|
-
|
279
|
-
(func_def := self._expanded_functions.get(operation.closure_id))
|
280
|
-
is not None
|
281
|
-
):
|
282
|
-
cached_closure = self._top_level_scope[func_def.name].value
|
283
|
-
operation.captured_vars.set(
|
284
|
-
cached_closure.captured_vars, cached_closure, operation
|
285
|
-
)
|
286
|
-
else:
|
287
|
-
self._expand_body(operation)
|
276
|
+
self._expand_body(operation)
|
288
277
|
|
289
278
|
return context
|
290
279
|
|
280
|
+
def _expand_cached_function(
|
281
|
+
self, operation: FunctionClosure, func_def: NativeFunctionDefinition
|
282
|
+
) -> None:
|
283
|
+
with self._builder.operation_context(operation):
|
284
|
+
cached_closure = self._top_level_scope[func_def.name].value
|
285
|
+
operation.captured_vars.set(
|
286
|
+
cached_closure.captured_vars, cached_closure, operation
|
287
|
+
)
|
288
|
+
|
291
289
|
def _expand_body(self, operation: Closure) -> None:
|
292
290
|
for block, block_body in operation.blocks.items():
|
293
291
|
self._expand_block(block_body, block)
|
@@ -1,17 +1,12 @@
|
|
1
1
|
import inspect
|
2
2
|
import os
|
3
|
-
from typing import Optional
|
4
3
|
|
5
4
|
from pydantic import ValidationError
|
6
5
|
|
7
6
|
from classiq.interface.exceptions import ClassiqError
|
8
7
|
from classiq.interface.model.allocate import Allocate
|
9
8
|
from classiq.interface.model.bind_operation import BindOperation
|
10
|
-
from classiq.interface.model.native_function_definition import NativeFunctionDefinition
|
11
9
|
from classiq.interface.model.quantum_function_call import QuantumFunctionCall
|
12
|
-
from classiq.interface.model.quantum_function_declaration import (
|
13
|
-
NamedParamsQuantumFunctionDeclaration,
|
14
|
-
)
|
15
10
|
from classiq.interface.source_reference import SourceReference
|
16
11
|
|
17
12
|
from classiq.model_expansions.closure import FunctionClosure, GenerativeFunctionClosure
|
@@ -24,22 +19,10 @@ from classiq.model_expansions.quantum_operations.quantum_function_call import (
|
|
24
19
|
DeclarativeQuantumFunctionCallEmitter,
|
25
20
|
)
|
26
21
|
from classiq.model_expansions.scope import Scope
|
27
|
-
from classiq.model_expansions.visitors.symbolic_param_inference import (
|
28
|
-
SymbolicParamInference,
|
29
|
-
)
|
30
22
|
from classiq.qmod.model_state_container import QMODULE
|
31
23
|
|
32
24
|
|
33
25
|
class FrontendGenerativeInterpreter(GenerativeInterpreter):
|
34
|
-
def infer_symbolic_parameters(
|
35
|
-
self,
|
36
|
-
functions: list[NativeFunctionDefinition],
|
37
|
-
additional_signatures: Optional[
|
38
|
-
list[NamedParamsQuantumFunctionDeclaration]
|
39
|
-
] = None,
|
40
|
-
) -> None:
|
41
|
-
SymbolicParamInference(functions, additional_signatures).infer()
|
42
|
-
|
43
26
|
def emit_allocate(self, allocate: Allocate) -> None:
|
44
27
|
AllocateEmitter(self, allow_symbolic_size=True).emit(allocate)
|
45
28
|
|
@@ -28,16 +28,19 @@ from classiq.interface.generator.expressions.proxies.classical.qmod_struct_insta
|
|
28
28
|
from classiq.interface.generator.functions.port_declaration import (
|
29
29
|
PortDeclarationDirection,
|
30
30
|
)
|
31
|
+
from classiq.interface.generator.functions.type_qualifier import TypeQualifier
|
31
32
|
from classiq.interface.model.classical_parameter_declaration import (
|
32
33
|
ClassicalParameterDeclaration,
|
33
34
|
)
|
34
35
|
from classiq.interface.model.handle_binding import HandleBinding
|
36
|
+
from classiq.interface.model.model import MAIN_FUNCTION_NAME
|
35
37
|
from classiq.interface.model.native_function_definition import NativeFunctionDefinition
|
36
38
|
from classiq.interface.model.port_declaration import PortDeclaration
|
37
39
|
from classiq.interface.model.quantum_function_call import ArgValue, QuantumFunctionCall
|
38
40
|
from classiq.interface.model.quantum_function_declaration import (
|
39
41
|
NamedParamsQuantumFunctionDeclaration,
|
40
42
|
PositionalArg,
|
43
|
+
QuantumOperandDeclaration,
|
41
44
|
)
|
42
45
|
from classiq.interface.model.quantum_statement import QuantumStatement
|
43
46
|
from classiq.interface.model.variable_declaration_statement import (
|
@@ -49,7 +52,7 @@ from classiq.model_expansions.capturing.captured_vars import (
|
|
49
52
|
UNINITIALIZED_VAR_MESSAGE,
|
50
53
|
validate_args_are_not_propagated,
|
51
54
|
)
|
52
|
-
from classiq.model_expansions.closure import FunctionClosure
|
55
|
+
from classiq.model_expansions.closure import Closure, FunctionClosure
|
53
56
|
from classiq.model_expansions.evaluators.argument_types import (
|
54
57
|
add_information_from_output_arguments,
|
55
58
|
)
|
@@ -63,6 +66,9 @@ from classiq.model_expansions.quantum_operations.emitter import (
|
|
63
66
|
Emitter,
|
64
67
|
QuantumStatementT,
|
65
68
|
)
|
69
|
+
from classiq.model_expansions.quantum_operations.function_calls_cache import (
|
70
|
+
get_func_call_cache_key,
|
71
|
+
)
|
66
72
|
from classiq.model_expansions.scope import (
|
67
73
|
Evaluated,
|
68
74
|
QuantumSymbol,
|
@@ -70,6 +76,9 @@ from classiq.model_expansions.scope import (
|
|
70
76
|
QuantumVariable,
|
71
77
|
Scope,
|
72
78
|
)
|
79
|
+
from classiq.model_expansions.transformers.type_qualifier_inference import (
|
80
|
+
TypeQualifierInference,
|
81
|
+
)
|
73
82
|
from classiq.model_expansions.transformers.var_splitter import VarSplitter
|
74
83
|
from classiq.model_expansions.utils.text_utils import are, readable_list, s
|
75
84
|
from classiq.qmod.semantics.validation.signature_validation import (
|
@@ -174,19 +183,22 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
|
|
174
183
|
)
|
175
184
|
new_positional_arg_decls = new_declaration.positional_arg_declarations
|
176
185
|
if not self.should_expand_function(function, evaluated_args):
|
177
|
-
is_atomic = True
|
178
186
|
new_declaration = self._expanded_functions_by_name.get(
|
179
187
|
function.name, new_declaration
|
180
188
|
)
|
181
189
|
else:
|
182
|
-
is_atomic = False
|
183
190
|
new_declaration = self._expand_function(
|
184
191
|
evaluated_args, new_declaration, function
|
185
192
|
)
|
193
|
+
new_positional_arg_decls = new_declaration.positional_arg_declarations
|
194
|
+
evaluated_args = [
|
195
|
+
arg
|
196
|
+
for arg in evaluated_args
|
197
|
+
if isinstance(arg.value, QuantumVariable) or _is_symbolic(arg.value)
|
198
|
+
]
|
186
199
|
|
187
|
-
|
188
|
-
|
189
|
-
)
|
200
|
+
add_information_from_output_arguments(new_positional_arg_decls, evaluated_args)
|
201
|
+
new_positional_args = [arg.emit() for arg in evaluated_args]
|
190
202
|
captured_args = function.captured_vars.filter_vars(function).get_captured_args(
|
191
203
|
self._builder.current_function
|
192
204
|
)
|
@@ -229,18 +241,20 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
|
|
229
241
|
function: FunctionClosure,
|
230
242
|
) -> NamedParamsQuantumFunctionDeclaration:
|
231
243
|
self._add_params_to_scope(decl.positional_arg_declarations, args, function)
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
244
|
+
function = function.with_new_declaration(decl)
|
245
|
+
cache_key = get_func_call_cache_key(decl, args)
|
246
|
+
if cache_key in self._expanded_functions:
|
247
|
+
function_def = self._expanded_functions[cache_key]
|
248
|
+
self._expand_cached_function(function, function_def)
|
237
249
|
self._expanded_functions_compilation_metadata[
|
238
250
|
function_def.name
|
239
251
|
].occurrences_number += 1
|
240
252
|
return function_def
|
241
253
|
|
254
|
+
context = self._expand_operation(function)
|
255
|
+
function_context = cast(FunctionContext, context)
|
242
256
|
function_def = self._create_function_definition(function_context, args)
|
243
|
-
self._expanded_functions[
|
257
|
+
self._expanded_functions[cache_key] = function_def
|
244
258
|
self._top_level_scope[function_def.name] = Evaluated(
|
245
259
|
value=function_context.closure.with_new_declaration(function_def)
|
246
260
|
)
|
@@ -270,6 +284,7 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
|
|
270
284
|
)
|
271
285
|
captured_ports = captured_vars.get_captured_parameters()
|
272
286
|
if len(captured_ports) == 0:
|
287
|
+
self._override_type_qualifier(function_context, func_def)
|
273
288
|
return func_def
|
274
289
|
func_def.positional_arg_declarations = list(
|
275
290
|
chain.from_iterable((func_def.positional_arg_declarations, captured_ports))
|
@@ -281,6 +296,8 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
|
|
281
296
|
rewrite_mapping |= captured_vars.get_classical_captured_mapping()
|
282
297
|
func_def.body = self.rewrite(func_def.body, rewrite_mapping)
|
283
298
|
|
299
|
+
self._override_type_qualifier(function_context, func_def)
|
300
|
+
|
284
301
|
return func_def
|
285
302
|
|
286
303
|
@staticmethod
|
@@ -309,26 +326,6 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
|
|
309
326
|
else:
|
310
327
|
closure.scope[parameter.name] = argument
|
311
328
|
|
312
|
-
def _get_new_positional_args(
|
313
|
-
self,
|
314
|
-
evaluated_args: list[Evaluated],
|
315
|
-
is_atomic: bool,
|
316
|
-
new_positional_arg_decls: Sequence[PositionalArg],
|
317
|
-
) -> list[ArgValue]:
|
318
|
-
evaluated_args = add_information_from_output_arguments(
|
319
|
-
new_positional_arg_decls, evaluated_args
|
320
|
-
)
|
321
|
-
if is_atomic:
|
322
|
-
return [arg.emit() for arg in evaluated_args]
|
323
|
-
|
324
|
-
positional_args = [
|
325
|
-
arg.emit()
|
326
|
-
for arg in evaluated_args
|
327
|
-
if isinstance(arg.value, QuantumVariable) or _is_symbolic(arg.value)
|
328
|
-
]
|
329
|
-
|
330
|
-
return positional_args
|
331
|
-
|
332
329
|
def _prepare_fully_typed_declaration(
|
333
330
|
self, function: FunctionClosure, evaluated_args: list[Evaluated]
|
334
331
|
) -> NamedParamsQuantumFunctionDeclaration:
|
@@ -381,3 +378,45 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
|
|
381
378
|
raise ClassiqExpansionError(UNINITIALIZED_VAR_MESSAGE.format(var_name))
|
382
379
|
if var_state and param.direction == PortDeclarationDirection.Output:
|
383
380
|
raise ClassiqExpansionError(INITIALIZED_VAR_MESSAGE.format(var_name))
|
381
|
+
|
382
|
+
def _override_type_qualifier(
|
383
|
+
self, func_context: FunctionContext, func_def: NativeFunctionDefinition
|
384
|
+
) -> None:
|
385
|
+
"""
|
386
|
+
The type qualifier can be changed according to the operand passed to the
|
387
|
+
function. For example,
|
388
|
+
apply_to_all(X, q) --> q will be QFree after expansion
|
389
|
+
apply_to_all(H, q) --> q will be Quantum after expansion
|
390
|
+
This also holds for the intermediate lambda created during the expansion.
|
391
|
+
|
392
|
+
We don't override the type qualifier if it's explicitly specified (QFree or
|
393
|
+
Const), neither in the function declaration nor in the operand declaration.
|
394
|
+
"""
|
395
|
+
|
396
|
+
if func_context.is_lambda:
|
397
|
+
self._update_type_qualifiers(func_def)
|
398
|
+
return
|
399
|
+
|
400
|
+
orig_name = func_context.name
|
401
|
+
if (
|
402
|
+
orig_name == MAIN_FUNCTION_NAME
|
403
|
+
or orig_name not in func_context.closure.scope
|
404
|
+
):
|
405
|
+
return
|
406
|
+
|
407
|
+
orig_func = func_context.closure.scope[orig_name].value
|
408
|
+
if isinstance(orig_func, Closure) and not any(
|
409
|
+
isinstance(param_decl, QuantumOperandDeclaration)
|
410
|
+
for param_decl in orig_func.positional_arg_declarations
|
411
|
+
):
|
412
|
+
return
|
413
|
+
|
414
|
+
self._update_type_qualifiers(func_def)
|
415
|
+
|
416
|
+
@staticmethod
|
417
|
+
def _update_type_qualifiers(func_def: NativeFunctionDefinition) -> None:
|
418
|
+
# only override the qualifier if it's unspecified (not QFree or Const)
|
419
|
+
for port in func_def.port_declarations:
|
420
|
+
if port.type_qualifier is TypeQualifier.Quantum:
|
421
|
+
port.type_qualifier = TypeQualifier.Inferred
|
422
|
+
TypeQualifierInference().run(func_def.port_declarations, func_def.body)
|
@@ -47,7 +47,7 @@ from classiq.interface.model.quantum_function_declaration import (
|
|
47
47
|
)
|
48
48
|
from classiq.interface.model.quantum_statement import QuantumOperation, QuantumStatement
|
49
49
|
|
50
|
-
from classiq.model_expansions.closure import Closure, GenerativeClosure
|
50
|
+
from classiq.model_expansions.closure import Closure, FunctionClosure, GenerativeClosure
|
51
51
|
from classiq.model_expansions.function_builder import (
|
52
52
|
OperationBuilder,
|
53
53
|
OperationContext,
|
@@ -87,6 +87,11 @@ class Emitter(Generic[QuantumStatementT], ABC):
|
|
87
87
|
def _expand_operation(self, closure: Closure) -> OperationContext:
|
88
88
|
return self._interpreter._expand_operation(closure)
|
89
89
|
|
90
|
+
def _expand_cached_function(
|
91
|
+
self, closure: FunctionClosure, func_def: NativeFunctionDefinition
|
92
|
+
) -> None:
|
93
|
+
return self._interpreter._expand_cached_function(closure, func_def)
|
94
|
+
|
90
95
|
@property
|
91
96
|
def _builder(self) -> OperationBuilder:
|
92
97
|
return self._interpreter._builder
|
@@ -0,0 +1,84 @@
|
|
1
|
+
import json
|
2
|
+
from typing import Any
|
3
|
+
|
4
|
+
from classiq.interface.exceptions import ClassiqInternalExpansionError
|
5
|
+
from classiq.interface.generator.expressions.proxies.classical.classical_proxy import (
|
6
|
+
ClassicalProxy,
|
7
|
+
)
|
8
|
+
from classiq.interface.generator.expressions.proxies.classical.classical_struct_proxy import (
|
9
|
+
ClassicalStructProxy,
|
10
|
+
)
|
11
|
+
from classiq.interface.generator.expressions.proxies.classical.utils import (
|
12
|
+
get_proxy_type,
|
13
|
+
)
|
14
|
+
from classiq.interface.generator.functions.port_declaration import (
|
15
|
+
PortDeclarationDirection,
|
16
|
+
)
|
17
|
+
from classiq.interface.model.port_declaration import PortDeclaration
|
18
|
+
from classiq.interface.model.quantum_function_declaration import (
|
19
|
+
NamedParamsQuantumFunctionDeclaration,
|
20
|
+
)
|
21
|
+
|
22
|
+
from classiq.model_expansions.closure import FunctionClosure
|
23
|
+
from classiq.model_expansions.scope import (
|
24
|
+
Evaluated,
|
25
|
+
QuantumSymbol,
|
26
|
+
evaluated_to_str as evaluated_classical_param_to_str,
|
27
|
+
)
|
28
|
+
|
29
|
+
|
30
|
+
def get_func_call_cache_key(
|
31
|
+
decl: NamedParamsQuantumFunctionDeclaration,
|
32
|
+
args: list[Evaluated],
|
33
|
+
) -> str:
|
34
|
+
if len(decl.positional_arg_declarations) != len(args):
|
35
|
+
raise ClassiqInternalExpansionError(
|
36
|
+
"Mismatch between number of args to number of arg declarations"
|
37
|
+
)
|
38
|
+
|
39
|
+
# output arguments cannot affect the morphization of the function, as their
|
40
|
+
# attributes are defined locally by the function, according to other arguments
|
41
|
+
non_outputs_args = [
|
42
|
+
arg
|
43
|
+
for arg_decl, arg in zip(decl.positional_arg_declarations, args)
|
44
|
+
if not (
|
45
|
+
isinstance(arg_decl, PortDeclaration)
|
46
|
+
and arg_decl.direction is PortDeclarationDirection.Output
|
47
|
+
)
|
48
|
+
]
|
49
|
+
return f"{decl.name}__{_evaluated_args_to_str(non_outputs_args)}"
|
50
|
+
|
51
|
+
|
52
|
+
def _evaluated_args_to_str(evaluated_args: list[Evaluated]) -> str:
|
53
|
+
args_signature = [
|
54
|
+
_evaluated_arg_to_str(eval_arg.value) for eval_arg in evaluated_args
|
55
|
+
]
|
56
|
+
return json.dumps(args_signature)
|
57
|
+
|
58
|
+
|
59
|
+
def _evaluated_arg_to_str(arg: Any) -> str:
|
60
|
+
if isinstance(arg, str):
|
61
|
+
return arg
|
62
|
+
if isinstance(arg, QuantumSymbol):
|
63
|
+
return _evaluated_quantum_symbol_to_str(arg)
|
64
|
+
if isinstance(arg, FunctionClosure):
|
65
|
+
return _evaluated_one_operand_to_str(arg)
|
66
|
+
if isinstance(arg, list) and arg and isinstance(arg[0], FunctionClosure):
|
67
|
+
return _evaluated_operands_list_to_str(arg)
|
68
|
+
if isinstance(arg, ClassicalProxy):
|
69
|
+
if isinstance(arg, ClassicalStructProxy):
|
70
|
+
return repr(arg.struct_declaration)
|
71
|
+
return repr(get_proxy_type(arg))
|
72
|
+
return evaluated_classical_param_to_str(arg)
|
73
|
+
|
74
|
+
|
75
|
+
def _evaluated_quantum_symbol_to_str(port: QuantumSymbol) -> str:
|
76
|
+
return port.quantum_type.model_dump_json(exclude_none=True, exclude={"name"})
|
77
|
+
|
78
|
+
|
79
|
+
def _evaluated_one_operand_to_str(operand: FunctionClosure) -> str:
|
80
|
+
return operand.name
|
81
|
+
|
82
|
+
|
83
|
+
def _evaluated_operands_list_to_str(arg: list[FunctionClosure]) -> str:
|
84
|
+
return json.dumps([_evaluated_one_operand_to_str(ope) for ope in arg])
|
@@ -3,7 +3,9 @@ from collections import UserDict
|
|
3
3
|
from collections.abc import Iterator
|
4
4
|
from dataclasses import dataclass
|
5
5
|
from functools import singledispatch
|
6
|
-
from typing import TYPE_CHECKING, Any, Optional, TypeVar, Union
|
6
|
+
from typing import TYPE_CHECKING, Any, NoReturn, Optional, TypeVar, Union
|
7
|
+
|
8
|
+
import sympy
|
7
9
|
|
8
10
|
from classiq.interface.exceptions import (
|
9
11
|
ClassiqExpansionError,
|
@@ -34,6 +36,8 @@ from classiq.interface.model.quantum_type import (
|
|
34
36
|
QuantumType,
|
35
37
|
)
|
36
38
|
|
39
|
+
from classiq.model_expansions.utils.text_utils import readable_list, s
|
40
|
+
|
37
41
|
if TYPE_CHECKING:
|
38
42
|
from classiq.model_expansions.closure import FunctionClosure
|
39
43
|
|
@@ -206,27 +210,33 @@ def _evaluated_to_str_struct_literal(value: QmodStructInstance) -> str:
|
|
206
210
|
return f"struct_literal({value.struct_declaration.name}, {', '.join(f'{k}={evaluated_to_str(v)}' for k, v in value.fields.items())})"
|
207
211
|
|
208
212
|
|
213
|
+
def _raise_type_error(val: Any, t: type, location_hint: Optional[str]) -> NoReturn:
|
214
|
+
if isinstance(val, sympy.Basic) and len(val.free_symbols) > 0:
|
215
|
+
symbolic_vars = list(val.free_symbols)
|
216
|
+
suffix = f" {location_hint}" if location_hint is not None else ""
|
217
|
+
raise ClassiqExpansionError(
|
218
|
+
f"Cannot use execution parameter{s(symbolic_vars)} {readable_list(symbolic_vars, quote=True)} in a compile-time context{suffix}"
|
219
|
+
)
|
220
|
+
raise ClassiqExpansionError(f"Invalid access to expression {val!r} as {t}")
|
221
|
+
|
222
|
+
|
209
223
|
@dataclass(frozen=True)
|
210
224
|
class Evaluated: # FIXME: Merge with EvaluatedExpression if possible
|
211
225
|
value: Any
|
212
226
|
defining_function: Optional["FunctionClosure"] = None
|
213
227
|
|
214
|
-
def as_type(self, t: type[T]) -> T:
|
228
|
+
def as_type(self, t: type[T], location_hint: Optional[str] = None) -> T:
|
215
229
|
if t is int:
|
216
|
-
return self._as_int() # type: ignore[return-value]
|
230
|
+
return self._as_int(location_hint) # type: ignore[return-value]
|
217
231
|
|
218
232
|
if not isinstance(self.value, t):
|
219
|
-
|
220
|
-
f"Invalid access to expression {self.value!r} as {t}"
|
221
|
-
)
|
233
|
+
_raise_type_error(self.value, t, location_hint)
|
222
234
|
|
223
235
|
return self.value
|
224
236
|
|
225
|
-
def _as_int(self) -> int:
|
237
|
+
def _as_int(self, location_hint: Optional[str]) -> int:
|
226
238
|
if not isinstance(self.value, (int, float)):
|
227
|
-
|
228
|
-
f"Invalid access to expression {self.value!r} as {int}"
|
229
|
-
)
|
239
|
+
_raise_type_error(self.value, int, location_hint)
|
230
240
|
|
231
241
|
return int(self.value)
|
232
242
|
|