classiq 0.78.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/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.0.dist-info}/METADATA +1 -1
- {classiq-0.78.0.dist-info → classiq-0.79.0.dist-info}/RECORD +48 -45
- {classiq-0.78.0.dist-info → classiq-0.79.0.dist-info}/WHEEL +0 -0
classiq/interface/_version.py
CHANGED
@@ -122,12 +122,16 @@ def get_arithmetic_params(
|
|
122
122
|
def compute_arithmetic_result_type(
|
123
123
|
expr_str: str, var_types: dict[str, QuantumType], machine_precision: int
|
124
124
|
) -> QuantumNumeric:
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
125
|
+
one_qbit_qnum = QuantumNumeric(
|
126
|
+
size=Expression(expr="1"),
|
127
|
+
is_signed=Expression(expr="False"),
|
128
|
+
fraction_digits=Expression(expr="0"),
|
129
|
+
)
|
130
|
+
if is_zero(expr_str):
|
131
|
+
one_qbit_qnum.set_bounds((0, 0))
|
132
|
+
return one_qbit_qnum
|
133
|
+
if is_bool(expr_str):
|
134
|
+
return one_qbit_qnum
|
131
135
|
arith_param = get_arithmetic_params(expr_str, var_types, machine_precision)
|
132
136
|
return register_info_to_quantum_type(
|
133
137
|
arith_param.outputs[ARITHMETIC_EXPRESSION_RESULT_NAME]
|
@@ -535,14 +535,11 @@ class Multiplier(BinaryOpWithFloatInputs):
|
|
535
535
|
elif isinstance(right_arg, float) and right_arg == 1.0:
|
536
536
|
assert isinstance(left_arg, RegisterArithmeticInfo)
|
537
537
|
return left_arg.size
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
and largest_bound > 0
|
544
|
-
)
|
545
|
-
return max(1, integer_places + fraction_places + extra_sign_bit)
|
538
|
+
if bounds[0] == 0 and bounds[1] == 0:
|
539
|
+
return max(1, fraction_places)
|
540
|
+
|
541
|
+
integer_part_size = number_utils.bounds_to_integer_part_size(*bounds)
|
542
|
+
return integer_part_size + fraction_places
|
546
543
|
|
547
544
|
def garbage_output_size(self) -> pydantic.NonNegativeInt:
|
548
545
|
return max(
|
@@ -610,7 +607,7 @@ class Power(BinaryOpParams[RegisterArithmeticInfo, pydantic.PositiveInt]):
|
|
610
607
|
)
|
611
608
|
for bound in self.left_arg.bounds
|
612
609
|
]
|
613
|
-
if (self.right_arg % 2) or min(bounds) >= 0:
|
610
|
+
if (self.right_arg % 2) or min(bounds) >= 0 or max(bounds) <= 0:
|
614
611
|
return (
|
615
612
|
number_utils.limit_fraction_places(
|
616
613
|
bounds[0] ** self.right_arg,
|
@@ -633,8 +630,8 @@ class Power(BinaryOpParams[RegisterArithmeticInfo, pydantic.PositiveInt]):
|
|
633
630
|
fraction_places = min(self.machine_precision, self.expected_fraction_places())
|
634
631
|
bounds = self._get_result_bounds()
|
635
632
|
size = number_utils.bounds_to_integer_part_size(*bounds) + fraction_places
|
636
|
-
if bounds[0] == bounds[1]:
|
637
|
-
size = 1
|
633
|
+
if bounds[0] == 0 and bounds[1] == 0:
|
634
|
+
size = max(1, fraction_places)
|
638
635
|
return RegisterArithmeticInfo(
|
639
636
|
size=size,
|
640
637
|
is_signed=self.left_arg.is_signed and (self.right_arg % 2 == 1),
|
@@ -35,20 +35,13 @@ def _is_int(val: Any) -> TypeGuard[Union[int, sympy.Basic]]:
|
|
35
35
|
return isinstance(val, int)
|
36
36
|
|
37
37
|
|
38
|
-
class
|
39
|
-
def __init__(
|
40
|
-
self,
|
41
|
-
handle: HandleBinding,
|
42
|
-
element_type: "ConcreteClassicalType",
|
43
|
-
length: "ExpressionValue",
|
44
|
-
) -> None:
|
38
|
+
class ClassicalSequenceProxy(NonSymbolicExpr, ClassicalProxy):
|
39
|
+
def __init__(self, handle: HandleBinding) -> None:
|
45
40
|
super().__init__(handle)
|
46
|
-
self._element_type = element_type
|
47
|
-
self._length = length
|
48
41
|
|
49
42
|
@property
|
50
43
|
def fields(self) -> Mapping[str, "ExpressionValue"]:
|
51
|
-
return {"len": self.
|
44
|
+
return {"len": self.length}
|
52
45
|
|
53
46
|
@property
|
54
47
|
def type_name(self) -> str:
|
@@ -56,7 +49,7 @@ class ClassicalArrayProxy(NonSymbolicExpr, ClassicalProxy):
|
|
56
49
|
|
57
50
|
@property
|
58
51
|
def length(self) -> "ExpressionValue":
|
59
|
-
|
52
|
+
raise NotImplementedError
|
60
53
|
|
61
54
|
def __getitem__(
|
62
55
|
self, key: Union[slice, int, Integer, ClassicalProxy]
|
@@ -73,17 +66,12 @@ class ClassicalArrayProxy(NonSymbolicExpr, ClassicalProxy):
|
|
73
66
|
stop = int(stop_)
|
74
67
|
if start >= stop:
|
75
68
|
raise ClassiqIndexError("Array slice has non-positive length")
|
76
|
-
if start < 0 or (isinstance(self.
|
69
|
+
if start < 0 or (isinstance(self.length, int) and stop > self.length):
|
77
70
|
raise ClassiqIndexError("Array slice is out of bounds")
|
78
|
-
return
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
end=Expression(expr=str(stop_)),
|
83
|
-
),
|
84
|
-
self._element_type,
|
85
|
-
stop_ - start_,
|
86
|
-
)
|
71
|
+
return self.get_slice_at(start_, stop_)
|
72
|
+
|
73
|
+
def get_slice_at(self, start: Any, stop: Any) -> ClassicalProxy:
|
74
|
+
raise NotImplementedError
|
87
75
|
|
88
76
|
def _get_subscript(
|
89
77
|
self, index_: Union[int, Integer, ClassicalProxy]
|
@@ -94,10 +82,89 @@ class ClassicalArrayProxy(NonSymbolicExpr, ClassicalProxy):
|
|
94
82
|
raise ClassiqIndexError(
|
95
83
|
"Array index is out of bounds (negative indices are not supported)"
|
96
84
|
)
|
97
|
-
if isinstance(self.
|
85
|
+
if isinstance(self.length, int) and index >= self.length:
|
98
86
|
raise ClassiqIndexError("Array index is out of bounds")
|
87
|
+
return self.get_subscript_at(index_)
|
88
|
+
|
89
|
+
def get_subscript_at(self, index: Any) -> ClassicalProxy:
|
90
|
+
raise NotImplementedError
|
91
|
+
|
92
|
+
|
93
|
+
class ClassicalArrayProxy(ClassicalSequenceProxy):
|
94
|
+
def __init__(
|
95
|
+
self,
|
96
|
+
handle: HandleBinding,
|
97
|
+
element_type: "ConcreteClassicalType",
|
98
|
+
length: "ExpressionValue",
|
99
|
+
) -> None:
|
100
|
+
super().__init__(handle)
|
101
|
+
self._element_type = element_type
|
102
|
+
if _is_int(length):
|
103
|
+
length = int(length)
|
104
|
+
self._length = length
|
105
|
+
|
106
|
+
@property
|
107
|
+
def length(self) -> "ExpressionValue":
|
108
|
+
return self._length
|
109
|
+
|
110
|
+
def get_slice_at(self, start: Any, stop: Any) -> ClassicalProxy:
|
111
|
+
return ClassicalArrayProxy(
|
112
|
+
SlicedHandleBinding(
|
113
|
+
base_handle=self.handle,
|
114
|
+
start=Expression(expr=str(start)),
|
115
|
+
end=Expression(expr=str(stop)),
|
116
|
+
),
|
117
|
+
self._element_type,
|
118
|
+
stop - start,
|
119
|
+
)
|
120
|
+
|
121
|
+
def get_subscript_at(self, index: Any) -> ClassicalProxy:
|
99
122
|
return self._element_type.get_classical_proxy(
|
100
123
|
SubscriptHandleBinding(
|
101
|
-
base_handle=self._handle, index=Expression(expr=str(
|
124
|
+
base_handle=self._handle, index=Expression(expr=str(index))
|
125
|
+
)
|
126
|
+
)
|
127
|
+
|
128
|
+
|
129
|
+
class ClassicalTupleProxy(ClassicalSequenceProxy):
|
130
|
+
def __init__(
|
131
|
+
self, handle: HandleBinding, element_types: list["ConcreteClassicalType"]
|
132
|
+
) -> None:
|
133
|
+
super().__init__(handle)
|
134
|
+
self._element_types = element_types
|
135
|
+
|
136
|
+
@property
|
137
|
+
def length(self) -> int:
|
138
|
+
return len(self._element_types)
|
139
|
+
|
140
|
+
def get_slice_at(self, start: Any, stop: Any) -> ClassicalProxy:
|
141
|
+
handle = SlicedHandleBinding(
|
142
|
+
base_handle=self.handle,
|
143
|
+
start=Expression(expr=str(start)),
|
144
|
+
end=Expression(expr=str(stop)),
|
145
|
+
)
|
146
|
+
if (_is_int(start) or start is None) and (_is_int(stop) or stop is None):
|
147
|
+
return ClassicalTupleProxy(
|
148
|
+
handle, self._element_types.__getitem__(slice(start, stop))
|
149
|
+
)
|
150
|
+
return ClassicalArrayProxy(
|
151
|
+
handle,
|
152
|
+
self._element_types[0].get_raw_type(),
|
153
|
+
stop - start,
|
154
|
+
)
|
155
|
+
|
156
|
+
def get_subscript_at(self, index: Any) -> ClassicalProxy:
|
157
|
+
handle = SubscriptHandleBinding(
|
158
|
+
base_handle=self._handle, index=Expression(expr=str(index))
|
159
|
+
)
|
160
|
+
if _is_int(index):
|
161
|
+
return self._element_types[int(index)].get_classical_proxy(handle)
|
162
|
+
return (
|
163
|
+
self._element_types[0]
|
164
|
+
.get_raw_type()
|
165
|
+
.get_classical_proxy(
|
166
|
+
SubscriptHandleBinding(
|
167
|
+
base_handle=self._handle, index=Expression(expr=str(index))
|
168
|
+
)
|
102
169
|
)
|
103
170
|
)
|
@@ -1,6 +1,7 @@
|
|
1
1
|
from classiq.interface.exceptions import ClassiqInternalExpansionError
|
2
2
|
from classiq.interface.generator.expressions.proxies.classical.classical_array_proxy import (
|
3
3
|
ClassicalArrayProxy,
|
4
|
+
ClassicalTupleProxy,
|
4
5
|
)
|
5
6
|
from classiq.interface.generator.expressions.proxies.classical.classical_proxy import (
|
6
7
|
ClassicalProxy,
|
@@ -14,6 +15,7 @@ from classiq.interface.generator.expressions.proxies.classical.classical_struct_
|
|
14
15
|
from classiq.interface.generator.functions.classical_type import (
|
15
16
|
ClassicalArray,
|
16
17
|
ClassicalList,
|
18
|
+
ClassicalTuple,
|
17
19
|
ClassicalType,
|
18
20
|
)
|
19
21
|
from classiq.interface.generator.functions.type_name import Struct
|
@@ -26,6 +28,8 @@ def get_proxy_type(proxy: ClassicalProxy) -> ClassicalType:
|
|
26
28
|
if not isinstance(proxy.length, int):
|
27
29
|
return ClassicalList(element_type=proxy._element_type)
|
28
30
|
return ClassicalArray(element_type=proxy._element_type, size=proxy.length)
|
31
|
+
if isinstance(proxy, ClassicalTupleProxy):
|
32
|
+
return ClassicalTuple(element_types=proxy._element_types)
|
29
33
|
if isinstance(proxy, ClassicalStructProxy):
|
30
34
|
classical_type = Struct(name=proxy._decl.name)
|
31
35
|
classical_type.set_classical_struct_decl(proxy._decl)
|
@@ -1,3 +1,4 @@
|
|
1
|
+
from itertools import chain
|
1
2
|
from typing import TYPE_CHECKING, Any, Literal
|
2
3
|
|
3
4
|
import pydantic
|
@@ -11,6 +12,7 @@ from classiq.interface.generator.expressions.proxies.classical.any_classical_val
|
|
11
12
|
)
|
12
13
|
from classiq.interface.generator.expressions.proxies.classical.classical_array_proxy import (
|
13
14
|
ClassicalArrayProxy,
|
15
|
+
ClassicalTupleProxy,
|
14
16
|
)
|
15
17
|
from classiq.interface.generator.expressions.proxies.classical.classical_proxy import (
|
16
18
|
ClassicalProxy,
|
@@ -47,6 +49,10 @@ class ClassicalType(HashableASTNode):
|
|
47
49
|
def is_purely_declarative(self) -> bool:
|
48
50
|
return not self._is_generative
|
49
51
|
|
52
|
+
@property
|
53
|
+
def is_purely_generative(self) -> bool:
|
54
|
+
return self._is_generative
|
55
|
+
|
50
56
|
def get_classical_proxy(self, handle: HandleBinding) -> ClassicalProxy:
|
51
57
|
return ClassicalScalarProxy(handle, self)
|
52
58
|
|
@@ -59,6 +65,9 @@ class ClassicalType(HashableASTNode):
|
|
59
65
|
res._is_generative = False
|
60
66
|
return res
|
61
67
|
|
68
|
+
def get_raw_type(self) -> "ConcreteClassicalType":
|
69
|
+
return self # type:ignore[return-value]
|
70
|
+
|
62
71
|
|
63
72
|
class Integer(ClassicalType):
|
64
73
|
kind: Literal["int"]
|
@@ -109,6 +118,16 @@ class ClassicalList(ClassicalType):
|
|
109
118
|
def is_purely_declarative(self) -> bool:
|
110
119
|
return super().is_purely_declarative and self.element_type.is_purely_declarative
|
111
120
|
|
121
|
+
@property
|
122
|
+
def is_purely_generative(self) -> bool:
|
123
|
+
return super().is_purely_generative and self.element_type.is_purely_generative
|
124
|
+
|
125
|
+
def get_raw_type(self) -> "ConcreteClassicalType":
|
126
|
+
raw_type = ClassicalList(element_type=self.element_type.get_raw_type())
|
127
|
+
if self._is_generative:
|
128
|
+
raw_type.set_generative()
|
129
|
+
return raw_type
|
130
|
+
|
112
131
|
|
113
132
|
class StructMetaType(ClassicalType):
|
114
133
|
kind: Literal["type_proxy"]
|
@@ -143,6 +162,61 @@ class ClassicalArray(ClassicalType):
|
|
143
162
|
def is_purely_declarative(self) -> bool:
|
144
163
|
return super().is_purely_declarative and self.element_type.is_purely_declarative
|
145
164
|
|
165
|
+
@property
|
166
|
+
def is_purely_generative(self) -> bool:
|
167
|
+
return super().is_purely_generative and self.element_type.is_purely_generative
|
168
|
+
|
169
|
+
def get_raw_type(self) -> "ConcreteClassicalType":
|
170
|
+
raw_type = ClassicalList(element_type=self.element_type.get_raw_type())
|
171
|
+
if self._is_generative:
|
172
|
+
raw_type.set_generative()
|
173
|
+
return raw_type
|
174
|
+
|
175
|
+
|
176
|
+
class ClassicalTuple(ClassicalType):
|
177
|
+
kind: Literal["tuple"]
|
178
|
+
element_types: list["ConcreteClassicalType"]
|
179
|
+
|
180
|
+
@pydantic.model_validator(mode="before")
|
181
|
+
@classmethod
|
182
|
+
def _set_kind(cls, values: Any) -> dict[str, Any]:
|
183
|
+
return values_with_discriminator(values, "kind", "tuple")
|
184
|
+
|
185
|
+
def get_classical_proxy(self, handle: HandleBinding) -> ClassicalProxy:
|
186
|
+
return ClassicalTupleProxy(handle=handle, element_types=self.element_types)
|
187
|
+
|
188
|
+
@property
|
189
|
+
def expressions(self) -> list[Expression]:
|
190
|
+
return list(
|
191
|
+
chain.from_iterable(
|
192
|
+
element_type.expressions for element_type in self.element_types
|
193
|
+
)
|
194
|
+
)
|
195
|
+
|
196
|
+
@property
|
197
|
+
def is_purely_declarative(self) -> bool:
|
198
|
+
return super().is_purely_declarative and all(
|
199
|
+
element_type.is_purely_declarative for element_type in self.element_types
|
200
|
+
)
|
201
|
+
|
202
|
+
@property
|
203
|
+
def is_purely_generative(self) -> bool:
|
204
|
+
return super().is_purely_generative and all(
|
205
|
+
element_type.is_purely_generative for element_type in self.element_types
|
206
|
+
)
|
207
|
+
|
208
|
+
def get_raw_type(self) -> "ConcreteClassicalType":
|
209
|
+
if len(self.element_types) == 0:
|
210
|
+
return self
|
211
|
+
raw_type = ClassicalList(element_type=self.element_types[0].get_raw_type())
|
212
|
+
if self._is_generative:
|
213
|
+
raw_type.set_generative()
|
214
|
+
return raw_type
|
215
|
+
|
216
|
+
@property
|
217
|
+
def size(self) -> int:
|
218
|
+
return len(self.element_types)
|
219
|
+
|
146
220
|
|
147
221
|
class OpaqueHandle(ClassicalType):
|
148
222
|
pass
|
@@ -6,6 +6,7 @@ from classiq.interface.generator.functions.classical_type import (
|
|
6
6
|
Bool,
|
7
7
|
ClassicalArray,
|
8
8
|
ClassicalList,
|
9
|
+
ClassicalTuple,
|
9
10
|
Estimation,
|
10
11
|
Histogram,
|
11
12
|
Integer,
|
@@ -32,6 +33,7 @@ ConcreteClassicalType = Annotated[
|
|
32
33
|
StructMetaType,
|
33
34
|
TypeName,
|
34
35
|
ClassicalArray,
|
36
|
+
ClassicalTuple,
|
35
37
|
VQEResult,
|
36
38
|
Histogram,
|
37
39
|
Estimation,
|
@@ -41,6 +43,7 @@ ConcreteClassicalType = Annotated[
|
|
41
43
|
]
|
42
44
|
ClassicalList.model_rebuild()
|
43
45
|
ClassicalArray.model_rebuild()
|
46
|
+
ClassicalTuple.model_rebuild()
|
44
47
|
|
45
48
|
NativePythonClassicalTypes = (int, float, bool, list)
|
46
49
|
PythonClassicalPydanticTypes = (Enum,)
|
@@ -28,7 +28,10 @@ from classiq.interface.model.quantum_type import (
|
|
28
28
|
)
|
29
29
|
|
30
30
|
if TYPE_CHECKING:
|
31
|
-
from classiq.interface.generator.functions.concrete_types import
|
31
|
+
from classiq.interface.generator.functions.concrete_types import (
|
32
|
+
ConcreteClassicalType,
|
33
|
+
ConcreteQuantumType,
|
34
|
+
)
|
32
35
|
from classiq.interface.generator.types.struct_declaration import StructDeclaration
|
33
36
|
|
34
37
|
|
@@ -138,16 +141,42 @@ class TypeName(ClassicalType, QuantumType):
|
|
138
141
|
@property
|
139
142
|
def is_purely_declarative(self) -> bool:
|
140
143
|
if self.is_enum:
|
141
|
-
return
|
142
|
-
return
|
144
|
+
return not self.is_generative
|
145
|
+
return all(
|
143
146
|
field_type.is_purely_declarative
|
144
147
|
for field_type in self.classical_struct_decl.variables.values()
|
145
148
|
)
|
146
149
|
|
150
|
+
@property
|
151
|
+
def is_purely_generative(self) -> bool:
|
152
|
+
if self.is_enum:
|
153
|
+
return self.is_generative
|
154
|
+
return all(
|
155
|
+
field_type.is_purely_generative
|
156
|
+
for field_type in self.classical_struct_decl.variables.values()
|
157
|
+
)
|
158
|
+
|
147
159
|
@property
|
148
160
|
def is_enum(self) -> bool:
|
149
161
|
return not self.has_fields and not self.has_classical_struct_decl
|
150
162
|
|
163
|
+
def get_raw_type(self) -> "ConcreteClassicalType":
|
164
|
+
if self.is_enum:
|
165
|
+
return self
|
166
|
+
if TYPE_CHECKING:
|
167
|
+
assert self._classical_struct_decl is not None
|
168
|
+
raw_type = TypeName(name=self.name)
|
169
|
+
raw_decl = self._classical_struct_decl.model_copy(
|
170
|
+
update=dict(
|
171
|
+
variables={
|
172
|
+
field_name: field_type.get_raw_type()
|
173
|
+
for field_name, field_type in self._classical_struct_decl.variables.items()
|
174
|
+
}
|
175
|
+
)
|
176
|
+
)
|
177
|
+
raw_type.set_classical_struct_decl(raw_decl)
|
178
|
+
return raw_type
|
179
|
+
|
151
180
|
|
152
181
|
class Enum(TypeName):
|
153
182
|
pass
|
@@ -210,7 +210,9 @@ class FunctionDebugInfoInterface(pydantic.BaseModel):
|
|
210
210
|
return generated_name
|
211
211
|
|
212
212
|
if isinstance(back_ref, QuantumFunctionCall):
|
213
|
-
name = generate_original_function_name(back_ref.func_name)
|
213
|
+
name = generate_original_function_name(back_ref.func_name).removeprefix(
|
214
|
+
ARITH_ENGINE_PREFIX
|
215
|
+
)
|
214
216
|
return self.add_suffix_from_generated_name(generated_name, name)
|
215
217
|
|
216
218
|
statement_kind: str = back_ref.kind
|
@@ -0,0 +1,47 @@
|
|
1
|
+
import ast
|
2
|
+
from typing import Any
|
3
|
+
|
4
|
+
from classiq.interface.ast_node import ASTNode
|
5
|
+
from classiq.interface.debug_info.debug_info import DebugInfoCollection
|
6
|
+
from classiq.interface.generator.expressions.expression import Expression
|
7
|
+
from classiq.interface.generator.functions.classical_type import ClassicalType
|
8
|
+
from classiq.interface.generator.functions.type_qualifier import TypeQualifier
|
9
|
+
from classiq.interface.generator.visitor import Transformer, Visitor
|
10
|
+
from classiq.interface.model.model import Model
|
11
|
+
from classiq.interface.model.port_declaration import AnonPortDeclaration
|
12
|
+
|
13
|
+
from classiq.model_expansions.transformers.model_renamer import ExprNormalizer
|
14
|
+
|
15
|
+
|
16
|
+
class ModelNormalizer(Visitor):
|
17
|
+
def visit(self, node: Any) -> None:
|
18
|
+
if isinstance(node, ASTNode):
|
19
|
+
node.model_config["frozen"] = False
|
20
|
+
node.source_ref = None
|
21
|
+
node.back_ref = None
|
22
|
+
if hasattr(node, "uuid"):
|
23
|
+
node.uuid = None
|
24
|
+
super().visit(node)
|
25
|
+
|
26
|
+
def visit_Model(self, model: Model) -> None:
|
27
|
+
model.debug_info = DebugInfoCollection()
|
28
|
+
model.functions.sort(key=lambda x: x.name)
|
29
|
+
self.generic_visit(model)
|
30
|
+
|
31
|
+
def visit_Expression(self, expr: Expression) -> None:
|
32
|
+
expr.expr = ast.unparse(ExprNormalizer().visit(ast.parse(expr.expr)))
|
33
|
+
|
34
|
+
def visit_AnonPortDeclaration(self, decl: AnonPortDeclaration) -> None:
|
35
|
+
decl.type_qualifier = TypeQualifier.Quantum
|
36
|
+
|
37
|
+
|
38
|
+
class ClearModelInternals(Transformer):
|
39
|
+
def visit_Expression(self, expr: Expression) -> Expression:
|
40
|
+
if expr._evaluated_expr is not None and not expr._evaluated_expr.is_constant():
|
41
|
+
expr._evaluated_expr = None
|
42
|
+
return expr
|
43
|
+
|
44
|
+
def visit_ClassicalType(self, classical_type: ClassicalType) -> ClassicalType:
|
45
|
+
return type(classical_type).model_validate_json(
|
46
|
+
classical_type.model_dump_json()
|
47
|
+
)
|
@@ -1 +1 @@
|
|
1
|
-
INTERFACE_VERSION = "
|
1
|
+
INTERFACE_VERSION = "11"
|
@@ -0,0 +1,12 @@
|
|
1
|
+
from typing import Literal, Optional
|
2
|
+
|
3
|
+
from classiq.interface.helpers.custom_pydantic_types import PydanticFloatTuple
|
4
|
+
from classiq.interface.model.handle_binding import ConcreteHandleBinding
|
5
|
+
from classiq.interface.model.quantum_statement import QuantumOperation
|
6
|
+
|
7
|
+
|
8
|
+
class SetBoundsStatement(QuantumOperation):
|
9
|
+
kind: Literal["SetBoundsStatement"]
|
10
|
+
|
11
|
+
target: ConcreteHandleBinding
|
12
|
+
bounds: Optional[PydanticFloatTuple]
|
classiq/interface/model/model.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
from collections import Counter
|
1
|
+
from collections import Counter, defaultdict
|
2
2
|
from collections.abc import Mapping
|
3
|
-
from typing import Any, Literal, NewType
|
3
|
+
from typing import Annotated, Any, Literal, NewType
|
4
4
|
|
5
5
|
import pydantic
|
6
6
|
|
@@ -99,9 +99,13 @@ class Model(VersionedModel, ASTNode):
|
|
99
99
|
debug_info: DebugInfoCollection = pydantic.Field(
|
100
100
|
default_factory=DebugInfoCollection
|
101
101
|
)
|
102
|
-
functions_compilation_metadata:
|
103
|
-
|
104
|
-
|
102
|
+
functions_compilation_metadata: defaultdict[
|
103
|
+
str,
|
104
|
+
Annotated[
|
105
|
+
CompilationMetadata,
|
106
|
+
pydantic.Field(default_factory=CompilationMetadata),
|
107
|
+
],
|
108
|
+
] = pydantic.Field(default_factory=lambda: defaultdict(CompilationMetadata))
|
105
109
|
|
106
110
|
@property
|
107
111
|
def main_func(self) -> NativeFunctionDefinition:
|
@@ -22,6 +22,7 @@ from classiq.interface.generator.expressions.proxies.quantum.qmod_qscalar_proxy
|
|
22
22
|
from classiq.interface.generator.expressions.proxies.quantum.qmod_sized_proxy import (
|
23
23
|
QmodSizedProxy,
|
24
24
|
)
|
25
|
+
from classiq.interface.helpers.custom_pydantic_types import PydanticFloatTuple
|
25
26
|
from classiq.interface.helpers.pydantic_model_helpers import values_with_discriminator
|
26
27
|
from classiq.interface.model.handle_binding import HandleBinding
|
27
28
|
|
@@ -193,6 +194,8 @@ class QuantumNumeric(QuantumScalar):
|
|
193
194
|
is_signed: Optional[Expression] = pydantic.Field(default=None)
|
194
195
|
fraction_digits: Optional[Expression] = pydantic.Field(default=None)
|
195
196
|
|
197
|
+
_bounds: Optional[PydanticFloatTuple] = pydantic.PrivateAttr(default=None)
|
198
|
+
|
196
199
|
@pydantic.model_validator(mode="before")
|
197
200
|
@classmethod
|
198
201
|
def _set_kind(cls, values: Any) -> dict[str, Any]:
|
@@ -287,6 +290,22 @@ class QuantumNumeric(QuantumScalar):
|
|
287
290
|
exprs.append(self.fraction_digits)
|
288
291
|
return exprs
|
289
292
|
|
293
|
+
def get_bounds(self) -> Optional[tuple[float, float]]:
|
294
|
+
return self._bounds
|
295
|
+
|
296
|
+
def set_bounds(self, bounds: Optional[tuple[float, float]]) -> None:
|
297
|
+
self._bounds = bounds
|
298
|
+
|
299
|
+
def reset_bounds(self) -> None:
|
300
|
+
self.set_bounds(None)
|
301
|
+
|
302
|
+
def get_maximal_bounds(self) -> tuple[float, float]:
|
303
|
+
return RegisterArithmeticInfo.get_maximal_bounds(
|
304
|
+
size=self.size_in_bits,
|
305
|
+
is_signed=self.sign_value,
|
306
|
+
fraction_places=self.fraction_digits_value,
|
307
|
+
)
|
308
|
+
|
290
309
|
|
291
310
|
class RegisterQuantumType(BaseModel):
|
292
311
|
quantum_types: "ConcreteQuantumType"
|
@@ -308,6 +327,7 @@ def register_info_to_quantum_type(reg_info: RegisterArithmeticInfo) -> QuantumNu
|
|
308
327
|
result.set_size_in_bits(reg_info.size)
|
309
328
|
result.is_signed = Expression(expr=str(reg_info.is_signed))
|
310
329
|
result.fraction_digits = Expression(expr=str(reg_info.fraction_places))
|
330
|
+
result.set_bounds(reg_info.bounds)
|
311
331
|
return result
|
312
332
|
|
313
333
|
|
@@ -315,17 +335,19 @@ UNRESOLVED_SIZE = 1000
|
|
315
335
|
|
316
336
|
|
317
337
|
def quantum_var_to_register(name: str, qtype: QuantumType) -> RegisterUserInput:
|
338
|
+
signed: bool = False
|
339
|
+
fraction_places: int = 0
|
340
|
+
bounds: Optional[tuple[float, float]] = None
|
318
341
|
if isinstance(qtype, QuantumNumeric):
|
319
342
|
signed = qtype.sign_value
|
320
343
|
fraction_places = qtype.fraction_digits_value
|
321
|
-
|
322
|
-
signed = False
|
323
|
-
fraction_places = 0
|
344
|
+
bounds = qtype.get_bounds()
|
324
345
|
return RegisterUserInput(
|
325
346
|
name=name,
|
326
347
|
size=qtype.size_in_bits if qtype.has_size_in_bits else UNRESOLVED_SIZE,
|
327
348
|
is_signed=signed,
|
328
349
|
fraction_places=fraction_places,
|
350
|
+
bounds=bounds,
|
329
351
|
)
|
330
352
|
|
331
353
|
|
@@ -5,6 +5,7 @@ from pydantic import Field
|
|
5
5
|
from classiq.interface.model.allocate import Allocate
|
6
6
|
from classiq.interface.model.bind_operation import BindOperation
|
7
7
|
from classiq.interface.model.block import Block
|
8
|
+
from classiq.interface.model.bounds import SetBoundsStatement
|
8
9
|
from classiq.interface.model.classical_if import ClassicalIf
|
9
10
|
from classiq.interface.model.control import Control
|
10
11
|
from classiq.interface.model.inplace_binary_operation import InplaceBinaryOperation
|
@@ -51,6 +52,7 @@ ConcreteQuantumStatement = Annotated[
|
|
51
52
|
Compute,
|
52
53
|
Action,
|
53
54
|
Uncompute,
|
55
|
+
SetBoundsStatement,
|
54
56
|
],
|
55
57
|
Field(..., discriminator="kind"),
|
56
58
|
]
|