classiq 0.78.0__py3-none-any.whl → 0.79.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- classiq/interface/_version.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/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 +3 -1
- classiq/interface/helpers/model_normalizer.py +47 -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/evaluators/argument_types.py +20 -0
- classiq/model_expansions/evaluators/classical_type_inference.py +42 -13
- classiq/model_expansions/evaluators/quantum_type_utils.py +31 -3
- classiq/model_expansions/generative_functions.py +1 -1
- 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 +47 -7
- classiq/model_expansions/quantum_operations/function_calls_cache.py +8 -1
- classiq/model_expansions/quantum_operations/quantum_function_call.py +5 -6
- classiq/model_expansions/scope.py +14 -4
- classiq/model_expansions/scope_initialization.py +1 -14
- classiq/model_expansions/visitors/symbolic_param_inference.py +32 -16
- 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/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/semantics/annotation/qstruct_annotator.py +15 -4
- classiq/qmod/utilities.py +1 -1
- {classiq-0.78.0.dist-info → classiq-0.79.1.dist-info}/METADATA +1 -1
- {classiq-0.78.0.dist-info → classiq-0.79.1.dist-info}/RECORD +48 -45
- {classiq-0.78.0.dist-info → classiq-0.79.1.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
|
|
@@ -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
|
@@ -40,3 +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
|
)
|
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()
|
@@ -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)
|
@@ -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,
|
@@ -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
|
@@ -29,6 +29,7 @@ from classiq.interface.generator.functions.port_declaration import (
|
|
29
29
|
PortDeclarationDirection,
|
30
30
|
)
|
31
31
|
from classiq.interface.generator.functions.type_qualifier import TypeQualifier
|
32
|
+
from classiq.interface.helpers.backward_compatibility import zip_strict
|
32
33
|
from classiq.interface.model.classical_parameter_declaration import (
|
33
34
|
ClassicalParameterDeclaration,
|
34
35
|
)
|
@@ -55,6 +56,7 @@ from classiq.model_expansions.capturing.captured_vars import (
|
|
55
56
|
from classiq.model_expansions.closure import Closure, FunctionClosure
|
56
57
|
from classiq.model_expansions.evaluators.argument_types import (
|
57
58
|
add_information_from_output_arguments,
|
59
|
+
handle_args_numeric_bounds,
|
58
60
|
)
|
59
61
|
from classiq.model_expansions.evaluators.parameter_types import (
|
60
62
|
evaluate_parameter_types_from_args,
|
@@ -81,6 +83,7 @@ from classiq.model_expansions.transformers.type_qualifier_inference import (
|
|
81
83
|
)
|
82
84
|
from classiq.model_expansions.transformers.var_splitter import VarSplitter
|
83
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
|
84
87
|
from classiq.qmod.semantics.validation.signature_validation import (
|
85
88
|
validate_function_signature,
|
86
89
|
)
|
@@ -123,6 +126,24 @@ def _is_symbolic(arg: Any) -> bool:
|
|
123
126
|
return False
|
124
127
|
|
125
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
|
+
|
126
147
|
class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSplitter):
|
127
148
|
def __init__(self, interpreter: "BaseInterpreter") -> None:
|
128
149
|
Emitter.__init__(self, interpreter)
|
@@ -187,6 +208,9 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
|
|
187
208
|
function.name, new_declaration
|
188
209
|
)
|
189
210
|
else:
|
211
|
+
# FIXME: enable for BE (CLS-2390)
|
212
|
+
if type(self._interpreter).__name__ == "FrontendGenerativeInterpreter":
|
213
|
+
_validate_gen_args(function, evaluated_args)
|
190
214
|
new_declaration = self._expand_function(
|
191
215
|
evaluated_args, new_declaration, function
|
192
216
|
)
|
@@ -198,10 +222,20 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
|
|
198
222
|
]
|
199
223
|
|
200
224
|
add_information_from_output_arguments(new_positional_arg_decls, evaluated_args)
|
201
|
-
|
225
|
+
handle_args_numeric_bounds(new_positional_arg_decls, evaluated_args)
|
202
226
|
captured_args = function.captured_vars.filter_vars(function).get_captured_args(
|
203
227
|
self._builder.current_function
|
204
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
|
+
]
|
205
239
|
validate_args_are_not_propagated(
|
206
240
|
new_positional_args,
|
207
241
|
captured_args,
|
@@ -240,9 +274,11 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
|
|
240
274
|
decl: NamedParamsQuantumFunctionDeclaration,
|
241
275
|
function: FunctionClosure,
|
242
276
|
) -> NamedParamsQuantumFunctionDeclaration:
|
243
|
-
self._add_params_to_scope(
|
277
|
+
inferred_args = self._add_params_to_scope(
|
278
|
+
decl.positional_arg_declarations, args, function
|
279
|
+
)
|
244
280
|
function = function.with_new_declaration(decl)
|
245
|
-
cache_key = get_func_call_cache_key(decl,
|
281
|
+
cache_key = get_func_call_cache_key(decl, inferred_args)
|
246
282
|
if cache_key in self._expanded_functions:
|
247
283
|
function_def = self._expanded_functions[cache_key]
|
248
284
|
self._expand_cached_function(function, function_def)
|
@@ -305,12 +341,13 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
|
|
305
341
|
parameters: Sequence[PositionalArg],
|
306
342
|
arguments: Sequence[Evaluated],
|
307
343
|
closure: FunctionClosure,
|
308
|
-
) ->
|
344
|
+
) -> list[Evaluated]:
|
345
|
+
inferred_args: list[Evaluated] = []
|
309
346
|
for parameter, argument in zip(parameters, arguments):
|
310
347
|
param_handle = HandleBinding(name=parameter.name)
|
311
348
|
if isinstance(argument.value, QuantumVariable):
|
312
349
|
assert isinstance(parameter, PortDeclaration)
|
313
|
-
|
350
|
+
inferred_arg = Evaluated(
|
314
351
|
QuantumSymbol(
|
315
352
|
handle=param_handle,
|
316
353
|
quantum_type=parameter.quantum_type,
|
@@ -319,12 +356,15 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
|
|
319
356
|
)
|
320
357
|
elif _is_symbolic(argument.value):
|
321
358
|
assert isinstance(parameter, ClassicalParameterDeclaration)
|
322
|
-
|
359
|
+
inferred_arg = Evaluated(
|
323
360
|
value=parameter.classical_type.get_classical_proxy(param_handle),
|
324
361
|
defining_function=closure,
|
325
362
|
)
|
326
363
|
else:
|
327
|
-
|
364
|
+
inferred_arg = argument
|
365
|
+
closure.scope[parameter.name] = inferred_arg
|
366
|
+
inferred_args.append(inferred_arg)
|
367
|
+
return inferred_args
|
328
368
|
|
329
369
|
def _prepare_fully_typed_declaration(
|
330
370
|
self, function: FunctionClosure, evaluated_args: list[Evaluated]
|
@@ -18,6 +18,7 @@ from classiq.interface.model.port_declaration import PortDeclaration
|
|
18
18
|
from classiq.interface.model.quantum_function_declaration import (
|
19
19
|
NamedParamsQuantumFunctionDeclaration,
|
20
20
|
)
|
21
|
+
from classiq.interface.model.quantum_type import QuantumNumeric
|
21
22
|
|
22
23
|
from classiq.model_expansions.closure import FunctionClosure
|
23
24
|
from classiq.model_expansions.scope import (
|
@@ -73,7 +74,13 @@ def _evaluated_arg_to_str(arg: Any) -> str:
|
|
73
74
|
|
74
75
|
|
75
76
|
def _evaluated_quantum_symbol_to_str(port: QuantumSymbol) -> str:
|
76
|
-
|
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
|
77
84
|
|
78
85
|
|
79
86
|
def _evaluated_one_operand_to_str(operand: FunctionClosure) -> str:
|
@@ -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)
|