classiq 0.85.0__py3-none-any.whl → 0.86.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/applications/combinatorial_optimization/combinatorial_problem.py +4 -3
- classiq/evaluators/qmod_annotated_expression.py +207 -0
- classiq/evaluators/qmod_expression_visitors/__init__.py +0 -0
- classiq/evaluators/qmod_expression_visitors/qmod_expression_bwc.py +134 -0
- classiq/evaluators/qmod_expression_visitors/qmod_expression_evaluator.py +232 -0
- classiq/evaluators/qmod_expression_visitors/qmod_expression_renamer.py +44 -0
- classiq/evaluators/qmod_expression_visitors/qmod_expression_simplifier.py +308 -0
- classiq/evaluators/qmod_node_evaluators/__init__.py +0 -0
- classiq/evaluators/qmod_node_evaluators/attribute_evaluation.py +112 -0
- classiq/evaluators/qmod_node_evaluators/binary_op_evaluation.py +132 -0
- classiq/evaluators/qmod_node_evaluators/bool_op_evaluation.py +70 -0
- classiq/evaluators/qmod_node_evaluators/classical_function_evaluation.py +311 -0
- classiq/evaluators/qmod_node_evaluators/compare_evaluation.py +107 -0
- classiq/evaluators/qmod_node_evaluators/constant_evaluation.py +67 -0
- classiq/evaluators/qmod_node_evaluators/list_evaluation.py +107 -0
- classiq/evaluators/qmod_node_evaluators/measurement_evaluation.py +25 -0
- classiq/evaluators/qmod_node_evaluators/name_evaluation.py +50 -0
- classiq/evaluators/qmod_node_evaluators/struct_instantiation_evaluation.py +66 -0
- classiq/evaluators/qmod_node_evaluators/subscript_evaluation.py +225 -0
- classiq/evaluators/qmod_node_evaluators/unary_op_evaluation.py +58 -0
- classiq/evaluators/qmod_node_evaluators/utils.py +80 -0
- classiq/execution/execution_session.py +4 -0
- classiq/interface/_version.py +1 -1
- classiq/interface/analyzer/analysis_params.py +1 -1
- classiq/interface/analyzer/result.py +1 -1
- classiq/interface/executor/quantum_code.py +2 -2
- classiq/interface/generator/arith/arithmetic_expression_validator.py +5 -1
- classiq/interface/generator/arith/binary_ops.py +43 -51
- classiq/interface/generator/arith/number_utils.py +3 -2
- classiq/interface/generator/arith/register_user_input.py +15 -0
- classiq/interface/generator/arith/unary_ops.py +32 -28
- classiq/interface/generator/expressions/expression_types.py +2 -2
- classiq/interface/generator/expressions/proxies/classical/qmod_struct_instance.py +7 -0
- classiq/interface/generator/functions/classical_function_declaration.py +0 -4
- classiq/interface/generator/functions/classical_type.py +0 -32
- classiq/interface/generator/generated_circuit_data.py +2 -0
- classiq/interface/generator/quantum_program.py +6 -1
- classiq/interface/generator/synthesis_metadata/synthesis_execution_data.py +29 -0
- classiq/interface/ide/operation_registry.py +2 -2
- classiq/interface/ide/visual_model.py +22 -1
- classiq/interface/model/quantum_type.py +67 -33
- classiq/model_expansions/arithmetic.py +115 -0
- classiq/model_expansions/arithmetic_compute_result_attrs.py +71 -0
- classiq/model_expansions/atomic_expression_functions_defs.py +5 -5
- classiq/model_expansions/generative_functions.py +15 -2
- classiq/model_expansions/interpreters/base_interpreter.py +7 -0
- classiq/model_expansions/interpreters/generative_interpreter.py +2 -0
- classiq/model_expansions/quantum_operations/call_emitter.py +5 -2
- classiq/model_expansions/scope_initialization.py +2 -0
- classiq/model_expansions/sympy_conversion/sympy_to_python.py +1 -1
- classiq/model_expansions/transformers/type_modifier_inference.py +5 -0
- classiq/model_expansions/visitors/boolean_expression_transformers.py +1 -1
- classiq/qmod/builtins/operations.py +1 -1
- classiq/qmod/declaration_inferrer.py +5 -3
- classiq/qmod/write_qmod.py +3 -1
- {classiq-0.85.0.dist-info → classiq-0.86.1.dist-info}/METADATA +1 -1
- {classiq-0.85.0.dist-info → classiq-0.86.1.dist-info}/RECORD +59 -37
- /classiq/{model_expansions/sympy_conversion/arithmetics.py → evaluators/qmod_expression_visitors/sympy_wrappers.py} +0 -0
- {classiq-0.85.0.dist-info → classiq-0.86.1.dist-info}/WHEEL +0 -0
@@ -0,0 +1,308 @@
|
|
1
|
+
import ast
|
2
|
+
from typing import Any, Optional, cast
|
3
|
+
|
4
|
+
import sympy
|
5
|
+
|
6
|
+
from classiq.interface.exceptions import ClassiqInternalExpansionError
|
7
|
+
from classiq.interface.generator.arith.arithmetic import compute_arithmetic_result_type
|
8
|
+
from classiq.interface.generator.expressions.expression import Expression
|
9
|
+
from classiq.interface.model.handle_binding import HandleBinding
|
10
|
+
from classiq.interface.model.quantum_type import (
|
11
|
+
QuantumBit,
|
12
|
+
QuantumNumeric,
|
13
|
+
QuantumType,
|
14
|
+
)
|
15
|
+
|
16
|
+
from classiq.evaluators.qmod_annotated_expression import (
|
17
|
+
QmodAnnotatedExpression,
|
18
|
+
QmodExprNodeId,
|
19
|
+
)
|
20
|
+
from classiq.evaluators.qmod_expression_visitors.sympy_wrappers import (
|
21
|
+
BitwiseAnd,
|
22
|
+
BitwiseNot,
|
23
|
+
BitwiseOr,
|
24
|
+
BitwiseXor,
|
25
|
+
LShift,
|
26
|
+
RShift,
|
27
|
+
)
|
28
|
+
from classiq.evaluators.qmod_node_evaluators.utils import QmodType
|
29
|
+
|
30
|
+
_SYMPY_WRAPPERS = {
|
31
|
+
wrapper.__name__: wrapper
|
32
|
+
for wrapper in [
|
33
|
+
BitwiseAnd,
|
34
|
+
BitwiseNot,
|
35
|
+
BitwiseOr,
|
36
|
+
BitwiseXor,
|
37
|
+
LShift,
|
38
|
+
RShift,
|
39
|
+
]
|
40
|
+
}
|
41
|
+
|
42
|
+
|
43
|
+
class _VarMaskTransformer(ast.NodeTransformer):
|
44
|
+
def __init__(self, expr_val: QmodAnnotatedExpression) -> None:
|
45
|
+
self._expr_val = expr_val
|
46
|
+
self._mask_id = 0
|
47
|
+
self.masks: dict[str, QmodExprNodeId] = {}
|
48
|
+
self._assigned_masks: dict[HandleBinding, str] = {}
|
49
|
+
|
50
|
+
def _create_mask(self) -> str:
|
51
|
+
mask = f"x{self._mask_id}"
|
52
|
+
self._mask_id += 1
|
53
|
+
return mask
|
54
|
+
|
55
|
+
def visit(self, node: ast.AST) -> ast.AST:
|
56
|
+
if self._expr_val.has_value(node):
|
57
|
+
return ast.parse(str(self._expr_val.get_value(node)))
|
58
|
+
if self._expr_val.has_var(node):
|
59
|
+
var = self._expr_val.get_var(node)
|
60
|
+
if var in self._assigned_masks:
|
61
|
+
mask = self._assigned_masks[var]
|
62
|
+
else:
|
63
|
+
mask = self._create_mask()
|
64
|
+
self._assigned_masks[var] = mask
|
65
|
+
self.masks[mask] = id(node)
|
66
|
+
return ast.Name(id=mask)
|
67
|
+
if self._expr_val.has_quantum_subscript(node):
|
68
|
+
mask = self._create_mask()
|
69
|
+
self.masks[mask] = id(node)
|
70
|
+
return ast.Name(id=mask)
|
71
|
+
return self.generic_visit(node)
|
72
|
+
|
73
|
+
def visit_Subscript(self, node: ast.Subscript) -> ast.AST:
|
74
|
+
mask = self._create_mask()
|
75
|
+
self.masks[mask] = id(node)
|
76
|
+
return ast.Name(id=mask)
|
77
|
+
|
78
|
+
|
79
|
+
class _InverseVarMaskTransformer(ast.NodeTransformer):
|
80
|
+
def __init__(
|
81
|
+
self, expr_val: QmodAnnotatedExpression, masks: dict[str, QmodExprNodeId]
|
82
|
+
) -> None:
|
83
|
+
self._expr_val = expr_val
|
84
|
+
self._masks = masks
|
85
|
+
|
86
|
+
def visit_Name(self, node: ast.Name) -> Any:
|
87
|
+
name = node.id
|
88
|
+
if name in self._masks:
|
89
|
+
mask_id = self._masks[name]
|
90
|
+
if not self._expr_val.has_node(mask_id):
|
91
|
+
raise ClassiqInternalExpansionError
|
92
|
+
return ast.Name(id=ast.unparse(self._expr_val.get_node(mask_id)))
|
93
|
+
return node
|
94
|
+
|
95
|
+
|
96
|
+
class _SympyCompatibilityTransformer(ast.NodeTransformer):
|
97
|
+
def visit_BoolOp(self, node: ast.BoolOp) -> ast.Call:
|
98
|
+
if len(node.values) != 2:
|
99
|
+
raise ClassiqInternalExpansionError
|
100
|
+
node = cast(ast.BoolOp, self.generic_visit(node))
|
101
|
+
if isinstance(node.op, ast.Or):
|
102
|
+
sympy_func = "Or"
|
103
|
+
else:
|
104
|
+
sympy_func = "And"
|
105
|
+
return ast.Call(func=ast.Name(id=sympy_func), args=node.values, keywords=[])
|
106
|
+
|
107
|
+
def visit_UnaryOp(self, node: ast.UnaryOp) -> Any:
|
108
|
+
node = cast(ast.UnaryOp, self.generic_visit(node))
|
109
|
+
if isinstance(node.op, ast.Not):
|
110
|
+
sympy_func = "Not"
|
111
|
+
elif isinstance(node.op, ast.Invert):
|
112
|
+
sympy_func = BitwiseNot.__name__
|
113
|
+
else:
|
114
|
+
return node
|
115
|
+
return ast.Call(func=ast.Name(id=sympy_func), args=[node.operand], keywords=[])
|
116
|
+
|
117
|
+
def visit_Compare(self, node: ast.Compare) -> ast.Call:
|
118
|
+
if len(node.ops) != 1:
|
119
|
+
raise ClassiqInternalExpansionError
|
120
|
+
node = cast(ast.Compare, self.generic_visit(node))
|
121
|
+
op = node.ops[0]
|
122
|
+
if isinstance(op, ast.Eq):
|
123
|
+
sympy_func = "Eq"
|
124
|
+
elif isinstance(op, ast.NotEq):
|
125
|
+
sympy_func = "Ne"
|
126
|
+
elif isinstance(op, ast.Lt):
|
127
|
+
sympy_func = "Lt"
|
128
|
+
elif isinstance(op, ast.LtE):
|
129
|
+
sympy_func = "Le"
|
130
|
+
elif isinstance(op, ast.Gt):
|
131
|
+
sympy_func = "Gt"
|
132
|
+
elif isinstance(op, ast.GtE):
|
133
|
+
sympy_func = "Ge"
|
134
|
+
else:
|
135
|
+
raise ClassiqInternalExpansionError
|
136
|
+
return ast.Call(
|
137
|
+
func=ast.Name(id=sympy_func),
|
138
|
+
args=[node.left, node.comparators[0]],
|
139
|
+
keywords=[],
|
140
|
+
)
|
141
|
+
|
142
|
+
def visit_BinOp(self, node: ast.BinOp) -> Any:
|
143
|
+
node = cast(ast.BinOp, self.generic_visit(node))
|
144
|
+
if isinstance(node.op, ast.LShift):
|
145
|
+
sympy_func = LShift.__name__
|
146
|
+
elif isinstance(node.op, ast.RShift):
|
147
|
+
sympy_func = RShift.__name__
|
148
|
+
elif isinstance(node.op, ast.BitOr):
|
149
|
+
sympy_func = BitwiseOr.__name__
|
150
|
+
elif isinstance(node.op, ast.BitXor):
|
151
|
+
sympy_func = BitwiseXor.__name__
|
152
|
+
elif isinstance(node.op, ast.BitAnd):
|
153
|
+
sympy_func = BitwiseAnd.__name__
|
154
|
+
else:
|
155
|
+
return node
|
156
|
+
return ast.Call(
|
157
|
+
func=ast.Name(id=sympy_func), args=[node.left, node.right], keywords=[]
|
158
|
+
)
|
159
|
+
|
160
|
+
|
161
|
+
class _InverseSympyCompatibilityTransformer(ast.NodeTransformer):
|
162
|
+
def visit_Call(self, node: ast.Call) -> Any:
|
163
|
+
node = cast(ast.Call, self.generic_visit(node))
|
164
|
+
if not isinstance(node.func, ast.Name):
|
165
|
+
raise ClassiqInternalExpansionError
|
166
|
+
func = node.func.id
|
167
|
+
|
168
|
+
if (
|
169
|
+
func
|
170
|
+
in {
|
171
|
+
"Eq",
|
172
|
+
"Ne",
|
173
|
+
"Lt",
|
174
|
+
"Le",
|
175
|
+
"Gt",
|
176
|
+
"Ge",
|
177
|
+
LShift.__name__,
|
178
|
+
RShift.__name__,
|
179
|
+
BitwiseOr.__name__,
|
180
|
+
BitwiseXor.__name__,
|
181
|
+
BitwiseAnd.__name__,
|
182
|
+
}
|
183
|
+
and (len(node.args) != 2 or len(node.keywords) > 0)
|
184
|
+
) or (
|
185
|
+
func in {BitwiseNot.__name__}
|
186
|
+
and (len(node.args) != 2 or len(node.keywords) > 0)
|
187
|
+
):
|
188
|
+
raise ClassiqInternalExpansionError
|
189
|
+
|
190
|
+
if func == BitwiseNot.__name__:
|
191
|
+
return ast.UnaryOp(op=ast.Invert, operand=node.args[0])
|
192
|
+
|
193
|
+
if func == "Eq":
|
194
|
+
return ast.Compare(
|
195
|
+
left=node.args[0], ops=[ast.Eq()], comparators=[node.args[1]]
|
196
|
+
)
|
197
|
+
if func == "Ne":
|
198
|
+
return ast.Compare(
|
199
|
+
left=node.args[0], ops=[ast.NotEq()], comparators=[node.args[1]]
|
200
|
+
)
|
201
|
+
if func == "Lt":
|
202
|
+
return ast.Compare(
|
203
|
+
left=node.args[0], ops=[ast.Lt()], comparators=[node.args[1]]
|
204
|
+
)
|
205
|
+
if func == "Le":
|
206
|
+
return ast.Compare(
|
207
|
+
left=node.args[0], ops=[ast.LtE()], comparators=[node.args[1]]
|
208
|
+
)
|
209
|
+
if func == "Gt":
|
210
|
+
return ast.Compare(
|
211
|
+
left=node.args[0], ops=[ast.Gt()], comparators=[node.args[1]]
|
212
|
+
)
|
213
|
+
if func == "GtE":
|
214
|
+
return ast.Compare(
|
215
|
+
left=node.args[0], ops=[ast.GtE()], comparators=[node.args[1]]
|
216
|
+
)
|
217
|
+
|
218
|
+
if func == LShift.__name__:
|
219
|
+
return ast.BinOp(left=node.args[0], op=ast.LShift(), right=node.args[1])
|
220
|
+
if func == RShift.__name__:
|
221
|
+
return ast.BinOp(left=node.args[0], op=ast.RShift(), right=node.args[1])
|
222
|
+
if func == BitwiseOr.__name__:
|
223
|
+
return ast.BinOp(left=node.args[0], op=ast.BitOr(), right=node.args[1])
|
224
|
+
if func == BitwiseXor.__name__:
|
225
|
+
return ast.BinOp(left=node.args[0], op=ast.BitXor(), right=node.args[1])
|
226
|
+
if func == BitwiseAnd.__name__:
|
227
|
+
return ast.BinOp(left=node.args[0], op=ast.BitAnd(), right=node.args[1])
|
228
|
+
|
229
|
+
return node
|
230
|
+
|
231
|
+
def visit_UnaryOp(self, node: ast.UnaryOp) -> Any:
|
232
|
+
node = cast(ast.UnaryOp, self.generic_visit(node))
|
233
|
+
|
234
|
+
if isinstance(node.op, ast.Invert):
|
235
|
+
return ast.UnaryOp(op=ast.Not(), operand=node.operand)
|
236
|
+
|
237
|
+
return node
|
238
|
+
|
239
|
+
def visit_BinOp(self, node: ast.BinOp) -> Any:
|
240
|
+
node = cast(ast.BinOp, self.generic_visit(node))
|
241
|
+
|
242
|
+
if isinstance(node.op, ast.BitOr):
|
243
|
+
return ast.BoolOp(op=ast.Or(), values=[node.left, node.right])
|
244
|
+
if isinstance(node.op, ast.BitAnd):
|
245
|
+
return ast.BoolOp(op=ast.And(), values=[node.left, node.right])
|
246
|
+
|
247
|
+
return node
|
248
|
+
|
249
|
+
|
250
|
+
def _get_numeric_type(
|
251
|
+
expr_val: QmodAnnotatedExpression,
|
252
|
+
expr_type: QmodType,
|
253
|
+
simplified_expr: str,
|
254
|
+
masks: dict[str, QmodExprNodeId],
|
255
|
+
machine_precision: int,
|
256
|
+
) -> Optional[QuantumNumeric]:
|
257
|
+
if isinstance(expr_type, QuantumBit):
|
258
|
+
return QuantumNumeric(
|
259
|
+
size=Expression(expr="1"),
|
260
|
+
is_signed=Expression(expr="False"),
|
261
|
+
fraction_digits=Expression(expr="0"),
|
262
|
+
)
|
263
|
+
if not isinstance(expr_type, QuantumNumeric):
|
264
|
+
return None
|
265
|
+
if expr_type.is_evaluated:
|
266
|
+
return expr_type
|
267
|
+
var_types = {
|
268
|
+
var_name: expr_val.get_type(node_id) for var_name, node_id in masks.items()
|
269
|
+
}
|
270
|
+
if not all(
|
271
|
+
isinstance(var_type, QuantumBit)
|
272
|
+
or (isinstance(var_type, QuantumNumeric) and var_type.is_instantiated)
|
273
|
+
for var_type in var_types.values()
|
274
|
+
):
|
275
|
+
return None
|
276
|
+
return compute_arithmetic_result_type(
|
277
|
+
simplified_expr, cast(dict[str, QuantumType], var_types), machine_precision
|
278
|
+
)
|
279
|
+
|
280
|
+
|
281
|
+
def simplify_qmod_expression(
|
282
|
+
expr_val: QmodAnnotatedExpression, machine_precision: int
|
283
|
+
) -> tuple[str, Optional[QuantumNumeric]]:
|
284
|
+
if expr_val.has_value(expr_val.root):
|
285
|
+
raise ClassiqInternalExpansionError(
|
286
|
+
"This expression is a constant value. No need for simplification"
|
287
|
+
)
|
288
|
+
var_mask_transformer = _VarMaskTransformer(expr_val)
|
289
|
+
masks = var_mask_transformer.masks
|
290
|
+
mask_expr = var_mask_transformer.visit(expr_val.root)
|
291
|
+
sympy_expr = _SympyCompatibilityTransformer().visit(mask_expr)
|
292
|
+
simplified_expr = str(
|
293
|
+
sympy.sympify(ast.unparse(sympy_expr), locals=_SYMPY_WRAPPERS)
|
294
|
+
)
|
295
|
+
restored_expr = _InverseSympyCompatibilityTransformer().visit(
|
296
|
+
ast.parse(simplified_expr, mode="eval")
|
297
|
+
)
|
298
|
+
expr_type = _get_numeric_type(
|
299
|
+
expr_val,
|
300
|
+
expr_val.get_type(expr_val.root),
|
301
|
+
ast.unparse(restored_expr),
|
302
|
+
masks,
|
303
|
+
machine_precision,
|
304
|
+
)
|
305
|
+
restored_expr = _InverseVarMaskTransformer(
|
306
|
+
expr_val, var_mask_transformer.masks
|
307
|
+
).visit(restored_expr)
|
308
|
+
return ast.unparse(restored_expr), expr_type
|
File without changes
|
@@ -0,0 +1,112 @@
|
|
1
|
+
import ast
|
2
|
+
from collections.abc import Mapping
|
3
|
+
|
4
|
+
from classiq.interface.exceptions import (
|
5
|
+
ClassiqExpansionError,
|
6
|
+
ClassiqInternalExpansionError,
|
7
|
+
)
|
8
|
+
from classiq.interface.generator.expressions.proxies.classical.qmod_struct_instance import (
|
9
|
+
QmodStructInstance,
|
10
|
+
)
|
11
|
+
from classiq.interface.generator.functions.classical_type import (
|
12
|
+
Bool,
|
13
|
+
ClassicalArray,
|
14
|
+
ClassicalTuple,
|
15
|
+
Integer,
|
16
|
+
)
|
17
|
+
from classiq.interface.generator.functions.type_name import TypeName
|
18
|
+
from classiq.interface.model.handle_binding import FieldHandleBinding
|
19
|
+
from classiq.interface.model.quantum_type import (
|
20
|
+
QuantumBitvector,
|
21
|
+
QuantumNumeric,
|
22
|
+
QuantumType,
|
23
|
+
)
|
24
|
+
|
25
|
+
from classiq.evaluators.qmod_annotated_expression import QmodAnnotatedExpression
|
26
|
+
from classiq.evaluators.qmod_node_evaluators.utils import QmodType, get_qmod_type_name
|
27
|
+
|
28
|
+
|
29
|
+
def _eval_type_attribute(
|
30
|
+
expr_val: QmodAnnotatedExpression, node: ast.Attribute
|
31
|
+
) -> None:
|
32
|
+
subject = node.value
|
33
|
+
attr = node.attr
|
34
|
+
|
35
|
+
subject_type = expr_val.get_type(subject)
|
36
|
+
if isinstance(subject_type, QuantumType) and attr == "size":
|
37
|
+
expr_val.set_type(node, Integer())
|
38
|
+
if subject_type.has_size_in_bits:
|
39
|
+
expr_val.set_value(node, subject_type.size_in_bits)
|
40
|
+
else:
|
41
|
+
expr_val.set_quantum_type_attr(node, subject, attr)
|
42
|
+
return
|
43
|
+
if isinstance(subject_type, (ClassicalArray, QuantumBitvector)) and attr == "len":
|
44
|
+
expr_val.set_type(node, Integer())
|
45
|
+
if subject_type.has_length:
|
46
|
+
expr_val.set_value(node, subject_type.length_value)
|
47
|
+
elif isinstance(subject_type, QuantumType):
|
48
|
+
expr_val.set_quantum_type_attr(node, subject, attr)
|
49
|
+
return
|
50
|
+
if isinstance(subject_type, ClassicalTuple) and attr == "len":
|
51
|
+
expr_val.set_type(node, Integer())
|
52
|
+
expr_val.set_value(node, len(subject_type.element_types))
|
53
|
+
return
|
54
|
+
if isinstance(subject_type, QuantumNumeric):
|
55
|
+
if attr == "is_signed":
|
56
|
+
expr_val.set_type(node, Bool())
|
57
|
+
if subject_type.has_sign:
|
58
|
+
expr_val.set_value(node, subject_type.sign_value)
|
59
|
+
elif subject_type.has_size_in_bits:
|
60
|
+
expr_val.set_value(node, False)
|
61
|
+
else:
|
62
|
+
expr_val.set_quantum_type_attr(node, subject, attr)
|
63
|
+
return
|
64
|
+
if attr == "fraction_digits":
|
65
|
+
expr_val.set_type(node, Integer())
|
66
|
+
if subject_type.has_fraction_digits:
|
67
|
+
expr_val.set_value(node, subject_type.fraction_digits_value)
|
68
|
+
elif subject_type.has_size_in_bits:
|
69
|
+
expr_val.set_value(node, 0)
|
70
|
+
else:
|
71
|
+
expr_val.set_quantum_type_attr(node, subject, attr)
|
72
|
+
return
|
73
|
+
raise ClassiqExpansionError(
|
74
|
+
f"{get_qmod_type_name(subject_type)} has no attribute {attr!r}"
|
75
|
+
)
|
76
|
+
|
77
|
+
|
78
|
+
def eval_attribute(expr_val: QmodAnnotatedExpression, node: ast.Attribute) -> None:
|
79
|
+
subject = node.value
|
80
|
+
attr = node.attr
|
81
|
+
|
82
|
+
subject_type = expr_val.get_type(subject)
|
83
|
+
if not isinstance(subject_type, TypeName) or (
|
84
|
+
subject_type.has_fields and attr == "size"
|
85
|
+
):
|
86
|
+
_eval_type_attribute(expr_val, node)
|
87
|
+
return
|
88
|
+
subject_fields: Mapping[str, QmodType]
|
89
|
+
if subject_type.has_classical_struct_decl:
|
90
|
+
subject_fields = subject_type.classical_struct_decl.variables
|
91
|
+
elif subject_type.has_fields:
|
92
|
+
subject_fields = subject_type.fields
|
93
|
+
else:
|
94
|
+
raise ClassiqInternalExpansionError
|
95
|
+
if attr not in subject_fields:
|
96
|
+
raise ClassiqExpansionError(
|
97
|
+
f"{get_qmod_type_name(subject_type)} has no field {attr!r}"
|
98
|
+
)
|
99
|
+
expr_val.set_type(node, subject_fields[attr])
|
100
|
+
|
101
|
+
if expr_val.has_value(subject):
|
102
|
+
subject_value = expr_val.get_value(subject)
|
103
|
+
if (
|
104
|
+
not isinstance(subject_value, QmodStructInstance)
|
105
|
+
or attr not in subject_value.fields
|
106
|
+
):
|
107
|
+
raise ClassiqInternalExpansionError
|
108
|
+
expr_val.set_value(node, subject_value.fields[attr])
|
109
|
+
elif expr_val.has_var(subject):
|
110
|
+
subject_var = expr_val.get_var(subject)
|
111
|
+
expr_val.set_var(node, FieldHandleBinding(base_handle=subject_var, field=attr))
|
112
|
+
expr_val.remove_var(subject)
|
@@ -0,0 +1,132 @@
|
|
1
|
+
import ast
|
2
|
+
from typing import TYPE_CHECKING
|
3
|
+
|
4
|
+
from classiq.interface.exceptions import (
|
5
|
+
ClassiqExpansionError,
|
6
|
+
ClassiqInternalExpansionError,
|
7
|
+
)
|
8
|
+
from classiq.interface.generator.functions.classical_type import Integer, Real
|
9
|
+
from classiq.interface.model.quantum_type import QuantumNumeric
|
10
|
+
|
11
|
+
from classiq.evaluators.qmod_annotated_expression import QmodAnnotatedExpression
|
12
|
+
from classiq.evaluators.qmod_node_evaluators.utils import (
|
13
|
+
IntegerValueType,
|
14
|
+
NumberValueType,
|
15
|
+
QmodType,
|
16
|
+
is_classical_integer,
|
17
|
+
is_classical_type,
|
18
|
+
is_numeric_type,
|
19
|
+
)
|
20
|
+
|
21
|
+
|
22
|
+
def _binary_op_allowed(left: QmodType, right: QmodType) -> bool:
|
23
|
+
return is_numeric_type(left) and is_numeric_type(right)
|
24
|
+
|
25
|
+
|
26
|
+
def _validate_binary_op(op: ast.AST, left_type: QmodType, right_type: QmodType) -> None:
|
27
|
+
if not _binary_op_allowed(left_type, right_type):
|
28
|
+
raise ClassiqExpansionError(
|
29
|
+
f"Both sides of the binary operator {type(op).__name__!r} must be "
|
30
|
+
f"scalar values"
|
31
|
+
)
|
32
|
+
if isinstance(op, (ast.LShift, ast.RShift)) and (
|
33
|
+
isinstance(left_type, Real) or isinstance(right_type, Real)
|
34
|
+
):
|
35
|
+
raise ClassiqExpansionError(
|
36
|
+
f"Bitshift operation {type(op).__name__!r} on real values is not "
|
37
|
+
f"supported"
|
38
|
+
)
|
39
|
+
if isinstance(op, (ast.BitOr, ast.BitXor, ast.BitAnd)) and (
|
40
|
+
isinstance(left_type, Real) or isinstance(right_type, Real)
|
41
|
+
):
|
42
|
+
raise ClassiqExpansionError(
|
43
|
+
f"Bitwise operation {type(op).__name__!r} on real values is not supported"
|
44
|
+
)
|
45
|
+
|
46
|
+
if isinstance(op, ast.MatMult):
|
47
|
+
raise ClassiqExpansionError(
|
48
|
+
f"Binary operation {type(op).__name__!r} is not supported"
|
49
|
+
)
|
50
|
+
|
51
|
+
if not is_classical_type(right_type) and (
|
52
|
+
isinstance(
|
53
|
+
op, (ast.Div, ast.FloorDiv, ast.Mod, ast.Pow, ast.LShift, ast.RShift)
|
54
|
+
)
|
55
|
+
):
|
56
|
+
raise ClassiqExpansionError(
|
57
|
+
f"Right-hand side of binary operation {type(op).__name__!r} must be classical numeric value"
|
58
|
+
)
|
59
|
+
|
60
|
+
|
61
|
+
def _eval_binary_op_constant(
|
62
|
+
op: ast.AST, left_value: NumberValueType, right_value: NumberValueType
|
63
|
+
) -> NumberValueType:
|
64
|
+
if isinstance(op, ast.Add):
|
65
|
+
return left_value + right_value
|
66
|
+
if isinstance(op, ast.Sub):
|
67
|
+
return left_value - right_value
|
68
|
+
if isinstance(op, ast.Mult):
|
69
|
+
return left_value * right_value
|
70
|
+
if isinstance(op, ast.Div):
|
71
|
+
if right_value == 0:
|
72
|
+
raise ClassiqExpansionError("Division by zero")
|
73
|
+
return left_value / right_value
|
74
|
+
if isinstance(op, ast.FloorDiv):
|
75
|
+
if right_value == 0:
|
76
|
+
raise ClassiqExpansionError("Integer division by zero")
|
77
|
+
if isinstance(left_value, complex) or isinstance(right_value, complex):
|
78
|
+
raise ClassiqExpansionError("Integer division with a complex number")
|
79
|
+
return left_value // right_value
|
80
|
+
if isinstance(op, ast.Mod):
|
81
|
+
if right_value == 0:
|
82
|
+
raise ClassiqExpansionError("Integer modulu by zero")
|
83
|
+
if isinstance(left_value, complex) or isinstance(right_value, complex):
|
84
|
+
raise ClassiqExpansionError("Integer modulu with a complex number")
|
85
|
+
return left_value % right_value
|
86
|
+
if isinstance(op, ast.Pow):
|
87
|
+
return left_value**right_value
|
88
|
+
|
89
|
+
if TYPE_CHECKING:
|
90
|
+
assert isinstance(left_value, IntegerValueType)
|
91
|
+
assert isinstance(right_value, IntegerValueType)
|
92
|
+
|
93
|
+
if isinstance(op, ast.LShift):
|
94
|
+
return left_value << right_value
|
95
|
+
if isinstance(op, ast.RShift):
|
96
|
+
return left_value >> right_value
|
97
|
+
if isinstance(op, ast.BitAnd):
|
98
|
+
return left_value & right_value
|
99
|
+
if isinstance(op, ast.BitOr):
|
100
|
+
return left_value | right_value
|
101
|
+
if isinstance(op, ast.BitXor):
|
102
|
+
return left_value ^ right_value
|
103
|
+
|
104
|
+
raise ClassiqInternalExpansionError
|
105
|
+
|
106
|
+
|
107
|
+
def eval_binary_op(expr_val: QmodAnnotatedExpression, node: ast.BinOp) -> None:
|
108
|
+
left = node.left
|
109
|
+
right = node.right
|
110
|
+
op = node.op
|
111
|
+
|
112
|
+
left_type = expr_val.get_type(left)
|
113
|
+
right_type = expr_val.get_type(right)
|
114
|
+
_validate_binary_op(op, left_type, right_type)
|
115
|
+
|
116
|
+
node_type: QmodType
|
117
|
+
if not is_classical_type(left_type) or not is_classical_type(left_type):
|
118
|
+
node_type = QuantumNumeric()
|
119
|
+
elif (
|
120
|
+
not isinstance(op, ast.Div)
|
121
|
+
and is_classical_integer(left_type)
|
122
|
+
and is_classical_integer(right_type)
|
123
|
+
):
|
124
|
+
node_type = Integer()
|
125
|
+
else:
|
126
|
+
node_type = Real()
|
127
|
+
expr_val.set_type(node, node_type)
|
128
|
+
|
129
|
+
if expr_val.has_value(left) and expr_val.has_value(right):
|
130
|
+
left_value = expr_val.get_value(left)
|
131
|
+
right_value = expr_val.get_value(right)
|
132
|
+
expr_val.set_value(node, _eval_binary_op_constant(op, left_value, right_value))
|
@@ -0,0 +1,70 @@
|
|
1
|
+
import ast
|
2
|
+
from functools import reduce
|
3
|
+
from operator import and_, or_
|
4
|
+
|
5
|
+
from classiq.interface.exceptions import (
|
6
|
+
ClassiqExpansionError,
|
7
|
+
ClassiqInternalExpansionError,
|
8
|
+
)
|
9
|
+
from classiq.interface.generator.functions.classical_type import Bool
|
10
|
+
from classiq.interface.model.quantum_type import QuantumBit, QuantumNumeric
|
11
|
+
|
12
|
+
from classiq.evaluators.qmod_annotated_expression import QmodAnnotatedExpression
|
13
|
+
from classiq.evaluators.qmod_node_evaluators.utils import (
|
14
|
+
QmodType,
|
15
|
+
is_classical_type,
|
16
|
+
qnum_is_qbit,
|
17
|
+
)
|
18
|
+
|
19
|
+
|
20
|
+
def bool_op_allowed(left: QmodType, right: QmodType) -> bool:
|
21
|
+
return is_bool_type(left) and is_bool_type(right)
|
22
|
+
|
23
|
+
|
24
|
+
def is_bool_type(qmod_type: QmodType) -> bool:
|
25
|
+
if isinstance(qmod_type, QuantumNumeric):
|
26
|
+
return qnum_is_qbit(qmod_type)
|
27
|
+
elif not isinstance(qmod_type, (QuantumBit, Bool)):
|
28
|
+
return False
|
29
|
+
return True
|
30
|
+
|
31
|
+
|
32
|
+
def eval_bool_op(expr_val: QmodAnnotatedExpression, node: ast.BoolOp) -> None:
|
33
|
+
if len(node.values) < 2:
|
34
|
+
raise ClassiqInternalExpansionError
|
35
|
+
|
36
|
+
left = node.values[0]
|
37
|
+
rights = node.values[0:]
|
38
|
+
op = node.op
|
39
|
+
|
40
|
+
left_type = expr_val.get_type(left)
|
41
|
+
right_types = [expr_val.get_type(right) for right in rights]
|
42
|
+
if not all(bool_op_allowed(left_type, right_type) for right_type in right_types):
|
43
|
+
raise ClassiqExpansionError(
|
44
|
+
f"Both sides of the Boolean operator {type(op).__name__!r} must be "
|
45
|
+
f"Boolean values"
|
46
|
+
)
|
47
|
+
qmod_type: QmodType
|
48
|
+
if not is_classical_type(left_type) or not all(
|
49
|
+
is_classical_type(right_type) for right_type in right_types
|
50
|
+
):
|
51
|
+
qmod_type = QuantumBit()
|
52
|
+
else:
|
53
|
+
qmod_type = Bool()
|
54
|
+
expr_val.set_type(node, qmod_type)
|
55
|
+
|
56
|
+
if not expr_val.has_value(left) or not all(
|
57
|
+
expr_val.has_value(right) for right in rights
|
58
|
+
):
|
59
|
+
return
|
60
|
+
|
61
|
+
left_value = expr_val.get_value(left)
|
62
|
+
right_values = [expr_val.get_value(right) for right in rights]
|
63
|
+
if isinstance(op, ast.And):
|
64
|
+
operator = and_
|
65
|
+
elif isinstance(op, ast.Or):
|
66
|
+
operator = or_
|
67
|
+
else:
|
68
|
+
raise ClassiqInternalExpansionError
|
69
|
+
constant_value = reduce(operator, [left_value, *right_values])
|
70
|
+
expr_val.set_value(node, constant_value)
|