classiq 0.86.0__py3-none-any.whl → 0.87.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/__init__.py +2 -0
- classiq/applications/chemistry/hartree_fock.py +5 -1
- classiq/applications/chemistry/op_utils.py +2 -2
- classiq/applications/combinatorial_helpers/encoding_mapping.py +11 -15
- classiq/applications/combinatorial_helpers/encoding_utils.py +6 -6
- classiq/applications/combinatorial_helpers/memory.py +4 -4
- classiq/applications/combinatorial_helpers/optimization_model.py +5 -5
- classiq/applications/combinatorial_helpers/pauli_helpers/pauli_utils.py +6 -10
- classiq/applications/combinatorial_helpers/pyomo_utils.py +27 -26
- classiq/applications/combinatorial_helpers/sympy_utils.py +2 -2
- classiq/applications/combinatorial_helpers/transformations/encoding.py +4 -6
- classiq/applications/combinatorial_helpers/transformations/fixed_variables.py +4 -4
- classiq/applications/combinatorial_helpers/transformations/ising_converter.py +2 -2
- classiq/applications/combinatorial_helpers/transformations/penalty_support.py +3 -3
- classiq/applications/combinatorial_optimization/combinatorial_problem.py +4 -0
- classiq/applications/hamiltonian/pauli_decomposition.py +33 -1
- classiq/evaluators/argument_types.py +15 -6
- classiq/evaluators/parameter_types.py +43 -39
- classiq/evaluators/qmod_annotated_expression.py +88 -11
- classiq/evaluators/qmod_expression_visitors/out_of_place_node_transformer.py +19 -0
- classiq/evaluators/qmod_expression_visitors/qmod_expression_evaluator.py +54 -11
- classiq/evaluators/qmod_expression_visitors/qmod_expression_renamer.py +40 -25
- classiq/evaluators/qmod_expression_visitors/qmod_expression_simplifier.py +29 -59
- classiq/evaluators/qmod_node_evaluators/attribute_evaluation.py +12 -5
- classiq/evaluators/qmod_node_evaluators/binary_op_evaluation.py +175 -28
- classiq/evaluators/qmod_node_evaluators/classical_function_evaluation.py +21 -14
- classiq/evaluators/qmod_node_evaluators/compare_evaluation.py +9 -5
- classiq/evaluators/qmod_node_evaluators/constant_evaluation.py +20 -1
- classiq/evaluators/qmod_node_evaluators/min_max_evaluation.py +97 -0
- classiq/evaluators/qmod_node_evaluators/name_evaluation.py +11 -26
- classiq/evaluators/qmod_node_evaluators/numeric_attrs_utils.py +56 -0
- classiq/evaluators/qmod_node_evaluators/piecewise_evaluation.py +40 -0
- classiq/evaluators/qmod_node_evaluators/struct_instantiation_evaluation.py +2 -3
- classiq/evaluators/qmod_node_evaluators/subscript_evaluation.py +48 -21
- classiq/evaluators/qmod_node_evaluators/unary_op_evaluation.py +53 -9
- classiq/evaluators/qmod_node_evaluators/utils.py +27 -5
- classiq/evaluators/qmod_type_inference/classical_type_inference.py +188 -0
- classiq/evaluators/qmod_type_inference/quantum_type_inference.py +292 -0
- classiq/evaluators/quantum_type_utils.py +0 -131
- classiq/execution/execution_session.py +1 -1
- classiq/execution/qnn.py +4 -1
- classiq/execution/user_budgets.py +1 -1
- classiq/interface/_version.py +1 -1
- classiq/interface/backend/backend_preferences.py +10 -30
- classiq/interface/backend/quantum_backend_providers.py +63 -52
- classiq/interface/generator/arith/binary_ops.py +107 -115
- classiq/interface/generator/arith/extremum_operations.py +33 -45
- classiq/interface/generator/arith/number_utils.py +4 -1
- classiq/interface/generator/circuit_code/types_and_constants.py +0 -9
- classiq/interface/generator/compiler_keywords.py +2 -0
- classiq/interface/generator/function_param_list.py +133 -5
- classiq/interface/generator/functions/classical_type.py +59 -2
- classiq/interface/generator/functions/qmod_python_interface.py +15 -0
- classiq/interface/generator/functions/type_name.py +6 -0
- classiq/interface/generator/model/preferences/preferences.py +1 -17
- classiq/interface/generator/quantum_program.py +1 -13
- classiq/interface/helpers/model_normalizer.py +2 -2
- classiq/interface/helpers/text_utils.py +7 -2
- classiq/interface/interface_version.py +1 -1
- classiq/interface/model/classical_if.py +40 -0
- classiq/interface/model/handle_binding.py +28 -16
- classiq/interface/model/quantum_type.py +61 -2
- classiq/interface/pretty_print/expression_to_qmod.py +24 -11
- classiq/interface/pyomo_extension/__init__.py +0 -4
- classiq/interface/pyomo_extension/pyomo_sympy_bimap.py +2 -2
- classiq/model_expansions/arithmetic.py +43 -1
- classiq/model_expansions/arithmetic_compute_result_attrs.py +255 -0
- classiq/model_expansions/capturing/captured_vars.py +2 -5
- classiq/model_expansions/quantum_operations/allocate.py +22 -15
- classiq/model_expansions/quantum_operations/arithmetic/explicit_boolean_expressions.py +1 -3
- classiq/model_expansions/quantum_operations/assignment_result_processor.py +52 -71
- classiq/model_expansions/quantum_operations/bind.py +15 -7
- classiq/model_expansions/quantum_operations/call_emitter.py +2 -10
- classiq/model_expansions/quantum_operations/classical_var_emitter.py +6 -0
- classiq/open_library/functions/__init__.py +3 -0
- classiq/open_library/functions/lcu.py +117 -0
- classiq/qmod/builtins/enums.py +2 -2
- classiq/qmod/builtins/structs.py +33 -18
- classiq/qmod/pretty_print/expression_to_python.py +7 -9
- {classiq-0.86.0.dist-info → classiq-0.87.0.dist-info}/METADATA +3 -3
- {classiq-0.86.0.dist-info → classiq-0.87.0.dist-info}/RECORD +83 -89
- classiq/interface/generator/amplitude_estimation.py +0 -34
- classiq/interface/generator/function_param_list_without_self_reference.py +0 -160
- classiq/interface/generator/grover_diffuser.py +0 -93
- classiq/interface/generator/grover_operator.py +0 -106
- classiq/interface/generator/oracles/__init__.py +0 -3
- classiq/interface/generator/oracles/arithmetic_oracle.py +0 -82
- classiq/interface/generator/oracles/custom_oracle.py +0 -65
- classiq/interface/generator/oracles/oracle_abc.py +0 -76
- classiq/interface/generator/oracles/oracle_function_param_list.py +0 -6
- classiq/interface/generator/piecewise_linear_amplitude_loading.py +0 -165
- classiq/interface/generator/qpe.py +0 -169
- classiq/interface/grover/grover_modelling_params.py +0 -13
- classiq/model_expansions/transformers/var_splitter.py +0 -224
- /classiq/{interface/grover → evaluators/qmod_type_inference}/__init__.py +0 -0
- {classiq-0.86.0.dist-info → classiq-0.87.0.dist-info}/WHEEL +0 -0
@@ -1,22 +1,18 @@
|
|
1
1
|
import ast
|
2
|
-
from typing import Any,
|
2
|
+
from typing import Any, cast
|
3
3
|
|
4
4
|
import sympy
|
5
5
|
|
6
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
7
|
from classiq.interface.model.handle_binding import HandleBinding
|
10
|
-
from classiq.interface.model.quantum_type import (
|
11
|
-
QuantumBit,
|
12
|
-
QuantumNumeric,
|
13
|
-
QuantumType,
|
14
|
-
)
|
15
8
|
|
16
9
|
from classiq.evaluators.qmod_annotated_expression import (
|
17
10
|
QmodAnnotatedExpression,
|
18
11
|
QmodExprNodeId,
|
19
12
|
)
|
13
|
+
from classiq.evaluators.qmod_expression_visitors.out_of_place_node_transformer import (
|
14
|
+
OutOfPlaceNodeTransformer,
|
15
|
+
)
|
20
16
|
from classiq.evaluators.qmod_expression_visitors.sympy_wrappers import (
|
21
17
|
BitwiseAnd,
|
22
18
|
BitwiseNot,
|
@@ -25,7 +21,7 @@ from classiq.evaluators.qmod_expression_visitors.sympy_wrappers import (
|
|
25
21
|
LShift,
|
26
22
|
RShift,
|
27
23
|
)
|
28
|
-
from classiq.
|
24
|
+
from classiq.model_expansions.atomic_expression_functions_defs import do_div
|
29
25
|
|
30
26
|
_SYMPY_WRAPPERS = {
|
31
27
|
wrapper.__name__: wrapper
|
@@ -37,10 +33,12 @@ _SYMPY_WRAPPERS = {
|
|
37
33
|
LShift,
|
38
34
|
RShift,
|
39
35
|
]
|
36
|
+
} | {
|
37
|
+
do_div.__name__: do_div,
|
40
38
|
}
|
41
39
|
|
42
40
|
|
43
|
-
class _VarMaskTransformer(
|
41
|
+
class _VarMaskTransformer(OutOfPlaceNodeTransformer):
|
44
42
|
def __init__(self, expr_val: QmodAnnotatedExpression) -> None:
|
45
43
|
self._expr_val = expr_val
|
46
44
|
self._mask_id = 0
|
@@ -68,7 +66,7 @@ class _VarMaskTransformer(ast.NodeTransformer):
|
|
68
66
|
mask = self._create_mask()
|
69
67
|
self.masks[mask] = id(node)
|
70
68
|
return ast.Name(id=mask)
|
71
|
-
return
|
69
|
+
return super().visit(node)
|
72
70
|
|
73
71
|
def visit_Subscript(self, node: ast.Subscript) -> ast.AST:
|
74
72
|
mask = self._create_mask()
|
@@ -76,7 +74,7 @@ class _VarMaskTransformer(ast.NodeTransformer):
|
|
76
74
|
return ast.Name(id=mask)
|
77
75
|
|
78
76
|
|
79
|
-
class _InverseVarMaskTransformer(
|
77
|
+
class _InverseVarMaskTransformer(OutOfPlaceNodeTransformer):
|
80
78
|
def __init__(
|
81
79
|
self, expr_val: QmodAnnotatedExpression, masks: dict[str, QmodExprNodeId]
|
82
80
|
) -> None:
|
@@ -93,7 +91,7 @@ class _InverseVarMaskTransformer(ast.NodeTransformer):
|
|
93
91
|
return node
|
94
92
|
|
95
93
|
|
96
|
-
class _SympyCompatibilityTransformer(
|
94
|
+
class _SympyCompatibilityTransformer(OutOfPlaceNodeTransformer):
|
97
95
|
def visit_BoolOp(self, node: ast.BoolOp) -> ast.Call:
|
98
96
|
if len(node.values) != 2:
|
99
97
|
raise ClassiqInternalExpansionError
|
@@ -151,6 +149,12 @@ class _SympyCompatibilityTransformer(ast.NodeTransformer):
|
|
151
149
|
sympy_func = BitwiseXor.__name__
|
152
150
|
elif isinstance(node.op, ast.BitAnd):
|
153
151
|
sympy_func = BitwiseAnd.__name__
|
152
|
+
elif isinstance(node.op, ast.Div):
|
153
|
+
return ast.Call(
|
154
|
+
func=ast.Name(id=do_div.__name__),
|
155
|
+
args=[node.left, node.right],
|
156
|
+
keywords=[],
|
157
|
+
)
|
154
158
|
else:
|
155
159
|
return node
|
156
160
|
return ast.Call(
|
@@ -158,7 +162,7 @@ class _SympyCompatibilityTransformer(ast.NodeTransformer):
|
|
158
162
|
)
|
159
163
|
|
160
164
|
|
161
|
-
class _InverseSympyCompatibilityTransformer(
|
165
|
+
class _InverseSympyCompatibilityTransformer(OutOfPlaceNodeTransformer):
|
162
166
|
def visit_Call(self, node: ast.Call) -> Any:
|
163
167
|
node = cast(ast.Call, self.generic_visit(node))
|
164
168
|
if not isinstance(node.func, ast.Name):
|
@@ -183,12 +187,12 @@ class _InverseSympyCompatibilityTransformer(ast.NodeTransformer):
|
|
183
187
|
and (len(node.args) != 2 or len(node.keywords) > 0)
|
184
188
|
) or (
|
185
189
|
func in {BitwiseNot.__name__}
|
186
|
-
and (len(node.args) !=
|
190
|
+
and (len(node.args) != 1 or len(node.keywords) > 0)
|
187
191
|
):
|
188
192
|
raise ClassiqInternalExpansionError
|
189
193
|
|
190
194
|
if func == BitwiseNot.__name__:
|
191
|
-
return ast.UnaryOp(op=ast.Invert, operand=node.args[0])
|
195
|
+
return ast.UnaryOp(op=ast.Invert(), operand=node.args[0])
|
192
196
|
|
193
197
|
if func == "Eq":
|
194
198
|
return ast.Compare(
|
@@ -226,6 +230,13 @@ class _InverseSympyCompatibilityTransformer(ast.NodeTransformer):
|
|
226
230
|
if func == BitwiseAnd.__name__:
|
227
231
|
return ast.BinOp(left=node.args[0], op=ast.BitAnd(), right=node.args[1])
|
228
232
|
|
233
|
+
if func == "Mod":
|
234
|
+
return ast.BinOp(left=node.args[0], op=ast.Mod(), right=node.args[1])
|
235
|
+
if func == "Max":
|
236
|
+
node.func.id = "max"
|
237
|
+
elif func == "Min":
|
238
|
+
node.func.id = "min"
|
239
|
+
|
229
240
|
return node
|
230
241
|
|
231
242
|
def visit_UnaryOp(self, node: ast.UnaryOp) -> Any:
|
@@ -247,46 +258,12 @@ class _InverseSympyCompatibilityTransformer(ast.NodeTransformer):
|
|
247
258
|
return node
|
248
259
|
|
249
260
|
|
250
|
-
def
|
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]]:
|
261
|
+
def simplify_qmod_expression(expr_val: QmodAnnotatedExpression) -> str:
|
284
262
|
if expr_val.has_value(expr_val.root):
|
285
263
|
raise ClassiqInternalExpansionError(
|
286
264
|
"This expression is a constant value. No need for simplification"
|
287
265
|
)
|
288
266
|
var_mask_transformer = _VarMaskTransformer(expr_val)
|
289
|
-
masks = var_mask_transformer.masks
|
290
267
|
mask_expr = var_mask_transformer.visit(expr_val.root)
|
291
268
|
sympy_expr = _SympyCompatibilityTransformer().visit(mask_expr)
|
292
269
|
simplified_expr = str(
|
@@ -295,14 +272,7 @@ def simplify_qmod_expression(
|
|
295
272
|
restored_expr = _InverseSympyCompatibilityTransformer().visit(
|
296
273
|
ast.parse(simplified_expr, mode="eval")
|
297
274
|
)
|
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
275
|
restored_expr = _InverseVarMaskTransformer(
|
306
276
|
expr_val, var_mask_transformer.masks
|
307
277
|
).visit(restored_expr)
|
308
|
-
return ast.unparse(restored_expr)
|
278
|
+
return ast.unparse(restored_expr)
|
@@ -23,7 +23,7 @@ from classiq.interface.model.quantum_type import (
|
|
23
23
|
)
|
24
24
|
|
25
25
|
from classiq.evaluators.qmod_annotated_expression import QmodAnnotatedExpression
|
26
|
-
from classiq.evaluators.qmod_node_evaluators.utils import QmodType
|
26
|
+
from classiq.evaluators.qmod_node_evaluators.utils import QmodType
|
27
27
|
|
28
28
|
|
29
29
|
def _eval_type_attribute(
|
@@ -71,7 +71,7 @@ def _eval_type_attribute(
|
|
71
71
|
expr_val.set_quantum_type_attr(node, subject, attr)
|
72
72
|
return
|
73
73
|
raise ClassiqExpansionError(
|
74
|
-
f"{
|
74
|
+
f"{subject_type.raw_qmod_type_name} has no attribute {attr!r}"
|
75
75
|
)
|
76
76
|
|
77
77
|
|
@@ -94,7 +94,7 @@ def eval_attribute(expr_val: QmodAnnotatedExpression, node: ast.Attribute) -> No
|
|
94
94
|
raise ClassiqInternalExpansionError
|
95
95
|
if attr not in subject_fields:
|
96
96
|
raise ClassiqExpansionError(
|
97
|
-
f"{
|
97
|
+
f"{subject_type.raw_qmod_type_name} has no field {attr!r}"
|
98
98
|
)
|
99
99
|
expr_val.set_type(node, subject_fields[attr])
|
100
100
|
|
@@ -103,9 +103,16 @@ def eval_attribute(expr_val: QmodAnnotatedExpression, node: ast.Attribute) -> No
|
|
103
103
|
if (
|
104
104
|
not isinstance(subject_value, QmodStructInstance)
|
105
105
|
or attr not in subject_value.fields
|
106
|
-
):
|
106
|
+
) and (not isinstance(subject_value, dict) or attr not in subject_value):
|
107
107
|
raise ClassiqInternalExpansionError
|
108
|
-
|
108
|
+
if isinstance(subject_value, QmodStructInstance):
|
109
|
+
attr_value = subject_value.fields[attr]
|
110
|
+
else:
|
111
|
+
# dicts are supported because our foreign funcs return dicts instead of
|
112
|
+
# QmodStructInstances
|
113
|
+
# FIXME: Remove (CLS-3241)
|
114
|
+
attr_value = subject_value[attr]
|
115
|
+
expr_val.set_value(node, attr_value)
|
109
116
|
elif expr_val.has_var(subject):
|
110
117
|
subject_var = expr_val.get_var(subject)
|
111
118
|
expr_val.set_var(node, FieldHandleBinding(base_handle=subject_var, field=attr))
|
@@ -5,10 +5,14 @@ from classiq.interface.exceptions import (
|
|
5
5
|
ClassiqExpansionError,
|
6
6
|
ClassiqInternalExpansionError,
|
7
7
|
)
|
8
|
-
from classiq.interface.generator.functions.classical_type import Integer, Real
|
8
|
+
from classiq.interface.generator.functions.classical_type import Bool, Integer, Real
|
9
9
|
from classiq.interface.model.quantum_type import QuantumNumeric
|
10
10
|
|
11
11
|
from classiq.evaluators.qmod_annotated_expression import QmodAnnotatedExpression
|
12
|
+
from classiq.evaluators.qmod_node_evaluators.numeric_attrs_utils import (
|
13
|
+
get_classical_value_for_arithmetic,
|
14
|
+
get_numeric_attrs,
|
15
|
+
)
|
12
16
|
from classiq.evaluators.qmod_node_evaluators.utils import (
|
13
17
|
IntegerValueType,
|
14
18
|
NumberValueType,
|
@@ -17,14 +21,38 @@ from classiq.evaluators.qmod_node_evaluators.utils import (
|
|
17
21
|
is_classical_type,
|
18
22
|
is_numeric_type,
|
19
23
|
)
|
24
|
+
from classiq.model_expansions.arithmetic import NumericAttributes
|
25
|
+
from classiq.model_expansions.arithmetic_compute_result_attrs import (
|
26
|
+
compute_result_attrs_add,
|
27
|
+
compute_result_attrs_bitwise_and,
|
28
|
+
compute_result_attrs_bitwise_or,
|
29
|
+
compute_result_attrs_bitwise_xor,
|
30
|
+
compute_result_attrs_lshift,
|
31
|
+
compute_result_attrs_modulo,
|
32
|
+
compute_result_attrs_multiply,
|
33
|
+
compute_result_attrs_power,
|
34
|
+
compute_result_attrs_rshift,
|
35
|
+
compute_result_attrs_subtract,
|
36
|
+
)
|
20
37
|
|
21
38
|
|
22
|
-
def _binary_op_allowed(left: QmodType, right: QmodType) -> bool:
|
23
|
-
|
39
|
+
def _binary_op_allowed(left: QmodType, right: QmodType, op: ast.AST) -> bool:
|
40
|
+
left_numeric = is_numeric_type(left)
|
41
|
+
right_numeric = is_numeric_type(right)
|
42
|
+
if isinstance(op, (ast.BitOr, ast.BitAnd, ast.BitXor)):
|
43
|
+
return (left_numeric or isinstance(left, Bool)) and (
|
44
|
+
right_numeric or isinstance(right, Bool)
|
45
|
+
)
|
46
|
+
return left_numeric and right_numeric
|
24
47
|
|
25
48
|
|
26
|
-
def _validate_binary_op(
|
27
|
-
|
49
|
+
def _validate_binary_op(
|
50
|
+
op: ast.AST,
|
51
|
+
left_type: QmodType,
|
52
|
+
right_type: QmodType,
|
53
|
+
treat_qnum_as_float: bool,
|
54
|
+
) -> None:
|
55
|
+
if not _binary_op_allowed(left_type, right_type, op):
|
28
56
|
raise ClassiqExpansionError(
|
29
57
|
f"Both sides of the binary operator {type(op).__name__!r} must be "
|
30
58
|
f"scalar values"
|
@@ -48,15 +76,132 @@ def _validate_binary_op(op: ast.AST, left_type: QmodType, right_type: QmodType)
|
|
48
76
|
f"Binary operation {type(op).__name__!r} is not supported"
|
49
77
|
)
|
50
78
|
|
51
|
-
if not
|
52
|
-
isinstance(
|
53
|
-
|
79
|
+
if not treat_qnum_as_float:
|
80
|
+
if isinstance(op, ast.FloorDiv) and (
|
81
|
+
not is_classical_type(left_type) or not is_classical_type(right_type)
|
82
|
+
):
|
83
|
+
raise ClassiqExpansionError(
|
84
|
+
f"{type(op).__name__!r} with quantum variables is not supported"
|
85
|
+
)
|
86
|
+
|
87
|
+
if not is_classical_type(right_type) and isinstance(
|
88
|
+
op, (ast.Div, ast.Mod, ast.Pow, ast.LShift, ast.RShift)
|
89
|
+
):
|
90
|
+
raise ClassiqExpansionError(
|
91
|
+
f"Right-hand side of binary operation {type(op).__name__!r} must be classical numeric value"
|
92
|
+
)
|
93
|
+
|
94
|
+
|
95
|
+
def _infer_binary_op_type(
|
96
|
+
expr_val: QmodAnnotatedExpression,
|
97
|
+
node: ast.BinOp,
|
98
|
+
left_type: QmodType,
|
99
|
+
right_type: QmodType,
|
100
|
+
machine_precision: int,
|
101
|
+
treat_qnum_as_float: bool,
|
102
|
+
) -> QmodType:
|
103
|
+
op = node.op
|
104
|
+
|
105
|
+
if is_classical_type(left_type) and is_classical_type(right_type):
|
106
|
+
if isinstance(left_type, Bool) and isinstance(right_type, Bool):
|
107
|
+
return Bool()
|
108
|
+
if (
|
109
|
+
not isinstance(op, ast.Div)
|
110
|
+
and (is_classical_integer(left_type) or isinstance(left_type, Bool))
|
111
|
+
and (is_classical_integer(right_type) or isinstance(right_type, Bool))
|
112
|
+
):
|
113
|
+
return Integer()
|
114
|
+
return Real()
|
115
|
+
|
116
|
+
left_attrs = get_numeric_attrs(
|
117
|
+
expr_val, node.left, left_type, machine_precision, treat_qnum_as_float
|
118
|
+
)
|
119
|
+
right_attrs = get_numeric_attrs(
|
120
|
+
expr_val, node.right, right_type, machine_precision, treat_qnum_as_float
|
121
|
+
)
|
122
|
+
|
123
|
+
if left_attrs is None or right_attrs is None:
|
124
|
+
return QuantumNumeric()
|
125
|
+
|
126
|
+
right_value = get_classical_value_for_arithmetic(
|
127
|
+
expr_val, node.right, right_type, treat_qnum_as_float
|
128
|
+
)
|
129
|
+
|
130
|
+
if isinstance(op, ast.Add):
|
131
|
+
result_attrs = compute_result_attrs_add(
|
132
|
+
left_attrs, right_attrs, machine_precision
|
54
133
|
)
|
55
|
-
|
56
|
-
|
57
|
-
|
134
|
+
|
135
|
+
elif isinstance(op, ast.Sub):
|
136
|
+
result_attrs = compute_result_attrs_subtract(
|
137
|
+
left_attrs, right_attrs, machine_precision
|
58
138
|
)
|
59
139
|
|
140
|
+
elif isinstance(op, ast.Mult):
|
141
|
+
result_attrs = compute_result_attrs_multiply(
|
142
|
+
left_attrs, right_attrs, machine_precision
|
143
|
+
)
|
144
|
+
|
145
|
+
elif isinstance(op, ast.Div):
|
146
|
+
if right_value is None:
|
147
|
+
return QuantumNumeric()
|
148
|
+
if right_value == 0:
|
149
|
+
raise ClassiqExpansionError("Division by zero")
|
150
|
+
right_attrs = NumericAttributes.from_constant(
|
151
|
+
1 / right_value, machine_precision
|
152
|
+
)
|
153
|
+
result_attrs = compute_result_attrs_multiply(
|
154
|
+
left_attrs, right_attrs, machine_precision
|
155
|
+
)
|
156
|
+
elif isinstance(op, ast.FloorDiv):
|
157
|
+
return QuantumNumeric()
|
158
|
+
|
159
|
+
elif isinstance(op, ast.Mod):
|
160
|
+
if right_value is None or treat_qnum_as_float:
|
161
|
+
return QuantumNumeric()
|
162
|
+
result_attrs = compute_result_attrs_modulo(
|
163
|
+
left_attrs, right_value, machine_precision
|
164
|
+
)
|
165
|
+
|
166
|
+
elif isinstance(op, ast.Pow):
|
167
|
+
if right_value is None or treat_qnum_as_float:
|
168
|
+
return QuantumNumeric()
|
169
|
+
result_attrs = compute_result_attrs_power(
|
170
|
+
left_attrs, right_value, machine_precision
|
171
|
+
)
|
172
|
+
|
173
|
+
elif isinstance(op, ast.LShift):
|
174
|
+
if right_value is None:
|
175
|
+
return QuantumNumeric()
|
176
|
+
result_attrs = compute_result_attrs_lshift(
|
177
|
+
left_attrs, right_value, machine_precision
|
178
|
+
)
|
179
|
+
|
180
|
+
elif isinstance(op, ast.RShift):
|
181
|
+
if right_value is None:
|
182
|
+
return QuantumNumeric()
|
183
|
+
result_attrs = compute_result_attrs_rshift(
|
184
|
+
left_attrs, right_value, machine_precision
|
185
|
+
)
|
186
|
+
|
187
|
+
elif isinstance(op, ast.BitAnd):
|
188
|
+
result_attrs = compute_result_attrs_bitwise_and(
|
189
|
+
left_attrs, right_attrs, machine_precision
|
190
|
+
)
|
191
|
+
elif isinstance(op, ast.BitOr):
|
192
|
+
result_attrs = compute_result_attrs_bitwise_or(
|
193
|
+
left_attrs, right_attrs, machine_precision
|
194
|
+
)
|
195
|
+
|
196
|
+
elif isinstance(op, ast.BitXor):
|
197
|
+
result_attrs = compute_result_attrs_bitwise_xor(
|
198
|
+
left_attrs, right_attrs, machine_precision
|
199
|
+
)
|
200
|
+
else:
|
201
|
+
raise ClassiqInternalExpansionError
|
202
|
+
|
203
|
+
return result_attrs.to_quantum_numeric()
|
204
|
+
|
60
205
|
|
61
206
|
def _eval_binary_op_constant(
|
62
207
|
op: ast.AST, left_value: NumberValueType, right_value: NumberValueType
|
@@ -79,9 +224,9 @@ def _eval_binary_op_constant(
|
|
79
224
|
return left_value // right_value
|
80
225
|
if isinstance(op, ast.Mod):
|
81
226
|
if right_value == 0:
|
82
|
-
raise ClassiqExpansionError("Integer
|
227
|
+
raise ClassiqExpansionError("Integer modulo by zero")
|
83
228
|
if isinstance(left_value, complex) or isinstance(right_value, complex):
|
84
|
-
raise ClassiqExpansionError("Integer
|
229
|
+
raise ClassiqExpansionError("Integer modulo with a complex number")
|
85
230
|
return left_value % right_value
|
86
231
|
if isinstance(op, ast.Pow):
|
87
232
|
return left_value**right_value
|
@@ -104,27 +249,29 @@ def _eval_binary_op_constant(
|
|
104
249
|
raise ClassiqInternalExpansionError
|
105
250
|
|
106
251
|
|
107
|
-
def eval_binary_op(
|
252
|
+
def eval_binary_op(
|
253
|
+
expr_val: QmodAnnotatedExpression,
|
254
|
+
node: ast.BinOp,
|
255
|
+
treat_qnum_as_float: bool,
|
256
|
+
machine_precision: int,
|
257
|
+
) -> None:
|
108
258
|
left = node.left
|
109
259
|
right = node.right
|
110
260
|
op = node.op
|
111
261
|
|
112
262
|
left_type = expr_val.get_type(left)
|
113
263
|
right_type = expr_val.get_type(right)
|
114
|
-
_validate_binary_op(op, left_type, right_type)
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
)
|
124
|
-
|
125
|
-
else:
|
126
|
-
node_type = Real()
|
127
|
-
expr_val.set_type(node, node_type)
|
264
|
+
_validate_binary_op(op, left_type, right_type, treat_qnum_as_float)
|
265
|
+
|
266
|
+
inferred_type = _infer_binary_op_type(
|
267
|
+
expr_val,
|
268
|
+
node,
|
269
|
+
left_type,
|
270
|
+
right_type,
|
271
|
+
machine_precision,
|
272
|
+
treat_qnum_as_float,
|
273
|
+
)
|
274
|
+
expr_val.set_type(node, inferred_type)
|
128
275
|
|
129
276
|
if expr_val.has_value(left) and expr_val.has_value(right):
|
130
277
|
left_value = expr_val.get_value(left)
|
@@ -28,10 +28,11 @@ from classiq.evaluators.qmod_node_evaluators.utils import (
|
|
28
28
|
QmodType,
|
29
29
|
array_len,
|
30
30
|
element_types,
|
31
|
-
|
31
|
+
is_classical_integer,
|
32
32
|
)
|
33
33
|
|
34
|
-
|
34
|
+
# These sympy functions are not declared as int funcs for some reason...
|
35
|
+
INTEGER_FUNCTION_OVERRIDE = {"floor", "ceiling"}
|
35
36
|
|
36
37
|
|
37
38
|
def _check_classical_array_arg_type(
|
@@ -91,8 +92,8 @@ def _check_classical_arg(
|
|
91
92
|
):
|
92
93
|
raise ClassiqExpansionError(
|
93
94
|
f"Parameter {param_name!r} of function {func_name!r} expects a "
|
94
|
-
f"{
|
95
|
-
f"{
|
95
|
+
f"{param_type.qmod_type_name} argument, but got a "
|
96
|
+
f"{arg_type.qmod_type_name}"
|
96
97
|
)
|
97
98
|
|
98
99
|
|
@@ -191,7 +192,9 @@ def try_eval_sympy_function(
|
|
191
192
|
ret_type = Bool()
|
192
193
|
if args is not None:
|
193
194
|
ret_val = bool(sympy_ret_val)
|
194
|
-
elif
|
195
|
+
elif (
|
196
|
+
hasattr(sympy_func, "is_Integer") and sympy_func.is_Integer
|
197
|
+
) or func_name in INTEGER_FUNCTION_OVERRIDE:
|
195
198
|
ret_type = Integer()
|
196
199
|
if args is not None:
|
197
200
|
ret_val = int(sympy_ret_val)
|
@@ -241,15 +244,6 @@ def try_eval_builtin_function(
|
|
241
244
|
)
|
242
245
|
args_have_values = all(expr_val.has_value(arg) for arg in node.args)
|
243
246
|
|
244
|
-
if func_name in MIN_MAX_FUNCTION_NAMES:
|
245
|
-
if not try_eval_sympy_function(expr_val, node, func_name.capitalize()):
|
246
|
-
return False
|
247
|
-
if args_are_int:
|
248
|
-
expr_val.set_type(node, Integer())
|
249
|
-
if expr_val.has_value(node):
|
250
|
-
expr_val.set_value(node, int(expr_val.get_value(node)))
|
251
|
-
return True
|
252
|
-
|
253
247
|
if func_name == "mod_inverse":
|
254
248
|
_validate_no_kwargs(node)
|
255
249
|
ret_type: QmodType
|
@@ -298,6 +292,19 @@ def try_eval_builtin_function(
|
|
298
292
|
)
|
299
293
|
return True
|
300
294
|
|
295
|
+
if func_name == "abs":
|
296
|
+
_validate_no_kwargs(node)
|
297
|
+
if len(node.args) > 0 and is_classical_integer(expr_val.get_type(node.args[0])):
|
298
|
+
ret_type = Integer()
|
299
|
+
else:
|
300
|
+
ret_type = Real()
|
301
|
+
expr_val.set_type(node, ret_type)
|
302
|
+
if args_have_values:
|
303
|
+
expr_val.set_value(
|
304
|
+
node, sympy.Abs(*[expr_val.get_value(arg) for arg in node.args])
|
305
|
+
)
|
306
|
+
return True
|
307
|
+
|
301
308
|
return False
|
302
309
|
|
303
310
|
|
@@ -14,6 +14,7 @@ from classiq.interface.generator.functions.classical_type import (
|
|
14
14
|
from classiq.interface.generator.functions.type_name import TypeName
|
15
15
|
from classiq.interface.model.quantum_type import (
|
16
16
|
QuantumBit,
|
17
|
+
QuantumBitvector,
|
17
18
|
QuantumNumeric,
|
18
19
|
QuantumScalar,
|
19
20
|
)
|
@@ -23,7 +24,6 @@ from classiq.evaluators.qmod_node_evaluators.list_evaluation import list_allowed
|
|
23
24
|
from classiq.evaluators.qmod_node_evaluators.utils import (
|
24
25
|
QmodType,
|
25
26
|
element_types,
|
26
|
-
get_qmod_type_name,
|
27
27
|
is_classical_integer,
|
28
28
|
is_classical_type,
|
29
29
|
qnum_is_qbit,
|
@@ -34,11 +34,13 @@ def comparison_allowed(
|
|
34
34
|
left: QmodType, right: QmodType, inequality: bool = False
|
35
35
|
) -> bool:
|
36
36
|
if isinstance(left, Bool):
|
37
|
-
return isinstance(right, (Bool, QuantumBit)) or (
|
37
|
+
return isinstance(right, (Bool, Integer, QuantumBit)) or (
|
38
38
|
isinstance(right, QuantumNumeric) and qnum_is_qbit(right)
|
39
39
|
)
|
40
40
|
if isinstance(left, Real) or is_classical_integer(left):
|
41
|
-
return isinstance(right, (Real, QuantumScalar)) or is_classical_integer(
|
41
|
+
return isinstance(right, (Real, Bool, QuantumScalar)) or is_classical_integer(
|
42
|
+
right
|
43
|
+
)
|
42
44
|
if isinstance(left, (ClassicalArray, ClassicalTuple)):
|
43
45
|
if inequality:
|
44
46
|
return False
|
@@ -62,6 +64,8 @@ def comparison_allowed(
|
|
62
64
|
return isinstance(right, (Bool, Real, QuantumScalar)) or is_classical_integer(
|
63
65
|
right
|
64
66
|
)
|
67
|
+
if isinstance(left, QuantumBitvector):
|
68
|
+
return False
|
65
69
|
raise ClassiqInternalExpansionError
|
66
70
|
|
67
71
|
|
@@ -76,8 +80,8 @@ def eval_compare(expr_val: QmodAnnotatedExpression, node: ast.Compare) -> None:
|
|
76
80
|
right_type = expr_val.get_type(right)
|
77
81
|
if not comparison_allowed(left_type, right_type, not isinstance(op, ast.Eq)):
|
78
82
|
raise ClassiqExpansionError(
|
79
|
-
f"Cannot compare {
|
80
|
-
f"and {
|
83
|
+
f"Cannot compare {left_type.qmod_type_name} {ast.unparse(left)!r} "
|
84
|
+
f"and {right_type.qmod_type_name} {ast.unparse(right)!r}"
|
81
85
|
)
|
82
86
|
qmod_type: QmodType
|
83
87
|
if not is_classical_type(left_type) or not is_classical_type(right_type):
|
@@ -7,12 +7,22 @@ import sympy
|
|
7
7
|
from classiq.interface.exceptions import (
|
8
8
|
ClassiqExpansionError,
|
9
9
|
)
|
10
|
-
from classiq.interface.generator.functions.classical_type import
|
10
|
+
from classiq.interface.generator.functions.classical_type import (
|
11
|
+
Bool,
|
12
|
+
ClassicalType,
|
13
|
+
Integer,
|
14
|
+
Real,
|
15
|
+
)
|
11
16
|
from classiq.interface.generator.functions.type_name import Enum
|
12
17
|
|
13
18
|
from classiq.evaluators.qmod_annotated_expression import QmodAnnotatedExpression
|
14
19
|
from classiq.evaluators.qmod_node_evaluators.utils import SYMPY_SYMBOLS, QmodType
|
15
20
|
|
21
|
+
QMOD_LITERALS: dict[str, tuple[ClassicalType, Any]] = {
|
22
|
+
"false": (Bool(), False),
|
23
|
+
"true": (Bool(), True),
|
24
|
+
}
|
25
|
+
|
16
26
|
|
17
27
|
def eval_enum_member(
|
18
28
|
expr_val: QmodAnnotatedExpression, node: ast.Attribute, enum: type[IntEnum]
|
@@ -44,6 +54,15 @@ def eval_constant(expr_val: QmodAnnotatedExpression, node: ast.Constant) -> None
|
|
44
54
|
expr_val.set_type(node, constant_type)
|
45
55
|
|
46
56
|
|
57
|
+
def try_eval_qmod_literal(expr_val: QmodAnnotatedExpression, node: ast.Name) -> bool:
|
58
|
+
if node.id not in QMOD_LITERALS:
|
59
|
+
return False
|
60
|
+
lit_type, lit_val = QMOD_LITERALS[node.id]
|
61
|
+
expr_val.set_type(node, lit_type)
|
62
|
+
expr_val.set_value(node, lit_val)
|
63
|
+
return True
|
64
|
+
|
65
|
+
|
47
66
|
def try_eval_sympy_constant(expr_val: QmodAnnotatedExpression, node: ast.Name) -> bool:
|
48
67
|
sympy_val = SYMPY_SYMBOLS.get(node.id)
|
49
68
|
if not isinstance(sympy_val, sympy.Expr) or not sympy_val.is_constant():
|