classiq 0.86.1__py3-none-any.whl → 0.88.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.
Potentially problematic release.
This version of classiq might be problematic. Click here for more details.
- classiq/__init__.py +2 -0
- classiq/applications/__init__.py +1 -2
- classiq/applications/chemistry/hartree_fock.py +5 -1
- classiq/applications/chemistry/op_utils.py +2 -2
- classiq/applications/combinatorial_helpers/combinatorial_problem_utils.py +1 -1
- 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 +34 -2
- classiq/evaluators/argument_types.py +15 -6
- classiq/evaluators/parameter_types.py +43 -39
- classiq/evaluators/qmod_annotated_expression.py +117 -17
- classiq/evaluators/qmod_expression_visitors/out_of_place_node_transformer.py +19 -0
- classiq/evaluators/qmod_expression_visitors/qmod_expression_bwc.py +0 -5
- classiq/evaluators/qmod_expression_visitors/qmod_expression_evaluator.py +66 -16
- classiq/evaluators/qmod_expression_visitors/qmod_expression_renamer.py +48 -26
- classiq/evaluators/qmod_expression_visitors/qmod_expression_simplifier.py +65 -72
- classiq/evaluators/qmod_node_evaluators/attribute_evaluation.py +13 -6
- classiq/evaluators/qmod_node_evaluators/binary_op_evaluation.py +175 -28
- classiq/evaluators/qmod_node_evaluators/classical_function_evaluation.py +36 -19
- classiq/evaluators/qmod_node_evaluators/compare_evaluation.py +17 -5
- classiq/evaluators/qmod_node_evaluators/constant_evaluation.py +24 -2
- 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 +3 -4
- classiq/evaluators/qmod_node_evaluators/subscript_evaluation.py +51 -24
- classiq/evaluators/qmod_node_evaluators/unary_op_evaluation.py +53 -9
- classiq/evaluators/qmod_node_evaluators/utils.py +28 -6
- classiq/evaluators/qmod_type_inference/classical_type_inference.py +188 -0
- classiq/evaluators/qmod_type_inference/quantum_type_inference.py +330 -0
- classiq/evaluators/quantum_type_utils.py +0 -131
- classiq/evaluators/type_type_match.py +1 -1
- classiq/execution/execution_session.py +18 -3
- 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/execution/primitives.py +1 -0
- classiq/interface/generator/application_apis/__init__.py +0 -1
- 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/expressions/atomic_expression_functions.py +0 -2
- classiq/interface/generator/function_param_list.py +129 -5
- classiq/interface/generator/functions/classical_type.py +67 -2
- classiq/interface/generator/functions/qmod_python_interface.py +15 -0
- classiq/interface/generator/functions/type_name.py +12 -0
- classiq/interface/generator/model/preferences/preferences.py +1 -17
- classiq/interface/generator/quantum_program.py +1 -13
- classiq/interface/generator/transpiler_basis_gates.py +5 -1
- classiq/interface/generator/types/builtin_enum_declarations.py +0 -8
- 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 +48 -0
- classiq/interface/model/classical_parameter_declaration.py +4 -0
- classiq/interface/model/handle_binding.py +28 -16
- classiq/interface/model/port_declaration.py +12 -0
- classiq/interface/model/quantum_function_declaration.py +12 -0
- classiq/interface/model/quantum_type.py +117 -2
- classiq/interface/pretty_print/expression_to_qmod.py +7 -8
- 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 +23 -16
- 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/model_expansions/quantum_operations/handle_evaluator.py +2 -8
- classiq/open_library/functions/__init__.py +4 -0
- classiq/open_library/functions/lcu.py +117 -0
- classiq/open_library/functions/state_preparation.py +47 -5
- classiq/qmod/builtins/__init__.py +0 -3
- classiq/qmod/builtins/classical_functions.py +0 -28
- classiq/qmod/builtins/enums.py +26 -20
- classiq/qmod/builtins/functions/__init__.py +0 -5
- classiq/qmod/builtins/operations.py +142 -0
- classiq/qmod/builtins/structs.py +33 -29
- classiq/qmod/native/pretty_printer.py +1 -1
- classiq/qmod/pretty_print/expression_to_python.py +1 -6
- classiq/qmod/pretty_print/pretty_printer.py +4 -1
- classiq/qmod/qmod_variable.py +94 -2
- classiq/qmod/semantics/annotation/call_annotation.py +4 -2
- classiq/qmod/semantics/annotation/qstruct_annotator.py +20 -5
- {classiq-0.86.1.dist-info → classiq-0.88.0.dist-info}/METADATA +5 -5
- {classiq-0.86.1.dist-info → classiq-0.88.0.dist-info}/RECORD +106 -124
- classiq/applications/finance/__init__.py +0 -15
- classiq/interface/finance/finance_modelling_params.py +0 -11
- classiq/interface/finance/function_input.py +0 -102
- classiq/interface/finance/gaussian_model_input.py +0 -50
- classiq/interface/finance/log_normal_model_input.py +0 -40
- classiq/interface/finance/model_input.py +0 -22
- classiq/interface/generator/amplitude_estimation.py +0 -34
- classiq/interface/generator/application_apis/finance_declarations.py +0 -108
- classiq/interface/generator/expressions/enums/__init__.py +0 -0
- classiq/interface/generator/expressions/enums/finance_functions.py +0 -12
- classiq/interface/generator/finance.py +0 -107
- 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/__init__.py +0 -0
- classiq/interface/grover/grover_modelling_params.py +0 -13
- classiq/model_expansions/transformers/var_splitter.py +0 -224
- classiq/qmod/builtins/functions/finance.py +0 -34
- /classiq/{interface/finance → evaluators/qmod_type_inference}/__init__.py +0 -0
- {classiq-0.86.1.dist-info → classiq-0.88.0.dist-info}/WHEEL +0 -0
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
from
|
|
2
|
-
|
|
3
|
-
from classiq.interface.model.handle_binding import HandleBinding, NestedHandleBinding
|
|
1
|
+
from classiq.interface.model.handle_binding import HandleBinding
|
|
4
2
|
|
|
5
3
|
from classiq.evaluators.qmod_annotated_expression import (
|
|
6
4
|
QmodAnnotatedExpression,
|
|
@@ -8,37 +6,61 @@ from classiq.evaluators.qmod_annotated_expression import (
|
|
|
8
6
|
)
|
|
9
7
|
|
|
10
8
|
|
|
11
|
-
def
|
|
9
|
+
def replace_expression_vars(
|
|
12
10
|
expr_val: QmodAnnotatedExpression,
|
|
13
11
|
renaming: dict[HandleBinding, HandleBinding],
|
|
14
|
-
) ->
|
|
12
|
+
) -> QmodAnnotatedExpression:
|
|
13
|
+
expr_val = expr_val.clone()
|
|
15
14
|
if len(renaming) == 0:
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
expr_val.lock()
|
|
16
|
+
return expr_val
|
|
17
|
+
all_vars = dict(expr_val.get_classical_vars()) | dict(expr_val.get_quantum_vars())
|
|
18
18
|
for node_id, var in all_vars.items():
|
|
19
|
-
renamed_var =
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
renamed_var = var
|
|
20
|
+
for source, target in renaming.items():
|
|
21
|
+
if renamed_var.name == source.name:
|
|
22
|
+
renamed_var = renamed_var.replace_prefix(source, target)
|
|
23
|
+
if renamed_var is var:
|
|
24
|
+
continue
|
|
25
|
+
node_type = expr_val.get_type(node_id)
|
|
26
|
+
expr_val.clear_node_data(node_id)
|
|
27
|
+
expr_val.set_type(node_id, node_type)
|
|
28
|
+
expr_val.set_var(node_id, renamed_var)
|
|
29
|
+
expr_val.lock()
|
|
30
|
+
return expr_val
|
|
23
31
|
|
|
24
32
|
|
|
25
|
-
def
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
if
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
def replace_expression_type_attrs(
|
|
34
|
+
expr_val: QmodAnnotatedExpression,
|
|
35
|
+
renaming: dict[tuple[HandleBinding, str], HandleBinding],
|
|
36
|
+
) -> QmodAnnotatedExpression:
|
|
37
|
+
expr_val = expr_val.clone()
|
|
38
|
+
if len(renaming) == 0:
|
|
39
|
+
expr_val.lock()
|
|
40
|
+
return expr_val
|
|
41
|
+
type_attrs = dict(expr_val.get_quantum_type_attributes())
|
|
42
|
+
for node_id, ta in type_attrs.items():
|
|
43
|
+
var = expr_val.get_var(ta.value)
|
|
44
|
+
renamed_var = var
|
|
45
|
+
renamed_attr = ta.attr
|
|
46
|
+
for (source, attr), target in renaming.items():
|
|
47
|
+
if renamed_attr == attr and renamed_var.name == source.name:
|
|
48
|
+
renamed_var = renamed_var.replace_prefix(source, target)
|
|
49
|
+
if renamed_var is var:
|
|
50
|
+
continue
|
|
51
|
+
node_type = expr_val.get_type(node_id)
|
|
52
|
+
expr_val.clear_node_data(node_id)
|
|
53
|
+
expr_val.set_type(node_id, node_type)
|
|
54
|
+
expr_val.set_var(node_id, renamed_var)
|
|
55
|
+
expr_val.lock()
|
|
56
|
+
return expr_val
|
|
36
57
|
|
|
37
58
|
|
|
38
|
-
def
|
|
59
|
+
def replace_expression_nodes(
|
|
39
60
|
expr_val: QmodAnnotatedExpression,
|
|
40
|
-
renaming: dict[QmodExprNodeId,
|
|
61
|
+
renaming: dict[QmodExprNodeId, str],
|
|
41
62
|
) -> str:
|
|
63
|
+
expr_val = expr_val.clone()
|
|
42
64
|
for node_id, renamed_var in renaming.items():
|
|
43
|
-
expr_val.set_var(node_id, renamed_var)
|
|
44
|
-
return expr_val
|
|
65
|
+
expr_val.set_var(node_id, HandleBinding(name=f"{renamed_var}"))
|
|
66
|
+
return str(expr_val)
|
|
@@ -1,22 +1,20 @@
|
|
|
1
1
|
import ast
|
|
2
|
-
from typing import Any,
|
|
2
|
+
from typing import Any, Callable, TypeVar, cast
|
|
3
3
|
|
|
4
4
|
import sympy
|
|
5
5
|
|
|
6
6
|
from classiq.interface.exceptions import ClassiqInternalExpansionError
|
|
7
|
-
from classiq.interface.generator.
|
|
8
|
-
|
|
9
|
-
from classiq.interface.model.handle_binding import HandleBinding
|
|
10
|
-
from classiq.interface.model.quantum_type import (
|
|
11
|
-
QuantumBit,
|
|
12
|
-
QuantumNumeric,
|
|
13
|
-
QuantumType,
|
|
7
|
+
from classiq.interface.generator.expressions.proxies.classical.qmod_struct_instance import (
|
|
8
|
+
QmodStructInstance,
|
|
14
9
|
)
|
|
15
10
|
|
|
16
11
|
from classiq.evaluators.qmod_annotated_expression import (
|
|
17
12
|
QmodAnnotatedExpression,
|
|
18
13
|
QmodExprNodeId,
|
|
19
14
|
)
|
|
15
|
+
from classiq.evaluators.qmod_expression_visitors.out_of_place_node_transformer import (
|
|
16
|
+
OutOfPlaceNodeTransformer,
|
|
17
|
+
)
|
|
20
18
|
from classiq.evaluators.qmod_expression_visitors.sympy_wrappers import (
|
|
21
19
|
BitwiseAnd,
|
|
22
20
|
BitwiseNot,
|
|
@@ -25,7 +23,7 @@ from classiq.evaluators.qmod_expression_visitors.sympy_wrappers import (
|
|
|
25
23
|
LShift,
|
|
26
24
|
RShift,
|
|
27
25
|
)
|
|
28
|
-
from classiq.
|
|
26
|
+
from classiq.model_expansions.atomic_expression_functions_defs import do_div
|
|
29
27
|
|
|
30
28
|
_SYMPY_WRAPPERS = {
|
|
31
29
|
wrapper.__name__: wrapper
|
|
@@ -37,15 +35,19 @@ _SYMPY_WRAPPERS = {
|
|
|
37
35
|
LShift,
|
|
38
36
|
RShift,
|
|
39
37
|
]
|
|
38
|
+
} | {
|
|
39
|
+
do_div.__name__: do_div,
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
_PY_NODE = TypeVar("_PY_NODE", bound=ast.AST)
|
|
43
|
+
|
|
42
44
|
|
|
43
|
-
class _VarMaskTransformer(
|
|
45
|
+
class _VarMaskTransformer(OutOfPlaceNodeTransformer):
|
|
44
46
|
def __init__(self, expr_val: QmodAnnotatedExpression) -> None:
|
|
45
47
|
self._expr_val = expr_val
|
|
46
48
|
self._mask_id = 0
|
|
47
49
|
self.masks: dict[str, QmodExprNodeId] = {}
|
|
48
|
-
self._assigned_masks: dict[
|
|
50
|
+
self._assigned_masks: dict[Any, str] = {}
|
|
49
51
|
|
|
50
52
|
def _create_mask(self) -> str:
|
|
51
53
|
mask = f"x{self._mask_id}"
|
|
@@ -54,29 +56,46 @@ class _VarMaskTransformer(ast.NodeTransformer):
|
|
|
54
56
|
|
|
55
57
|
def visit(self, node: ast.AST) -> ast.AST:
|
|
56
58
|
if self._expr_val.has_value(node):
|
|
57
|
-
|
|
59
|
+
val = self._expr_val.get_value(node)
|
|
60
|
+
if not isinstance(val, (list, QmodStructInstance)):
|
|
61
|
+
return ast.Constant(value=val)
|
|
62
|
+
mask = self._create_mask()
|
|
63
|
+
self.masks[mask] = id(node)
|
|
64
|
+
return ast.Name(id=mask)
|
|
58
65
|
if self._expr_val.has_var(node):
|
|
59
66
|
var = self._expr_val.get_var(node)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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):
|
|
67
|
+
var_str = str(var.collapse())
|
|
68
|
+
if var_str in self._assigned_masks:
|
|
69
|
+
return ast.Name(id=self._assigned_masks[var_str])
|
|
68
70
|
mask = self._create_mask()
|
|
71
|
+
self._assigned_masks[var_str] = mask
|
|
69
72
|
self.masks[mask] = id(node)
|
|
70
73
|
return ast.Name(id=mask)
|
|
71
|
-
return
|
|
72
|
-
|
|
73
|
-
def
|
|
74
|
-
|
|
74
|
+
return super().visit(node)
|
|
75
|
+
|
|
76
|
+
def _reduce_node(
|
|
77
|
+
self, node: _PY_NODE, inner_node: ast.AST, key: Callable[[_PY_NODE], str]
|
|
78
|
+
) -> ast.AST:
|
|
79
|
+
new_value = self.visit(inner_node)
|
|
80
|
+
if isinstance(new_value, ast.Name):
|
|
81
|
+
mask_key = (new_value.id, key(node))
|
|
82
|
+
if mask_key in self._assigned_masks:
|
|
83
|
+
return ast.Name(self._assigned_masks[mask_key])
|
|
84
|
+
mask = self._create_mask()
|
|
85
|
+
self._assigned_masks[mask_key] = mask
|
|
86
|
+
else:
|
|
87
|
+
mask = self._create_mask()
|
|
75
88
|
self.masks[mask] = id(node)
|
|
76
89
|
return ast.Name(id=mask)
|
|
77
90
|
|
|
91
|
+
def visit_Attribute(self, node: ast.Attribute) -> ast.AST:
|
|
92
|
+
return self._reduce_node(node, node.value, lambda n: n.attr)
|
|
78
93
|
|
|
79
|
-
|
|
94
|
+
def visit_Subscript(self, node: ast.Subscript) -> ast.AST:
|
|
95
|
+
return self._reduce_node(node, node.value, lambda n: ast.unparse(node.slice))
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class _InverseVarMaskTransformer(OutOfPlaceNodeTransformer):
|
|
80
99
|
def __init__(
|
|
81
100
|
self, expr_val: QmodAnnotatedExpression, masks: dict[str, QmodExprNodeId]
|
|
82
101
|
) -> None:
|
|
@@ -93,7 +112,7 @@ class _InverseVarMaskTransformer(ast.NodeTransformer):
|
|
|
93
112
|
return node
|
|
94
113
|
|
|
95
114
|
|
|
96
|
-
class _SympyCompatibilityTransformer(
|
|
115
|
+
class _SympyCompatibilityTransformer(OutOfPlaceNodeTransformer):
|
|
97
116
|
def visit_BoolOp(self, node: ast.BoolOp) -> ast.Call:
|
|
98
117
|
if len(node.values) != 2:
|
|
99
118
|
raise ClassiqInternalExpansionError
|
|
@@ -151,6 +170,12 @@ class _SympyCompatibilityTransformer(ast.NodeTransformer):
|
|
|
151
170
|
sympy_func = BitwiseXor.__name__
|
|
152
171
|
elif isinstance(node.op, ast.BitAnd):
|
|
153
172
|
sympy_func = BitwiseAnd.__name__
|
|
173
|
+
elif isinstance(node.op, ast.Div):
|
|
174
|
+
return ast.Call(
|
|
175
|
+
func=ast.Name(id=do_div.__name__),
|
|
176
|
+
args=[node.left, node.right],
|
|
177
|
+
keywords=[],
|
|
178
|
+
)
|
|
154
179
|
else:
|
|
155
180
|
return node
|
|
156
181
|
return ast.Call(
|
|
@@ -158,7 +183,7 @@ class _SympyCompatibilityTransformer(ast.NodeTransformer):
|
|
|
158
183
|
)
|
|
159
184
|
|
|
160
185
|
|
|
161
|
-
class _InverseSympyCompatibilityTransformer(
|
|
186
|
+
class _InverseSympyCompatibilityTransformer(OutOfPlaceNodeTransformer):
|
|
162
187
|
def visit_Call(self, node: ast.Call) -> Any:
|
|
163
188
|
node = cast(ast.Call, self.generic_visit(node))
|
|
164
189
|
if not isinstance(node.func, ast.Name):
|
|
@@ -183,12 +208,12 @@ class _InverseSympyCompatibilityTransformer(ast.NodeTransformer):
|
|
|
183
208
|
and (len(node.args) != 2 or len(node.keywords) > 0)
|
|
184
209
|
) or (
|
|
185
210
|
func in {BitwiseNot.__name__}
|
|
186
|
-
and (len(node.args) !=
|
|
211
|
+
and (len(node.args) != 1 or len(node.keywords) > 0)
|
|
187
212
|
):
|
|
188
213
|
raise ClassiqInternalExpansionError
|
|
189
214
|
|
|
190
215
|
if func == BitwiseNot.__name__:
|
|
191
|
-
return ast.UnaryOp(op=ast.Invert, operand=node.args[0])
|
|
216
|
+
return ast.UnaryOp(op=ast.Invert(), operand=node.args[0])
|
|
192
217
|
|
|
193
218
|
if func == "Eq":
|
|
194
219
|
return ast.Compare(
|
|
@@ -226,6 +251,15 @@ class _InverseSympyCompatibilityTransformer(ast.NodeTransformer):
|
|
|
226
251
|
if func == BitwiseAnd.__name__:
|
|
227
252
|
return ast.BinOp(left=node.args[0], op=ast.BitAnd(), right=node.args[1])
|
|
228
253
|
|
|
254
|
+
if func == "Abs":
|
|
255
|
+
node.func.id = "abs"
|
|
256
|
+
if func == "Max":
|
|
257
|
+
node.func.id = "max"
|
|
258
|
+
elif func == "Min":
|
|
259
|
+
node.func.id = "min"
|
|
260
|
+
if func == "Mod":
|
|
261
|
+
return ast.BinOp(left=node.args[0], op=ast.Mod(), right=node.args[1])
|
|
262
|
+
|
|
229
263
|
return node
|
|
230
264
|
|
|
231
265
|
def visit_UnaryOp(self, node: ast.UnaryOp) -> Any:
|
|
@@ -247,46 +281,12 @@ class _InverseSympyCompatibilityTransformer(ast.NodeTransformer):
|
|
|
247
281
|
return node
|
|
248
282
|
|
|
249
283
|
|
|
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]]:
|
|
284
|
+
def simplify_qmod_expression(expr_val: QmodAnnotatedExpression) -> str:
|
|
284
285
|
if expr_val.has_value(expr_val.root):
|
|
285
286
|
raise ClassiqInternalExpansionError(
|
|
286
287
|
"This expression is a constant value. No need for simplification"
|
|
287
288
|
)
|
|
288
289
|
var_mask_transformer = _VarMaskTransformer(expr_val)
|
|
289
|
-
masks = var_mask_transformer.masks
|
|
290
290
|
mask_expr = var_mask_transformer.visit(expr_val.root)
|
|
291
291
|
sympy_expr = _SympyCompatibilityTransformer().visit(mask_expr)
|
|
292
292
|
simplified_expr = str(
|
|
@@ -295,14 +295,7 @@ def simplify_qmod_expression(
|
|
|
295
295
|
restored_expr = _InverseSympyCompatibilityTransformer().visit(
|
|
296
296
|
ast.parse(simplified_expr, mode="eval")
|
|
297
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
298
|
restored_expr = _InverseVarMaskTransformer(
|
|
306
299
|
expr_val, var_mask_transformer.masks
|
|
307
300
|
).visit(restored_expr)
|
|
308
|
-
return ast.unparse(restored_expr)
|
|
301
|
+
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(
|
|
@@ -42,7 +42,7 @@ def _eval_type_attribute(
|
|
|
42
42
|
return
|
|
43
43
|
if isinstance(subject_type, (ClassicalArray, QuantumBitvector)) and attr == "len":
|
|
44
44
|
expr_val.set_type(node, Integer())
|
|
45
|
-
if subject_type.
|
|
45
|
+
if subject_type.has_constant_length:
|
|
46
46
|
expr_val.set_value(node, subject_type.length_value)
|
|
47
47
|
elif isinstance(subject_type, QuantumType):
|
|
48
48
|
expr_val.set_quantum_type_attr(node, subject, attr)
|
|
@@ -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)
|