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
@@ -18,6 +18,7 @@ from classiq.interface.generator.expressions.proxies.classical.any_classical_val
|
|
18
18
|
)
|
19
19
|
from classiq.interface.generator.expressions.proxies.classical.classical_array_proxy import (
|
20
20
|
ClassicalArrayProxy,
|
21
|
+
ClassicalSequenceProxy,
|
21
22
|
)
|
22
23
|
from classiq.interface.generator.expressions.proxies.classical.classical_proxy import (
|
23
24
|
ClassicalProxy,
|
@@ -39,6 +40,7 @@ from classiq.interface.generator.functions.classical_type import (
|
|
39
40
|
Bool,
|
40
41
|
ClassicalArray,
|
41
42
|
ClassicalList,
|
43
|
+
ClassicalTuple,
|
42
44
|
ClassicalType,
|
43
45
|
OpaqueHandle,
|
44
46
|
QmodPyObject,
|
@@ -46,6 +48,7 @@ from classiq.interface.generator.functions.classical_type import (
|
|
46
48
|
StructMetaType,
|
47
49
|
)
|
48
50
|
from classiq.interface.generator.functions.type_name import TypeName
|
51
|
+
from classiq.interface.helpers.backward_compatibility import zip_strict
|
49
52
|
|
50
53
|
from classiq.model_expansions.model_tables import (
|
51
54
|
HandleIdentifier,
|
@@ -92,6 +95,15 @@ def qmod_val_to_python(val: ExpressionValue, qmod_type: ClassicalType) -> Any:
|
|
92
95
|
if isinstance(val, list):
|
93
96
|
return [qmod_val_to_python(elem, qmod_type.element_type) for elem in val]
|
94
97
|
|
98
|
+
elif isinstance(qmod_type, ClassicalTuple):
|
99
|
+
if isinstance(val, list):
|
100
|
+
return [
|
101
|
+
qmod_val_to_python(elem, elem_type)
|
102
|
+
for elem_type, elem in zip_strict(
|
103
|
+
qmod_type.element_types, val, strict=True
|
104
|
+
)
|
105
|
+
]
|
106
|
+
|
95
107
|
elif isinstance(qmod_type, OpaqueHandle):
|
96
108
|
if isinstance(val, HandleIdentifier):
|
97
109
|
return HandleTable.get_handle_object(val)
|
@@ -133,7 +145,7 @@ def python_val_to_qmod(val: Any, qmod_type: ClassicalType) -> ExpressionValue:
|
|
133
145
|
field_name: python_val_to_qmod(val[field_name], field_type)
|
134
146
|
for field_name, field_type in struct_decl.variables.items()
|
135
147
|
}
|
136
|
-
return QmodStructInstance(struct_decl, qmod_dict)
|
148
|
+
return QmodStructInstance(struct_decl.model_copy(), qmod_dict)
|
137
149
|
|
138
150
|
if isinstance(qmod_type, ClassicalList):
|
139
151
|
if not isinstance(val, list):
|
@@ -162,7 +174,7 @@ def python_call_wrapper(func: Callable, *args: ExpressionValue) -> Any:
|
|
162
174
|
|
163
175
|
def struct_literal(struct_type_symbol: Symbol, **kwargs: Any) -> QmodStructInstance:
|
164
176
|
return QmodStructInstance(
|
165
|
-
QMODULE.type_decls[struct_type_symbol.name],
|
177
|
+
QMODULE.type_decls[struct_type_symbol.name].model_copy(),
|
166
178
|
{field: sympy_to_python(field_value) for field, field_value in kwargs.items()},
|
167
179
|
)
|
168
180
|
|
@@ -236,7 +248,7 @@ def _is_qmod_value(val: Any) -> bool:
|
|
236
248
|
|
237
249
|
|
238
250
|
def do_subscript(value: Any, index: Any) -> Any:
|
239
|
-
if not isinstance(value, (list,
|
251
|
+
if not isinstance(value, (list, ClassicalSequenceProxy)) or not isinstance(
|
240
252
|
index, QmodQNumProxy
|
241
253
|
):
|
242
254
|
if isinstance(index, (QmodSizedProxy, QmodStructInstance)):
|
@@ -260,7 +272,7 @@ def do_subscript(value: Any, index: Any) -> Any:
|
|
260
272
|
"Quantum numeric subscript must be an unsigned integer (is_signed=False, "
|
261
273
|
"fraction_digits=0)"
|
262
274
|
)
|
263
|
-
if isinstance(value,
|
275
|
+
if isinstance(value, ClassicalSequenceProxy):
|
264
276
|
length = value.length
|
265
277
|
else:
|
266
278
|
length = len(value)
|
@@ -269,7 +281,7 @@ def do_subscript(value: Any, index: Any) -> Any:
|
|
269
281
|
f"Quantum numeric subscript size mismatch: The quantum numeric has "
|
270
282
|
f"{index.size} qubits but the list size is {length} != 2**{index.size}"
|
271
283
|
)
|
272
|
-
if isinstance(value,
|
284
|
+
if isinstance(value, ClassicalSequenceProxy):
|
273
285
|
return AnyClassicalValue(
|
274
286
|
f"do_subscript({qmod_val_to_expr_str(value)}, {qmod_val_to_expr_str(index)})"
|
275
287
|
)
|
@@ -289,7 +301,9 @@ def do_slice(value: Any, lower: Any, upper: Any) -> Any:
|
|
289
301
|
|
290
302
|
|
291
303
|
def do_sum(val: Any) -> Any:
|
292
|
-
if isinstance(val,
|
304
|
+
if (isinstance(val, sympy.Basic) and len(val.free_symbols) > 0) or (
|
305
|
+
isinstance(val, ClassicalArrayProxy) and not isinstance(val.length, int)
|
306
|
+
):
|
293
307
|
return AnyClassicalValue(f"sum({val})")
|
294
308
|
return sum(val)
|
295
309
|
|
@@ -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])
|
@@ -3,8 +3,10 @@ from collections.abc import Sequence
|
|
3
3
|
from classiq.interface.generator.functions.port_declaration import (
|
4
4
|
PortDeclarationDirection,
|
5
5
|
)
|
6
|
+
from classiq.interface.generator.functions.type_qualifier import TypeQualifier
|
6
7
|
from classiq.interface.model.port_declaration import AnonPortDeclaration
|
7
8
|
from classiq.interface.model.quantum_function_declaration import AnonPositionalArg
|
9
|
+
from classiq.interface.model.quantum_type import QuantumNumeric
|
8
10
|
|
9
11
|
from classiq.model_expansions.evaluators.quantum_type_utils import copy_type_information
|
10
12
|
from classiq.model_expansions.scope import Evaluated, QuantumVariable
|
@@ -13,7 +15,7 @@ from classiq.model_expansions.scope import Evaluated, QuantumVariable
|
|
13
15
|
def add_information_from_output_arguments(
|
14
16
|
parameters: Sequence[AnonPositionalArg],
|
15
17
|
args: list[Evaluated],
|
16
|
-
) ->
|
18
|
+
) -> None:
|
17
19
|
"""
|
18
20
|
This function propagates the quantum type information from the output arguments
|
19
21
|
to the arguments that were passed to it.
|
@@ -40,4 +42,21 @@ def add_information_from_output_arguments(
|
|
40
42
|
argument_as_quantum_symbol.quantum_type,
|
41
43
|
str(argument_as_quantum_symbol),
|
42
44
|
)
|
43
|
-
|
45
|
+
|
46
|
+
|
47
|
+
def handle_args_numeric_bounds(
|
48
|
+
parameters: Sequence[AnonPositionalArg],
|
49
|
+
args: list[Evaluated],
|
50
|
+
) -> None:
|
51
|
+
for parameter, argument in zip(parameters, args):
|
52
|
+
if not isinstance(parameter, AnonPortDeclaration):
|
53
|
+
continue
|
54
|
+
|
55
|
+
argument_as_quantum_symbol = argument.as_type(QuantumVariable)
|
56
|
+
|
57
|
+
if (
|
58
|
+
parameter.direction != PortDeclarationDirection.Output
|
59
|
+
and parameter.type_qualifier != TypeQualifier.Const
|
60
|
+
and isinstance(argument_as_quantum_symbol.quantum_type, QuantumNumeric)
|
61
|
+
):
|
62
|
+
argument_as_quantum_symbol.quantum_type.reset_bounds()
|
@@ -1,11 +1,12 @@
|
|
1
|
-
from typing import Any, Union
|
1
|
+
from typing import TYPE_CHECKING, Any, Union
|
2
2
|
|
3
3
|
from classiq.interface.exceptions import ClassiqExpansionError
|
4
4
|
from classiq.interface.generator.expressions.proxies.classical.any_classical_value import (
|
5
5
|
AnyClassicalValue,
|
6
6
|
)
|
7
7
|
from classiq.interface.generator.expressions.proxies.classical.classical_array_proxy import (
|
8
|
-
|
8
|
+
ClassicalSequenceProxy,
|
9
|
+
_is_int,
|
9
10
|
)
|
10
11
|
from classiq.interface.generator.expressions.proxies.classical.classical_struct_proxy import (
|
11
12
|
ClassicalStructProxy,
|
@@ -16,6 +17,7 @@ from classiq.interface.generator.expressions.proxies.classical.qmod_struct_insta
|
|
16
17
|
from classiq.interface.generator.functions.classical_type import (
|
17
18
|
ClassicalArray,
|
18
19
|
ClassicalList,
|
20
|
+
ClassicalTuple,
|
19
21
|
ClassicalType,
|
20
22
|
)
|
21
23
|
from classiq.interface.generator.functions.type_name import TypeName
|
@@ -26,7 +28,7 @@ from classiq.interface.helpers.backward_compatibility import zip_strict
|
|
26
28
|
def infer_classical_type(val: Any, classical_type: ClassicalType) -> ClassicalType:
|
27
29
|
if isinstance(classical_type, TypeName):
|
28
30
|
return _infer_classical_struct_type(val, classical_type)
|
29
|
-
if isinstance(classical_type, (ClassicalArray, ClassicalList)):
|
31
|
+
if isinstance(classical_type, (ClassicalArray, ClassicalList, ClassicalTuple)):
|
30
32
|
return _infer_classical_array_type(val, classical_type)
|
31
33
|
return classical_type
|
32
34
|
|
@@ -34,7 +36,11 @@ def infer_classical_type(val: Any, classical_type: ClassicalType) -> ClassicalTy
|
|
34
36
|
def _infer_classical_struct_type(val: Any, classical_type: TypeName) -> ClassicalType:
|
35
37
|
if not isinstance(val, (QmodStructInstance, ClassicalStructProxy)):
|
36
38
|
return classical_type
|
37
|
-
|
39
|
+
if classical_type.is_enum:
|
40
|
+
raise ClassiqExpansionError(
|
41
|
+
f"{classical_type.type_name!r} expected, got {str(val)!r}"
|
42
|
+
)
|
43
|
+
decl = classical_type.classical_struct_decl
|
38
44
|
new_fields = {
|
39
45
|
field_name: infer_classical_type(field_val, field_type)
|
40
46
|
for (field_name, field_val), field_type in zip_strict(
|
@@ -51,9 +57,9 @@ def _infer_classical_struct_type(val: Any, classical_type: TypeName) -> Classica
|
|
51
57
|
|
52
58
|
|
53
59
|
def _infer_classical_array_type(
|
54
|
-
val: Any, classical_type: Union[ClassicalArray, ClassicalList]
|
60
|
+
val: Any, classical_type: Union[ClassicalArray, ClassicalList, ClassicalTuple]
|
55
61
|
) -> ClassicalType:
|
56
|
-
if isinstance(val,
|
62
|
+
if isinstance(val, ClassicalSequenceProxy):
|
57
63
|
val_length = val.length
|
58
64
|
elif isinstance(val, list):
|
59
65
|
val_length = len(val)
|
@@ -62,7 +68,7 @@ def _infer_classical_array_type(
|
|
62
68
|
else:
|
63
69
|
raise ClassiqExpansionError(f"Array expected, got {str(val)!r}")
|
64
70
|
if (
|
65
|
-
isinstance(classical_type, ClassicalArray)
|
71
|
+
isinstance(classical_type, (ClassicalArray, ClassicalTuple))
|
66
72
|
and isinstance(val_length, int)
|
67
73
|
and isinstance(classical_type.size, int)
|
68
74
|
and val_length != classical_type.size
|
@@ -71,11 +77,34 @@ def _infer_classical_array_type(
|
|
71
77
|
f"Type mismatch: Argument has {val_length} items but "
|
72
78
|
f"{classical_type.size} expected"
|
73
79
|
)
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
80
|
+
new_classical_type = _infer_inner_array_types(classical_type, val, val_length)
|
81
|
+
if classical_type.is_generative:
|
82
|
+
new_classical_type.set_generative()
|
83
|
+
return new_classical_type
|
84
|
+
|
85
|
+
|
86
|
+
def _infer_inner_array_types(
|
87
|
+
classical_type: ClassicalType, val: Any, val_length: Any
|
88
|
+
) -> ClassicalType:
|
89
|
+
if isinstance(classical_type, (ClassicalArray, ClassicalList)):
|
90
|
+
if _is_int(val_length) and val_length != 0:
|
91
|
+
return ClassicalTuple(
|
92
|
+
element_types=(
|
93
|
+
infer_classical_type(val[i], classical_type.element_type)
|
94
|
+
for i in range(int(val_length))
|
95
|
+
),
|
96
|
+
)
|
97
|
+
element_type: ClassicalType
|
98
|
+
if val_length == 0:
|
99
|
+
element_type = classical_type.element_type
|
100
|
+
else:
|
101
|
+
element_type = infer_classical_type(val[0], classical_type.element_type)
|
102
|
+
return ClassicalArray(element_type=element_type, size=val_length)
|
103
|
+
if TYPE_CHECKING:
|
104
|
+
assert isinstance(classical_type, ClassicalTuple)
|
105
|
+
return ClassicalTuple(
|
106
|
+
element_types=(
|
107
|
+
infer_classical_type(val[idx], element_type)
|
108
|
+
for idx, element_type in enumerate(classical_type.element_types)
|
79
109
|
),
|
80
|
-
size=val_length,
|
81
110
|
)
|
@@ -38,6 +38,7 @@ def copy_type_information(
|
|
38
38
|
expr=str(from_type.fraction_digits_value)
|
39
39
|
)
|
40
40
|
set_size(to_type, from_type.size_in_bits, to_param_name)
|
41
|
+
set_bounds(from_type, to_type)
|
41
42
|
elif isinstance(to_type, QuantumBitvector):
|
42
43
|
if isinstance(from_type, QuantumBitvector) and type( # noqa: E721
|
43
44
|
from_type.element_type
|
@@ -146,16 +147,20 @@ def set_length_by_size(
|
|
146
147
|
quantum_array.length = Expression(expr=str(size // element_size))
|
147
148
|
|
148
149
|
|
149
|
-
def validate_bind_targets(
|
150
|
+
def validate_bind_targets(
|
151
|
+
bind: BindOperation, scope: Scope, allow_symbolic_size: bool
|
152
|
+
) -> None:
|
150
153
|
illegal_qnum_bind_targets = []
|
151
154
|
for out_handle in bind.out_handles:
|
152
155
|
out_var = scope[out_handle.name].as_type(QuantumSymbol)
|
153
156
|
out_var_type = out_var.quantum_type
|
154
157
|
if not isinstance(out_var_type, QuantumNumeric):
|
155
158
|
continue
|
156
|
-
if not out_var_type.
|
159
|
+
if (allow_symbolic_size and not out_var_type.is_instantiated) or (
|
160
|
+
not allow_symbolic_size and not out_var_type.has_size_in_bits
|
161
|
+
):
|
157
162
|
illegal_qnum_bind_targets.append(str(out_var.handle))
|
158
|
-
elif not out_var_type.has_sign:
|
163
|
+
elif not allow_symbolic_size and not out_var_type.has_sign:
|
159
164
|
assert not out_var_type.has_fraction_digits
|
160
165
|
illegal_qnum_bind_targets.append(str(out_var.handle))
|
161
166
|
if len(illegal_qnum_bind_targets) > 0:
|
@@ -187,3 +192,26 @@ def is_signature_monomorphic(params: Sequence[PositionalArg]) -> bool:
|
|
187
192
|
isinstance(param, PortDeclaration) and param.quantum_type.is_evaluated
|
188
193
|
for param in params
|
189
194
|
)
|
195
|
+
|
196
|
+
|
197
|
+
def set_bounds(from_type: QuantumType, to_type: QuantumNumeric) -> None:
|
198
|
+
if not isinstance(from_type, QuantumNumeric):
|
199
|
+
to_type.reset_bounds()
|
200
|
+
return
|
201
|
+
|
202
|
+
if from_type.is_evaluated and to_type.is_evaluated:
|
203
|
+
same_attributes = to_type.sign_value == from_type.sign_value and (
|
204
|
+
to_type.fraction_digits_value == from_type.fraction_digits_value
|
205
|
+
)
|
206
|
+
else:
|
207
|
+
same_attributes = (
|
208
|
+
(from_type.is_signed is not None and from_type.fraction_digits is not None)
|
209
|
+
and (to_type.is_signed is not None and to_type.fraction_digits is not None)
|
210
|
+
and (to_type.is_signed.expr == from_type.is_signed.expr)
|
211
|
+
and (to_type.fraction_digits.expr == from_type.fraction_digits.expr)
|
212
|
+
)
|
213
|
+
|
214
|
+
if same_attributes:
|
215
|
+
to_type.set_bounds(from_type.get_bounds())
|
216
|
+
else:
|
217
|
+
to_type.reset_bounds()
|
@@ -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
|
@@ -114,7 +114,7 @@ def translate_ast_arg_to_python_qmod(param: PositionalArg, value: Any) -> Any:
|
|
114
114
|
return [QTerminalCallable(inner_decl, index_=idx) for idx in range(len(value))]
|
115
115
|
if (
|
116
116
|
isinstance(value, QmodStructInstance)
|
117
|
-
and param.classical_type.
|
117
|
+
and not param.classical_type.is_purely_generative
|
118
118
|
):
|
119
119
|
classical_type = Struct(name=value.struct_declaration.name)
|
120
120
|
classical_type.set_classical_struct_decl(value.struct_declaration)
|
@@ -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
|
|
@@ -15,6 +15,7 @@ from classiq.interface.generator.functions.builtins.internal_operators import (
|
|
15
15
|
)
|
16
16
|
from classiq.interface.model.allocate import Allocate
|
17
17
|
from classiq.interface.model.bind_operation import BindOperation
|
18
|
+
from classiq.interface.model.bounds import SetBoundsStatement
|
18
19
|
from classiq.interface.model.classical_if import ClassicalIf
|
19
20
|
from classiq.interface.model.control import Control
|
20
21
|
from classiq.interface.model.inplace_binary_operation import InplaceBinaryOperation
|
@@ -66,6 +67,7 @@ from classiq.model_expansions.quantum_operations.block_evaluator import (
|
|
66
67
|
IfElimination,
|
67
68
|
RepeatElimination,
|
68
69
|
)
|
70
|
+
from classiq.model_expansions.quantum_operations.bounds import SetBoundsEmitter
|
69
71
|
from classiq.model_expansions.quantum_operations.composite_emitter import (
|
70
72
|
CompositeEmitter,
|
71
73
|
)
|
@@ -297,6 +299,13 @@ class GenerativeInterpreter(BaseInterpreter):
|
|
297
299
|
],
|
298
300
|
).emit(phase)
|
299
301
|
|
302
|
+
@emit.register
|
303
|
+
def emit_set_bounds(self, op: SetBoundsStatement) -> None:
|
304
|
+
CompositeEmitter[SetBoundsStatement](
|
305
|
+
self,
|
306
|
+
[HandleEvaluator(self, "target"), SetBoundsEmitter(self)],
|
307
|
+
).emit(op)
|
308
|
+
|
300
309
|
def _expand_body(self, operation: Closure) -> None:
|
301
310
|
if isinstance(operation, FunctionClosure) and operation.name == "permute":
|
302
311
|
# special expansion since permute is generative
|
@@ -1,4 +1,7 @@
|
|
1
1
|
from classiq.model_expansions.quantum_operations.bind import BindEmitter
|
2
|
+
from classiq.model_expansions.quantum_operations.bounds import (
|
3
|
+
SetBoundsEmitter,
|
4
|
+
)
|
2
5
|
from classiq.model_expansions.quantum_operations.quantum_function_call import (
|
3
6
|
QuantumFunctionCallEmitter,
|
4
7
|
)
|
@@ -10,7 +10,7 @@ from classiq.interface.generator.expressions.proxies.classical.any_classical_val
|
|
10
10
|
)
|
11
11
|
from classiq.interface.model.allocate import Allocate
|
12
12
|
from classiq.interface.model.handle_binding import NestedHandleBinding
|
13
|
-
from classiq.interface.model.quantum_type import QuantumBitvector
|
13
|
+
from classiq.interface.model.quantum_type import QuantumBitvector, QuantumNumeric
|
14
14
|
|
15
15
|
from classiq.model_expansions.evaluators.quantum_type_utils import copy_type_information
|
16
16
|
from classiq.model_expansions.quantum_operations.emitter import Emitter
|
@@ -38,6 +38,8 @@ class AllocateEmitter(Emitter[Allocate]):
|
|
38
38
|
)
|
39
39
|
|
40
40
|
size_expr = self._get_var_size(target, allocate.size)
|
41
|
+
if isinstance(target.quantum_type, QuantumNumeric):
|
42
|
+
target.quantum_type.set_bounds((0, 0))
|
41
43
|
allocate = allocate.model_copy(
|
42
44
|
update=dict(
|
43
45
|
size=Expression(expr=size_expr),
|
@@ -37,15 +37,17 @@ from classiq.qmod.builtins.functions.standard_gates import CX
|
|
37
37
|
|
38
38
|
class AssignmentResultProcessor(Emitter[QuantumAssignmentOperation]):
|
39
39
|
def emit(self, op: QuantumAssignmentOperation, /) -> bool:
|
40
|
+
result_symbol = self._interpreter.evaluate(op.result_var).as_type(QuantumSymbol)
|
41
|
+
result_type = result_symbol.quantum_type
|
42
|
+
|
40
43
|
if not (
|
41
44
|
isinstance(op, ArithmeticOperation)
|
42
45
|
and op.operation_kind == ArithmeticOperationKind.Assignment
|
43
46
|
):
|
47
|
+
if isinstance(result_type, QuantumNumeric):
|
48
|
+
result_type.reset_bounds()
|
44
49
|
return False
|
45
50
|
|
46
|
-
result_symbol = self._interpreter.evaluate(op.result_var).as_type(QuantumSymbol)
|
47
|
-
result_type = result_symbol.quantum_type
|
48
|
-
|
49
51
|
validate_assignment_bool_expression(
|
50
52
|
result_symbol, op.expression.expr, op.operation_kind
|
51
53
|
)
|
@@ -69,6 +71,7 @@ class AssignmentResultProcessor(Emitter[QuantumAssignmentOperation]):
|
|
69
71
|
result_type, inferred_result_type, str(op.result_var)
|
70
72
|
)
|
71
73
|
self._assign_to_inferred_var_and_bind(op, result_type, inferred_result_type)
|
74
|
+
result_type.set_bounds(inferred_result_type.get_bounds())
|
72
75
|
return True
|
73
76
|
|
74
77
|
def _infer_result_type(self, op: ArithmeticOperation) -> Optional[QuantumNumeric]:
|
@@ -105,6 +108,7 @@ class AssignmentResultProcessor(Emitter[QuantumAssignmentOperation]):
|
|
105
108
|
result_type.fraction_digits = Expression(
|
106
109
|
expr=str(inferred_result_type.fraction_digits_value)
|
107
110
|
)
|
111
|
+
result_type.set_bounds(inferred_result_type.get_bounds())
|
108
112
|
|
109
113
|
@staticmethod
|
110
114
|
def _same_numeric_attributes(
|
@@ -1,3 +1,4 @@
|
|
1
|
+
from itertools import chain
|
1
2
|
from typing import TYPE_CHECKING
|
2
3
|
|
3
4
|
from classiq.interface.exceptions import (
|
@@ -5,6 +6,7 @@ from classiq.interface.exceptions import (
|
|
5
6
|
ClassiqInternalExpansionError,
|
6
7
|
)
|
7
8
|
from classiq.interface.model.bind_operation import BindOperation
|
9
|
+
from classiq.interface.model.quantum_type import QuantumNumeric
|
8
10
|
|
9
11
|
from classiq.model_expansions.evaluators.parameter_types import (
|
10
12
|
evaluate_types_in_quantum_symbols,
|
@@ -29,8 +31,13 @@ class BindEmitter(Emitter[BindOperation]):
|
|
29
31
|
|
30
32
|
def emit(self, bind: BindOperation, /) -> bool:
|
31
33
|
inputs, outputs = self._get_inputs_outputs(bind)
|
32
|
-
validate_bind_targets(bind, self._current_scope)
|
34
|
+
validate_bind_targets(bind, self._current_scope, self._allow_symbolic_size)
|
33
35
|
self._process_var_sizes(bind, inputs, outputs)
|
36
|
+
|
37
|
+
for symbol in chain(inputs, outputs):
|
38
|
+
if isinstance(symbol.quantum_type, QuantumNumeric):
|
39
|
+
symbol.quantum_type.reset_bounds()
|
40
|
+
|
34
41
|
self.emit_statement(
|
35
42
|
BindOperation(
|
36
43
|
in_handles=bind.in_handles,
|