classiq 0.77.0__py3-none-any.whl → 0.79.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/arith/arithmetic.py +10 -6
- classiq/interface/generator/arith/binary_ops.py +8 -11
- classiq/interface/generator/expressions/proxies/classical/any_classical_value.py +9 -3
- classiq/interface/generator/expressions/proxies/classical/classical_array_proxy.py +90 -23
- classiq/interface/generator/expressions/proxies/classical/utils.py +4 -0
- classiq/interface/generator/functions/classical_type.py +74 -0
- classiq/interface/generator/functions/concrete_types.py +3 -0
- classiq/interface/generator/functions/type_name.py +32 -3
- classiq/interface/generator/generated_circuit_data.py +21 -8
- classiq/interface/helpers/model_normalizer.py +47 -0
- classiq/interface/ide/visual_model.py +2 -0
- classiq/interface/interface_version.py +1 -1
- classiq/interface/model/bounds.py +12 -0
- classiq/interface/model/model.py +9 -5
- classiq/interface/model/quantum_type.py +25 -3
- classiq/interface/model/statement_block.py +2 -0
- classiq/model_expansions/atomic_expression_functions_defs.py +20 -6
- classiq/model_expansions/closure.py +1 -58
- classiq/model_expansions/evaluators/argument_types.py +21 -2
- classiq/model_expansions/evaluators/classical_type_inference.py +42 -13
- classiq/model_expansions/evaluators/quantum_type_utils.py +31 -3
- classiq/model_expansions/function_builder.py +0 -45
- classiq/model_expansions/generative_functions.py +1 -1
- classiq/model_expansions/interpreters/base_interpreter.py +12 -14
- classiq/model_expansions/interpreters/frontend_generative_interpreter.py +0 -17
- classiq/model_expansions/interpreters/generative_interpreter.py +9 -0
- classiq/model_expansions/quantum_operations/__init__.py +3 -0
- classiq/model_expansions/quantum_operations/allocate.py +3 -1
- classiq/model_expansions/quantum_operations/assignment_result_processor.py +7 -3
- classiq/model_expansions/quantum_operations/bind.py +8 -1
- classiq/model_expansions/quantum_operations/bounds.py +30 -0
- classiq/model_expansions/quantum_operations/call_emitter.py +116 -37
- classiq/model_expansions/quantum_operations/emitter.py +6 -1
- classiq/model_expansions/quantum_operations/function_calls_cache.py +91 -0
- classiq/model_expansions/quantum_operations/quantum_function_call.py +5 -6
- classiq/model_expansions/scope.py +33 -13
- classiq/model_expansions/scope_initialization.py +1 -14
- 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 +33 -28
- classiq/model_expansions/visitors/variable_references.py +39 -43
- classiq/open_library/functions/linear_pauli_rotation.py +6 -6
- classiq/open_library/functions/state_preparation.py +1 -1
- classiq/open_library/functions/utility_functions.py +2 -2
- classiq/qmod/builtins/classical_execution_primitives.py +1 -1
- classiq/qmod/declaration_inferrer.py +5 -3
- classiq/qmod/model_state_container.py +21 -1
- classiq/qmod/native/pretty_printer.py +8 -0
- classiq/qmod/pretty_print/expression_to_python.py +8 -1
- classiq/qmod/pretty_print/pretty_printer.py +7 -0
- classiq/qmod/python_classical_type.py +1 -1
- classiq/qmod/qmod_parameter.py +43 -7
- classiq/qmod/qmod_variable.py +7 -4
- classiq/qmod/semantics/annotation/qstruct_annotator.py +15 -4
- classiq/qmod/utilities.py +5 -1
- {classiq-0.77.0.dist-info → classiq-0.79.0.dist-info}/METADATA +1 -1
- {classiq-0.77.0.dist-info → classiq-0.79.0.dist-info}/RECORD +68 -59
- classiq/interface/executor/iqae_result.py +0 -17
- {classiq-0.77.0.dist-info → classiq-0.79.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,30 @@
|
|
1
|
+
from typing import TYPE_CHECKING
|
2
|
+
|
3
|
+
from classiq.interface.exceptions import ClassiqExpansionError
|
4
|
+
from classiq.interface.model.bounds import SetBoundsStatement
|
5
|
+
|
6
|
+
from classiq.model_expansions.quantum_operations.bind import Emitter
|
7
|
+
from classiq.model_expansions.scope import QuantumSymbol
|
8
|
+
from classiq.qmod.qmod_variable import QuantumNumeric
|
9
|
+
|
10
|
+
if TYPE_CHECKING:
|
11
|
+
from classiq.model_expansions.interpreters.base_interpreter import BaseInterpreter
|
12
|
+
|
13
|
+
|
14
|
+
class SetBoundsEmitter(Emitter[SetBoundsStatement]):
|
15
|
+
def __init__(
|
16
|
+
self, interpreter: "BaseInterpreter", keep_statement: bool = True
|
17
|
+
) -> None:
|
18
|
+
super().__init__(interpreter)
|
19
|
+
self._keep_statement = keep_statement
|
20
|
+
|
21
|
+
def emit(self, op: SetBoundsStatement, /) -> bool:
|
22
|
+
target = self._interpreter.evaluate(op.target).as_type(QuantumSymbol)
|
23
|
+
if not isinstance(target.quantum_type, QuantumNumeric):
|
24
|
+
raise ClassiqExpansionError(
|
25
|
+
f"Cannot set bounds of a non-numeric variable {op.target.qmod_expr!r}"
|
26
|
+
)
|
27
|
+
target.quantum_type.set_bounds(op.bounds)
|
28
|
+
if self._keep_statement:
|
29
|
+
self.emit_statement(op)
|
30
|
+
return True
|
@@ -28,16 +28,20 @@ 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
|
32
|
+
from classiq.interface.helpers.backward_compatibility import zip_strict
|
31
33
|
from classiq.interface.model.classical_parameter_declaration import (
|
32
34
|
ClassicalParameterDeclaration,
|
33
35
|
)
|
34
36
|
from classiq.interface.model.handle_binding import HandleBinding
|
37
|
+
from classiq.interface.model.model import MAIN_FUNCTION_NAME
|
35
38
|
from classiq.interface.model.native_function_definition import NativeFunctionDefinition
|
36
39
|
from classiq.interface.model.port_declaration import PortDeclaration
|
37
40
|
from classiq.interface.model.quantum_function_call import ArgValue, QuantumFunctionCall
|
38
41
|
from classiq.interface.model.quantum_function_declaration import (
|
39
42
|
NamedParamsQuantumFunctionDeclaration,
|
40
43
|
PositionalArg,
|
44
|
+
QuantumOperandDeclaration,
|
41
45
|
)
|
42
46
|
from classiq.interface.model.quantum_statement import QuantumStatement
|
43
47
|
from classiq.interface.model.variable_declaration_statement import (
|
@@ -49,9 +53,10 @@ from classiq.model_expansions.capturing.captured_vars import (
|
|
49
53
|
UNINITIALIZED_VAR_MESSAGE,
|
50
54
|
validate_args_are_not_propagated,
|
51
55
|
)
|
52
|
-
from classiq.model_expansions.closure import FunctionClosure
|
56
|
+
from classiq.model_expansions.closure import Closure, FunctionClosure
|
53
57
|
from classiq.model_expansions.evaluators.argument_types import (
|
54
58
|
add_information_from_output_arguments,
|
59
|
+
handle_args_numeric_bounds,
|
55
60
|
)
|
56
61
|
from classiq.model_expansions.evaluators.parameter_types import (
|
57
62
|
evaluate_parameter_types_from_args,
|
@@ -63,6 +68,9 @@ from classiq.model_expansions.quantum_operations.emitter import (
|
|
63
68
|
Emitter,
|
64
69
|
QuantumStatementT,
|
65
70
|
)
|
71
|
+
from classiq.model_expansions.quantum_operations.function_calls_cache import (
|
72
|
+
get_func_call_cache_key,
|
73
|
+
)
|
66
74
|
from classiq.model_expansions.scope import (
|
67
75
|
Evaluated,
|
68
76
|
QuantumSymbol,
|
@@ -70,8 +78,12 @@ from classiq.model_expansions.scope import (
|
|
70
78
|
QuantumVariable,
|
71
79
|
Scope,
|
72
80
|
)
|
81
|
+
from classiq.model_expansions.transformers.type_qualifier_inference import (
|
82
|
+
TypeQualifierInference,
|
83
|
+
)
|
73
84
|
from classiq.model_expansions.transformers.var_splitter import VarSplitter
|
74
85
|
from classiq.model_expansions.utils.text_utils import are, readable_list, s
|
86
|
+
from classiq.qmod.pretty_print.expression_to_python import transform_expression
|
75
87
|
from classiq.qmod.semantics.validation.signature_validation import (
|
76
88
|
validate_function_signature,
|
77
89
|
)
|
@@ -114,6 +126,24 @@ def _is_symbolic(arg: Any) -> bool:
|
|
114
126
|
return False
|
115
127
|
|
116
128
|
|
129
|
+
def _validate_gen_args(
|
130
|
+
function: FunctionClosure, evaluated_args: list[Evaluated]
|
131
|
+
) -> None:
|
132
|
+
for param, arg in zip_strict(
|
133
|
+
function.positional_arg_declarations, evaluated_args, strict=True
|
134
|
+
):
|
135
|
+
if (
|
136
|
+
isinstance(param, ClassicalParameterDeclaration)
|
137
|
+
and param.classical_type.is_purely_generative
|
138
|
+
and _is_symbolic(arg.value)
|
139
|
+
):
|
140
|
+
raise ClassiqExpansionError(
|
141
|
+
f"Parameter {param.name!r} is used in a compile-time expression "
|
142
|
+
f"context but is passed a runtime expression "
|
143
|
+
f"{transform_expression(str(arg.value), {}, {}, one_line=True)!r}"
|
144
|
+
)
|
145
|
+
|
146
|
+
|
117
147
|
class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSplitter):
|
118
148
|
def __init__(self, interpreter: "BaseInterpreter") -> None:
|
119
149
|
Emitter.__init__(self, interpreter)
|
@@ -174,22 +204,38 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
|
|
174
204
|
)
|
175
205
|
new_positional_arg_decls = new_declaration.positional_arg_declarations
|
176
206
|
if not self.should_expand_function(function, evaluated_args):
|
177
|
-
is_atomic = True
|
178
207
|
new_declaration = self._expanded_functions_by_name.get(
|
179
208
|
function.name, new_declaration
|
180
209
|
)
|
181
210
|
else:
|
182
|
-
|
211
|
+
# FIXME: enable for BE (CLS-2390)
|
212
|
+
if type(self._interpreter).__name__ == "FrontendGenerativeInterpreter":
|
213
|
+
_validate_gen_args(function, evaluated_args)
|
183
214
|
new_declaration = self._expand_function(
|
184
215
|
evaluated_args, new_declaration, function
|
185
216
|
)
|
217
|
+
new_positional_arg_decls = new_declaration.positional_arg_declarations
|
218
|
+
evaluated_args = [
|
219
|
+
arg
|
220
|
+
for arg in evaluated_args
|
221
|
+
if isinstance(arg.value, QuantumVariable) or _is_symbolic(arg.value)
|
222
|
+
]
|
186
223
|
|
187
|
-
|
188
|
-
|
189
|
-
)
|
224
|
+
add_information_from_output_arguments(new_positional_arg_decls, evaluated_args)
|
225
|
+
handle_args_numeric_bounds(new_positional_arg_decls, evaluated_args)
|
190
226
|
captured_args = function.captured_vars.filter_vars(function).get_captured_args(
|
191
227
|
self._builder.current_function
|
192
228
|
)
|
229
|
+
new_positional_args = [
|
230
|
+
arg.emit(param)
|
231
|
+
for param, arg in zip_strict(
|
232
|
+
new_positional_arg_decls[
|
233
|
+
: len(new_positional_arg_decls) - len(captured_args)
|
234
|
+
],
|
235
|
+
evaluated_args,
|
236
|
+
strict=True,
|
237
|
+
)
|
238
|
+
]
|
193
239
|
validate_args_are_not_propagated(
|
194
240
|
new_positional_args,
|
195
241
|
captured_args,
|
@@ -228,19 +274,23 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
|
|
228
274
|
decl: NamedParamsQuantumFunctionDeclaration,
|
229
275
|
function: FunctionClosure,
|
230
276
|
) -> NamedParamsQuantumFunctionDeclaration:
|
231
|
-
self._add_params_to_scope(
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
277
|
+
inferred_args = self._add_params_to_scope(
|
278
|
+
decl.positional_arg_declarations, args, function
|
279
|
+
)
|
280
|
+
function = function.with_new_declaration(decl)
|
281
|
+
cache_key = get_func_call_cache_key(decl, inferred_args)
|
282
|
+
if cache_key in self._expanded_functions:
|
283
|
+
function_def = self._expanded_functions[cache_key]
|
284
|
+
self._expand_cached_function(function, function_def)
|
237
285
|
self._expanded_functions_compilation_metadata[
|
238
286
|
function_def.name
|
239
287
|
].occurrences_number += 1
|
240
288
|
return function_def
|
241
289
|
|
290
|
+
context = self._expand_operation(function)
|
291
|
+
function_context = cast(FunctionContext, context)
|
242
292
|
function_def = self._create_function_definition(function_context, args)
|
243
|
-
self._expanded_functions[
|
293
|
+
self._expanded_functions[cache_key] = function_def
|
244
294
|
self._top_level_scope[function_def.name] = Evaluated(
|
245
295
|
value=function_context.closure.with_new_declaration(function_def)
|
246
296
|
)
|
@@ -270,6 +320,7 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
|
|
270
320
|
)
|
271
321
|
captured_ports = captured_vars.get_captured_parameters()
|
272
322
|
if len(captured_ports) == 0:
|
323
|
+
self._override_type_qualifier(function_context, func_def)
|
273
324
|
return func_def
|
274
325
|
func_def.positional_arg_declarations = list(
|
275
326
|
chain.from_iterable((func_def.positional_arg_declarations, captured_ports))
|
@@ -281,6 +332,8 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
|
|
281
332
|
rewrite_mapping |= captured_vars.get_classical_captured_mapping()
|
282
333
|
func_def.body = self.rewrite(func_def.body, rewrite_mapping)
|
283
334
|
|
335
|
+
self._override_type_qualifier(function_context, func_def)
|
336
|
+
|
284
337
|
return func_def
|
285
338
|
|
286
339
|
@staticmethod
|
@@ -288,12 +341,13 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
|
|
288
341
|
parameters: Sequence[PositionalArg],
|
289
342
|
arguments: Sequence[Evaluated],
|
290
343
|
closure: FunctionClosure,
|
291
|
-
) ->
|
344
|
+
) -> list[Evaluated]:
|
345
|
+
inferred_args: list[Evaluated] = []
|
292
346
|
for parameter, argument in zip(parameters, arguments):
|
293
347
|
param_handle = HandleBinding(name=parameter.name)
|
294
348
|
if isinstance(argument.value, QuantumVariable):
|
295
349
|
assert isinstance(parameter, PortDeclaration)
|
296
|
-
|
350
|
+
inferred_arg = Evaluated(
|
297
351
|
QuantumSymbol(
|
298
352
|
handle=param_handle,
|
299
353
|
quantum_type=parameter.quantum_type,
|
@@ -302,32 +356,15 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
|
|
302
356
|
)
|
303
357
|
elif _is_symbolic(argument.value):
|
304
358
|
assert isinstance(parameter, ClassicalParameterDeclaration)
|
305
|
-
|
359
|
+
inferred_arg = Evaluated(
|
306
360
|
value=parameter.classical_type.get_classical_proxy(param_handle),
|
307
361
|
defining_function=closure,
|
308
362
|
)
|
309
363
|
else:
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
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
|
364
|
+
inferred_arg = argument
|
365
|
+
closure.scope[parameter.name] = inferred_arg
|
366
|
+
inferred_args.append(inferred_arg)
|
367
|
+
return inferred_args
|
331
368
|
|
332
369
|
def _prepare_fully_typed_declaration(
|
333
370
|
self, function: FunctionClosure, evaluated_args: list[Evaluated]
|
@@ -381,3 +418,45 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
|
|
381
418
|
raise ClassiqExpansionError(UNINITIALIZED_VAR_MESSAGE.format(var_name))
|
382
419
|
if var_state and param.direction == PortDeclarationDirection.Output:
|
383
420
|
raise ClassiqExpansionError(INITIALIZED_VAR_MESSAGE.format(var_name))
|
421
|
+
|
422
|
+
def _override_type_qualifier(
|
423
|
+
self, func_context: FunctionContext, func_def: NativeFunctionDefinition
|
424
|
+
) -> None:
|
425
|
+
"""
|
426
|
+
The type qualifier can be changed according to the operand passed to the
|
427
|
+
function. For example,
|
428
|
+
apply_to_all(X, q) --> q will be QFree after expansion
|
429
|
+
apply_to_all(H, q) --> q will be Quantum after expansion
|
430
|
+
This also holds for the intermediate lambda created during the expansion.
|
431
|
+
|
432
|
+
We don't override the type qualifier if it's explicitly specified (QFree or
|
433
|
+
Const), neither in the function declaration nor in the operand declaration.
|
434
|
+
"""
|
435
|
+
|
436
|
+
if func_context.is_lambda:
|
437
|
+
self._update_type_qualifiers(func_def)
|
438
|
+
return
|
439
|
+
|
440
|
+
orig_name = func_context.name
|
441
|
+
if (
|
442
|
+
orig_name == MAIN_FUNCTION_NAME
|
443
|
+
or orig_name not in func_context.closure.scope
|
444
|
+
):
|
445
|
+
return
|
446
|
+
|
447
|
+
orig_func = func_context.closure.scope[orig_name].value
|
448
|
+
if isinstance(orig_func, Closure) and not any(
|
449
|
+
isinstance(param_decl, QuantumOperandDeclaration)
|
450
|
+
for param_decl in orig_func.positional_arg_declarations
|
451
|
+
):
|
452
|
+
return
|
453
|
+
|
454
|
+
self._update_type_qualifiers(func_def)
|
455
|
+
|
456
|
+
@staticmethod
|
457
|
+
def _update_type_qualifiers(func_def: NativeFunctionDefinition) -> None:
|
458
|
+
# only override the qualifier if it's unspecified (not QFree or Const)
|
459
|
+
for port in func_def.port_declarations:
|
460
|
+
if port.type_qualifier is TypeQualifier.Quantum:
|
461
|
+
port.type_qualifier = TypeQualifier.Inferred
|
462
|
+
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,91 @@
|
|
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
|
+
from classiq.interface.model.quantum_type import QuantumNumeric
|
22
|
+
|
23
|
+
from classiq.model_expansions.closure import FunctionClosure
|
24
|
+
from classiq.model_expansions.scope import (
|
25
|
+
Evaluated,
|
26
|
+
QuantumSymbol,
|
27
|
+
evaluated_to_str as evaluated_classical_param_to_str,
|
28
|
+
)
|
29
|
+
|
30
|
+
|
31
|
+
def get_func_call_cache_key(
|
32
|
+
decl: NamedParamsQuantumFunctionDeclaration,
|
33
|
+
args: list[Evaluated],
|
34
|
+
) -> str:
|
35
|
+
if len(decl.positional_arg_declarations) != len(args):
|
36
|
+
raise ClassiqInternalExpansionError(
|
37
|
+
"Mismatch between number of args to number of arg declarations"
|
38
|
+
)
|
39
|
+
|
40
|
+
# output arguments cannot affect the morphization of the function, as their
|
41
|
+
# attributes are defined locally by the function, according to other arguments
|
42
|
+
non_outputs_args = [
|
43
|
+
arg
|
44
|
+
for arg_decl, arg in zip(decl.positional_arg_declarations, args)
|
45
|
+
if not (
|
46
|
+
isinstance(arg_decl, PortDeclaration)
|
47
|
+
and arg_decl.direction is PortDeclarationDirection.Output
|
48
|
+
)
|
49
|
+
]
|
50
|
+
return f"{decl.name}__{_evaluated_args_to_str(non_outputs_args)}"
|
51
|
+
|
52
|
+
|
53
|
+
def _evaluated_args_to_str(evaluated_args: list[Evaluated]) -> str:
|
54
|
+
args_signature = [
|
55
|
+
_evaluated_arg_to_str(eval_arg.value) for eval_arg in evaluated_args
|
56
|
+
]
|
57
|
+
return json.dumps(args_signature)
|
58
|
+
|
59
|
+
|
60
|
+
def _evaluated_arg_to_str(arg: Any) -> str:
|
61
|
+
if isinstance(arg, str):
|
62
|
+
return arg
|
63
|
+
if isinstance(arg, QuantumSymbol):
|
64
|
+
return _evaluated_quantum_symbol_to_str(arg)
|
65
|
+
if isinstance(arg, FunctionClosure):
|
66
|
+
return _evaluated_one_operand_to_str(arg)
|
67
|
+
if isinstance(arg, list) and arg and isinstance(arg[0], FunctionClosure):
|
68
|
+
return _evaluated_operands_list_to_str(arg)
|
69
|
+
if isinstance(arg, ClassicalProxy):
|
70
|
+
if isinstance(arg, ClassicalStructProxy):
|
71
|
+
return repr(arg.struct_declaration)
|
72
|
+
return repr(get_proxy_type(arg))
|
73
|
+
return evaluated_classical_param_to_str(arg)
|
74
|
+
|
75
|
+
|
76
|
+
def _evaluated_quantum_symbol_to_str(port: QuantumSymbol) -> str:
|
77
|
+
res = port.quantum_type.model_dump_json(exclude_none=True, exclude={"name"})
|
78
|
+
if (
|
79
|
+
isinstance(port.quantum_type, QuantumNumeric)
|
80
|
+
and (bounds := port.quantum_type.get_bounds()) is not None
|
81
|
+
):
|
82
|
+
res += f"_{float(bounds[0])}_{float(bounds[1])}"
|
83
|
+
return res
|
84
|
+
|
85
|
+
|
86
|
+
def _evaluated_one_operand_to_str(operand: FunctionClosure) -> str:
|
87
|
+
return operand.name
|
88
|
+
|
89
|
+
|
90
|
+
def _evaluated_operands_list_to_str(arg: list[FunctionClosure]) -> str:
|
91
|
+
return json.dumps([_evaluated_one_operand_to_str(ope) for ope in arg])
|
@@ -1,10 +1,9 @@
|
|
1
1
|
from typing import TYPE_CHECKING
|
2
2
|
|
3
|
+
import sympy
|
4
|
+
|
3
5
|
from classiq.interface.exceptions import ClassiqInternalExpansionError
|
4
6
|
from classiq.interface.generator.expressions.expression import Expression
|
5
|
-
from classiq.interface.generator.expressions.proxies.classical.classical_scalar_proxy import (
|
6
|
-
ClassicalScalarProxy,
|
7
|
-
)
|
8
7
|
from classiq.interface.model.classical_if import ClassicalIf
|
9
8
|
from classiq.interface.model.quantum_function_call import QuantumFunctionCall
|
10
9
|
from classiq.interface.model.quantum_lambda_function import OperandIdentifier
|
@@ -29,7 +28,7 @@ class QuantumFunctionCallEmitter(CallEmitter[QuantumFunctionCall]):
|
|
29
28
|
def emit(self, call: QuantumFunctionCall, /) -> bool:
|
30
29
|
if isinstance(call.function, OperandIdentifier):
|
31
30
|
index_val = self._interpreter.evaluate(call.function.index).value
|
32
|
-
if isinstance(index_val,
|
31
|
+
if isinstance(index_val, sympy.Basic):
|
33
32
|
return self._emit_symbolic_lambda_list(call, index_val)
|
34
33
|
function = self._interpreter.evaluate(call.function).as_type(FunctionClosure)
|
35
34
|
args = call.positional_args
|
@@ -40,7 +39,7 @@ class QuantumFunctionCallEmitter(CallEmitter[QuantumFunctionCall]):
|
|
40
39
|
return True
|
41
40
|
|
42
41
|
def _emit_symbolic_lambda_list(
|
43
|
-
self, call: QuantumFunctionCall, index:
|
42
|
+
self, call: QuantumFunctionCall, index: sympy.Basic
|
44
43
|
) -> bool:
|
45
44
|
if TYPE_CHECKING:
|
46
45
|
assert isinstance(call.function, OperandIdentifier)
|
@@ -55,7 +54,7 @@ class QuantumFunctionCallEmitter(CallEmitter[QuantumFunctionCall]):
|
|
55
54
|
|
56
55
|
@staticmethod
|
57
56
|
def _create_recursive_if(
|
58
|
-
call: QuantumFunctionCall, index:
|
57
|
+
call: QuantumFunctionCall, index: sympy.Basic, num_funcs: int
|
59
58
|
) -> list[QuantumStatement]:
|
60
59
|
if TYPE_CHECKING:
|
61
60
|
assert isinstance(call.function, OperandIdentifier)
|
@@ -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,
|
@@ -29,11 +31,18 @@ from classiq.interface.model.handle_binding import (
|
|
29
31
|
SubscriptHandleBinding,
|
30
32
|
)
|
31
33
|
from classiq.interface.model.quantum_function_call import ArgValue
|
34
|
+
from classiq.interface.model.quantum_function_declaration import (
|
35
|
+
AnonPositionalArg,
|
36
|
+
AnonQuantumOperandDeclaration,
|
37
|
+
)
|
32
38
|
from classiq.interface.model.quantum_type import (
|
33
39
|
QuantumBitvector,
|
40
|
+
QuantumNumeric,
|
34
41
|
QuantumType,
|
35
42
|
)
|
36
43
|
|
44
|
+
from classiq.model_expansions.utils.text_utils import readable_list, s
|
45
|
+
|
37
46
|
if TYPE_CHECKING:
|
38
47
|
from classiq.model_expansions.closure import FunctionClosure
|
39
48
|
|
@@ -180,6 +189,9 @@ class QuantumSymbolList(QuantumVariable):
|
|
180
189
|
length = Expression(
|
181
190
|
expr=str(sum(symbol.quantum_type.size_in_bits for symbol in symbols))
|
182
191
|
)
|
192
|
+
for symbol in symbols:
|
193
|
+
if isinstance(symbol.quantum_type, QuantumNumeric):
|
194
|
+
symbol.quantum_type.reset_bounds()
|
183
195
|
return QuantumSymbolList(
|
184
196
|
handles=handles, quantum_type=QuantumBitvector(length=length)
|
185
197
|
)
|
@@ -206,37 +218,45 @@ def _evaluated_to_str_struct_literal(value: QmodStructInstance) -> str:
|
|
206
218
|
return f"struct_literal({value.struct_declaration.name}, {', '.join(f'{k}={evaluated_to_str(v)}' for k, v in value.fields.items())})"
|
207
219
|
|
208
220
|
|
221
|
+
def _raise_type_error(val: Any, t: type, location_hint: Optional[str]) -> NoReturn:
|
222
|
+
if isinstance(val, sympy.Basic) and len(val.free_symbols) > 0:
|
223
|
+
symbolic_vars = sorted(map(str, val.free_symbols))
|
224
|
+
suffix = f" {location_hint}" if location_hint is not None else ""
|
225
|
+
raise ClassiqExpansionError(
|
226
|
+
f"Cannot use execution parameter{s(symbolic_vars)} {readable_list(symbolic_vars, quote=True)} in a compile-time context{suffix}"
|
227
|
+
)
|
228
|
+
raise ClassiqExpansionError(f"Invalid access to expression {val!r} as {t}")
|
229
|
+
|
230
|
+
|
209
231
|
@dataclass(frozen=True)
|
210
232
|
class Evaluated: # FIXME: Merge with EvaluatedExpression if possible
|
211
233
|
value: Any
|
212
234
|
defining_function: Optional["FunctionClosure"] = None
|
213
235
|
|
214
|
-
def as_type(self, t: type[T]) -> T:
|
236
|
+
def as_type(self, t: type[T], location_hint: Optional[str] = None) -> T:
|
215
237
|
if t is int:
|
216
|
-
return self._as_int() # type: ignore[return-value]
|
238
|
+
return self._as_int(location_hint) # type: ignore[return-value]
|
217
239
|
|
218
240
|
if not isinstance(self.value, t):
|
219
|
-
|
220
|
-
f"Invalid access to expression {self.value!r} as {t}"
|
221
|
-
)
|
241
|
+
_raise_type_error(self.value, t, location_hint)
|
222
242
|
|
223
243
|
return self.value
|
224
244
|
|
225
|
-
def _as_int(self) -> int:
|
245
|
+
def _as_int(self, location_hint: Optional[str]) -> int:
|
226
246
|
if not isinstance(self.value, (int, float)):
|
227
|
-
|
228
|
-
f"Invalid access to expression {self.value!r} as {int}"
|
229
|
-
)
|
247
|
+
_raise_type_error(self.value, int, location_hint)
|
230
248
|
|
231
249
|
return int(self.value)
|
232
250
|
|
233
|
-
def emit(self) -> ArgValue:
|
251
|
+
def emit(self, param: Optional[AnonPositionalArg] = None) -> ArgValue:
|
234
252
|
from classiq.model_expansions.closure import FunctionClosure
|
235
253
|
|
236
254
|
if isinstance(self.value, (QuantumVariable, FunctionClosure)):
|
237
255
|
return self.value.emit()
|
238
|
-
if
|
239
|
-
isinstance(
|
256
|
+
if (
|
257
|
+
isinstance(param, AnonQuantumOperandDeclaration)
|
258
|
+
and isinstance(self.value, list)
|
259
|
+
and all(isinstance(item, FunctionClosure) for item in self.value)
|
240
260
|
):
|
241
261
|
return [item.emit() for item in self.value]
|
242
262
|
|
@@ -1,12 +1,7 @@
|
|
1
1
|
from collections.abc import Sequence
|
2
2
|
|
3
|
-
from classiq.interface.exceptions import ClassiqError
|
3
|
+
from classiq.interface.exceptions import ClassiqError
|
4
4
|
from classiq.interface.generator.constant import Constant
|
5
|
-
from classiq.interface.generator.functions.classical_type import (
|
6
|
-
ClassicalArray,
|
7
|
-
ClassicalList,
|
8
|
-
)
|
9
|
-
from classiq.interface.generator.functions.concrete_types import ConcreteClassicalType
|
10
5
|
from classiq.interface.model.classical_parameter_declaration import (
|
11
6
|
ClassicalParameterDeclaration,
|
12
7
|
)
|
@@ -138,11 +133,3 @@ def init_top_level_scope(model: Model, scope: Scope) -> None:
|
|
138
133
|
def init_builtin_types() -> None:
|
139
134
|
QMODULE.enum_decls |= BUILTIN_ENUM_DECLARATIONS
|
140
135
|
QMODULE.type_decls |= BUILTIN_STRUCT_DECLARATIONS
|
141
|
-
|
142
|
-
|
143
|
-
def _get_shape(classical_type: ConcreteClassicalType) -> tuple[int, ...]:
|
144
|
-
if isinstance(classical_type, ClassicalList):
|
145
|
-
raise ClassiqInternalExpansionError("Unexpected classical list")
|
146
|
-
if isinstance(classical_type, ClassicalArray):
|
147
|
-
return classical_type.size, *_get_shape(classical_type.element_type)
|
148
|
-
return ()
|