classiq 0.86.1__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.1.dist-info → classiq-0.87.0.dist-info}/METADATA +3 -3
- {classiq-0.86.1.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.1.dist-info → classiq-0.87.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,97 @@
|
|
1
|
+
import ast
|
2
|
+
|
3
|
+
from classiq.interface.exceptions import (
|
4
|
+
ClassiqExpansionError,
|
5
|
+
ClassiqInternalExpansionError,
|
6
|
+
)
|
7
|
+
from classiq.interface.generator.functions.classical_type import Integer, Real
|
8
|
+
from classiq.interface.model.quantum_type import QuantumNumeric
|
9
|
+
|
10
|
+
from classiq.evaluators.qmod_annotated_expression import QmodAnnotatedExpression
|
11
|
+
from classiq.evaluators.qmod_node_evaluators.numeric_attrs_utils import (
|
12
|
+
get_numeric_attrs,
|
13
|
+
)
|
14
|
+
from classiq.evaluators.qmod_node_evaluators.utils import (
|
15
|
+
QmodType,
|
16
|
+
is_classical_integer,
|
17
|
+
is_classical_type,
|
18
|
+
is_numeric_type,
|
19
|
+
)
|
20
|
+
from classiq.model_expansions.arithmetic import NumericAttributes
|
21
|
+
from classiq.model_expansions.arithmetic_compute_result_attrs import (
|
22
|
+
compute_result_attrs_max,
|
23
|
+
compute_result_attrs_min,
|
24
|
+
)
|
25
|
+
|
26
|
+
|
27
|
+
def _infer_min_max_op_type(
|
28
|
+
expr_val: QmodAnnotatedExpression,
|
29
|
+
node: ast.Call,
|
30
|
+
func_name: str,
|
31
|
+
args_types: list[QmodType],
|
32
|
+
treat_qnum_as_float: bool,
|
33
|
+
machine_precision: int,
|
34
|
+
) -> QmodType:
|
35
|
+
if all(is_classical_type(arg_type) for arg_type in args_types):
|
36
|
+
if all(is_classical_integer(arg_type) for arg_type in args_types):
|
37
|
+
return Integer()
|
38
|
+
return Real()
|
39
|
+
|
40
|
+
args_attrs: list[NumericAttributes] = []
|
41
|
+
for arg, arg_type in zip(node.args, args_types):
|
42
|
+
attrs = get_numeric_attrs(
|
43
|
+
expr_val, arg, arg_type, machine_precision, treat_qnum_as_float
|
44
|
+
)
|
45
|
+
if attrs is None:
|
46
|
+
return QuantumNumeric()
|
47
|
+
args_attrs.append(attrs)
|
48
|
+
|
49
|
+
if func_name == "min":
|
50
|
+
result_attrs = compute_result_attrs_min(args_attrs, machine_precision)
|
51
|
+
elif func_name == "max":
|
52
|
+
result_attrs = compute_result_attrs_max(args_attrs, machine_precision)
|
53
|
+
else:
|
54
|
+
raise ClassiqInternalExpansionError
|
55
|
+
|
56
|
+
return result_attrs.to_quantum_numeric()
|
57
|
+
|
58
|
+
|
59
|
+
def eval_min_max_op(
|
60
|
+
expr_val: QmodAnnotatedExpression,
|
61
|
+
node: ast.Call,
|
62
|
+
func_name: str,
|
63
|
+
treat_qnum_as_float: bool,
|
64
|
+
machine_precision: int,
|
65
|
+
) -> None:
|
66
|
+
if len(node.args) < 1:
|
67
|
+
raise ClassiqExpansionError(f"{func_name!r} expects at least one argument")
|
68
|
+
|
69
|
+
args_types = [expr_val.get_type(arg) for arg in node.args]
|
70
|
+
if not all(is_numeric_type(arg_type) for arg_type in args_types):
|
71
|
+
raise ClassiqExpansionError(
|
72
|
+
f"All arguments of {func_name!r} must be scalar values"
|
73
|
+
)
|
74
|
+
|
75
|
+
inferred_type = _infer_min_max_op_type(
|
76
|
+
expr_val,
|
77
|
+
node,
|
78
|
+
func_name,
|
79
|
+
args_types,
|
80
|
+
treat_qnum_as_float,
|
81
|
+
machine_precision,
|
82
|
+
)
|
83
|
+
expr_val.set_type(node, inferred_type)
|
84
|
+
|
85
|
+
if all(expr_val.has_value(arg) for arg in node.args):
|
86
|
+
values = [expr_val.get_value(arg) for arg in node.args]
|
87
|
+
if not all(isinstance(value, (int, float)) for value in values):
|
88
|
+
raise ClassiqExpansionError(f"Invalid argument for function {func_name!r}")
|
89
|
+
|
90
|
+
if func_name == "min":
|
91
|
+
result_value = min(*values)
|
92
|
+
elif func_name == "max":
|
93
|
+
result_value = max(*values)
|
94
|
+
else:
|
95
|
+
raise ClassiqInternalExpansionError
|
96
|
+
|
97
|
+
expr_val.set_value(node, result_value)
|
@@ -1,6 +1,8 @@
|
|
1
1
|
import ast
|
2
2
|
from typing import Any
|
3
3
|
|
4
|
+
import sympy
|
5
|
+
|
4
6
|
from classiq.interface.exceptions import (
|
5
7
|
ClassiqInternalExpansionError,
|
6
8
|
)
|
@@ -8,42 +10,25 @@ from classiq.interface.generator.expressions.proxies.classical.qmod_struct_insta
|
|
8
10
|
QmodStructInstance,
|
9
11
|
)
|
10
12
|
from classiq.interface.generator.functions.classical_type import (
|
11
|
-
Bool,
|
12
|
-
ClassicalTuple,
|
13
13
|
ClassicalType,
|
14
|
-
Integer,
|
15
|
-
Real,
|
16
14
|
)
|
17
|
-
from classiq.interface.generator.functions.type_name import Struct
|
18
15
|
from classiq.interface.model.handle_binding import HandleBinding
|
19
16
|
from classiq.interface.model.quantum_type import QuantumType
|
20
17
|
|
21
18
|
from classiq.evaluators.qmod_annotated_expression import QmodAnnotatedExpression
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
if isinstance(value, bool):
|
26
|
-
return Bool()
|
27
|
-
if isinstance(value, int):
|
28
|
-
return Integer()
|
29
|
-
if isinstance(value, (float, complex)):
|
30
|
-
return Real()
|
31
|
-
if isinstance(value, list):
|
32
|
-
return ClassicalTuple(
|
33
|
-
element_types=[_infer_classical_type(item) for item in value]
|
34
|
-
)
|
35
|
-
if isinstance(value, QmodStructInstance):
|
36
|
-
classical_type = Struct(name=value.struct_declaration.name)
|
37
|
-
classical_type.set_classical_struct_decl(value.struct_declaration)
|
38
|
-
return classical_type
|
39
|
-
raise ClassiqInternalExpansionError
|
19
|
+
from classiq.evaluators.qmod_type_inference.classical_type_inference import (
|
20
|
+
infer_classical_type,
|
21
|
+
)
|
40
22
|
|
41
23
|
|
42
24
|
def eval_name(expr_val: QmodAnnotatedExpression, node: ast.Name, value: Any) -> None:
|
43
|
-
|
44
|
-
|
25
|
+
# FIXME: Remove sympy compatibility (CLS-3214)
|
26
|
+
if isinstance(
|
27
|
+
value, (bool, int, float, complex, list, QmodStructInstance, sympy.Basic)
|
28
|
+
):
|
29
|
+
expr_val.set_type(node, infer_classical_type(value))
|
45
30
|
expr_val.set_value(node, value)
|
46
|
-
elif isinstance(value, (ClassicalType, QuantumType)):
|
31
|
+
elif isinstance(value, (ClassicalType, QuantumType)): # type:ignore[unreachable]
|
47
32
|
expr_val.set_type(node, value)
|
48
33
|
expr_val.set_var(node, HandleBinding(name=node.id))
|
49
34
|
else:
|
@@ -0,0 +1,56 @@
|
|
1
|
+
import ast
|
2
|
+
from typing import TYPE_CHECKING, Optional
|
3
|
+
|
4
|
+
from classiq.interface.exceptions import ClassiqExpansionError
|
5
|
+
from classiq.interface.generator.functions.classical_type import Bool
|
6
|
+
from classiq.interface.model.quantum_type import QuantumScalar
|
7
|
+
|
8
|
+
from classiq.evaluators.qmod_annotated_expression import QmodAnnotatedExpression
|
9
|
+
from classiq.evaluators.qmod_node_evaluators.utils import QmodType, is_classical_type
|
10
|
+
from classiq.model_expansions.arithmetic import NumericAttributes
|
11
|
+
|
12
|
+
|
13
|
+
def get_numeric_attrs(
|
14
|
+
expr_val: QmodAnnotatedExpression,
|
15
|
+
node: ast.AST,
|
16
|
+
qmod_type: QmodType,
|
17
|
+
machine_precision: int,
|
18
|
+
treat_qnum_as_float: bool,
|
19
|
+
) -> Optional[NumericAttributes]:
|
20
|
+
if isinstance(qmod_type, Bool):
|
21
|
+
return NumericAttributes.from_bounds(0, 1, 0, machine_precision)
|
22
|
+
if is_classical_type(qmod_type):
|
23
|
+
value = get_classical_value_for_arithmetic(
|
24
|
+
expr_val, node, qmod_type, treat_qnum_as_float
|
25
|
+
)
|
26
|
+
if value is None:
|
27
|
+
return None
|
28
|
+
return NumericAttributes.from_constant(value, machine_precision)
|
29
|
+
|
30
|
+
if TYPE_CHECKING:
|
31
|
+
assert isinstance(qmod_type, QuantumScalar)
|
32
|
+
if not qmod_type.is_evaluated:
|
33
|
+
return None
|
34
|
+
return NumericAttributes.from_quantum_scalar(qmod_type, machine_precision)
|
35
|
+
|
36
|
+
|
37
|
+
def get_classical_value_for_arithmetic(
|
38
|
+
expr_val: QmodAnnotatedExpression,
|
39
|
+
node: ast.AST,
|
40
|
+
qmod_type: QmodType,
|
41
|
+
treat_qnum_as_float: bool,
|
42
|
+
) -> Optional[float]:
|
43
|
+
if not is_classical_type(qmod_type):
|
44
|
+
return None
|
45
|
+
if not expr_val.has_value(node):
|
46
|
+
return None
|
47
|
+
|
48
|
+
value = expr_val.get_value(node)
|
49
|
+
if not isinstance(value, (int, float)):
|
50
|
+
if treat_qnum_as_float and isinstance(value, complex):
|
51
|
+
return None
|
52
|
+
raise ClassiqExpansionError(
|
53
|
+
"Arithmetic of quantum variables and non-real values is not supported"
|
54
|
+
)
|
55
|
+
|
56
|
+
return float(value)
|
@@ -0,0 +1,40 @@
|
|
1
|
+
import ast
|
2
|
+
from collections.abc import Sequence
|
3
|
+
|
4
|
+
import sympy
|
5
|
+
|
6
|
+
from classiq.interface.exceptions import ClassiqExpansionError
|
7
|
+
from classiq.interface.generator.functions.classical_type import Bool, Integer, Real
|
8
|
+
|
9
|
+
from classiq.evaluators.qmod_annotated_expression import QmodAnnotatedExpression
|
10
|
+
|
11
|
+
|
12
|
+
def eval_piecewise(
|
13
|
+
expr_val: QmodAnnotatedExpression,
|
14
|
+
node: ast.Call,
|
15
|
+
args: Sequence[tuple[ast.AST, ast.AST]],
|
16
|
+
) -> None:
|
17
|
+
cond_types = [expr_val.get_type(cond) for _, cond in args]
|
18
|
+
if not all(isinstance(cond_type, Bool) for cond_type in cond_types):
|
19
|
+
raise ClassiqExpansionError(
|
20
|
+
"Piecewise conditions must be classical Boolean values"
|
21
|
+
)
|
22
|
+
|
23
|
+
value_types = [expr_val.get_type(value) for value, _ in args]
|
24
|
+
if not all(isinstance(value_type, (Integer, Real)) for value_type in value_types):
|
25
|
+
raise ClassiqExpansionError("Piecewise values must be classical numeric values")
|
26
|
+
|
27
|
+
if all(isinstance(value_type, Integer) for value_type in value_types):
|
28
|
+
expr_val.set_type(node, Integer())
|
29
|
+
else:
|
30
|
+
expr_val.set_type(node, Real())
|
31
|
+
|
32
|
+
if not all(
|
33
|
+
expr_val.has_value(value) and expr_val.has_value(cond) for value, cond in args
|
34
|
+
):
|
35
|
+
return
|
36
|
+
|
37
|
+
values = [
|
38
|
+
(expr_val.get_value(value), expr_val.get_value(cond)) for value, cond in args
|
39
|
+
]
|
40
|
+
expr_val.set_value(node, sympy.Piecewise(*values))
|
@@ -15,7 +15,6 @@ from classiq.evaluators.qmod_annotated_expression import QmodAnnotatedExpression
|
|
15
15
|
from classiq.evaluators.qmod_node_evaluators.compare_evaluation import (
|
16
16
|
comparison_allowed,
|
17
17
|
)
|
18
|
-
from classiq.evaluators.qmod_node_evaluators.utils import get_qmod_type_name
|
19
18
|
|
20
19
|
|
21
20
|
def eval_struct_instantiation(
|
@@ -42,8 +41,8 @@ def eval_struct_instantiation(
|
|
42
41
|
expected_type = decl.variables[field_name]
|
43
42
|
if not comparison_allowed(assignment_type, expected_type):
|
44
43
|
raise ClassiqExpansionError(
|
45
|
-
f"Cannot assign value of type {
|
46
|
-
f"to field {field_name!r} of type {
|
44
|
+
f"Cannot assign value of type {assignment_type.qmod_type_name} "
|
45
|
+
f"to field {field_name!r} of type {expected_type.qmod_type_name}"
|
47
46
|
)
|
48
47
|
|
49
48
|
classical_type = Struct(name=decl.name)
|
@@ -4,10 +4,7 @@ from typing import Optional, cast
|
|
4
4
|
from classiq.interface.exceptions import (
|
5
5
|
ClassiqExpansionError,
|
6
6
|
ClassiqInternalExpansionError,
|
7
|
-
|
8
|
-
from classiq.interface.generator.arith.arithmetic import (
|
9
|
-
aggregate_numeric_types,
|
10
|
-
compute_arithmetic_result_type,
|
7
|
+
ClassiqValueError,
|
11
8
|
)
|
12
9
|
from classiq.interface.generator.expressions.expression import Expression
|
13
10
|
from classiq.interface.generator.functions.classical_type import (
|
@@ -16,6 +13,7 @@ from classiq.interface.generator.functions.classical_type import (
|
|
16
13
|
Integer,
|
17
14
|
Real,
|
18
15
|
)
|
16
|
+
from classiq.interface.helpers.text_utils import s
|
19
17
|
from classiq.interface.model.handle_binding import (
|
20
18
|
SlicedHandleBinding,
|
21
19
|
SubscriptHandleBinding,
|
@@ -23,16 +21,19 @@ from classiq.interface.model.handle_binding import (
|
|
23
21
|
from classiq.interface.model.quantum_type import (
|
24
22
|
QuantumBitvector,
|
25
23
|
QuantumNumeric,
|
24
|
+
QuantumScalar,
|
26
25
|
QuantumType,
|
27
|
-
register_info_to_quantum_type,
|
28
26
|
)
|
29
27
|
|
30
28
|
from classiq.evaluators.qmod_annotated_expression import QmodAnnotatedExpression
|
31
29
|
from classiq.evaluators.qmod_node_evaluators.utils import (
|
32
30
|
QmodType,
|
31
|
+
array_len,
|
33
32
|
element_types,
|
34
|
-
|
35
|
-
|
33
|
+
get_numeric_properties,
|
34
|
+
)
|
35
|
+
from classiq.model_expansions.arithmetic_compute_result_attrs import (
|
36
|
+
compute_result_attrs_quantum_subscript,
|
36
37
|
)
|
37
38
|
|
38
39
|
|
@@ -49,7 +50,7 @@ def _eval_slice(expr_val: QmodAnnotatedExpression, node: ast.Subscript) -> None:
|
|
49
50
|
for index_type in (start_type, stop_type):
|
50
51
|
if not isinstance(index_type, Integer):
|
51
52
|
raise ClassiqExpansionError(
|
52
|
-
f"Slice indices must be integers, not {
|
53
|
+
f"Slice indices must be integers, not {index_type.raw_qmod_type_name}"
|
53
54
|
)
|
54
55
|
|
55
56
|
start_val: Optional[int] = None
|
@@ -105,7 +106,7 @@ def _eval_slice(expr_val: QmodAnnotatedExpression, node: ast.Subscript) -> None:
|
|
105
106
|
)
|
106
107
|
else:
|
107
108
|
raise ClassiqExpansionError(
|
108
|
-
f"{
|
109
|
+
f"{subject_type.raw_qmod_type_name} is not subscriptable"
|
109
110
|
)
|
110
111
|
expr_val.set_type(node, slice_type)
|
111
112
|
|
@@ -134,7 +135,8 @@ def _eval_subscript(expr_val: QmodAnnotatedExpression, node: ast.Subscript) -> N
|
|
134
135
|
index_type = expr_val.get_type(subscript)
|
135
136
|
if not isinstance(index_type, Integer):
|
136
137
|
raise ClassiqExpansionError(
|
137
|
-
f"Array indices must be integers or slices, not
|
138
|
+
f"Array indices must be integers or slices, not "
|
139
|
+
f"{index_type.raw_qmod_type_name}"
|
138
140
|
)
|
139
141
|
|
140
142
|
sub_val: Optional[int] = None
|
@@ -165,7 +167,7 @@ def _eval_subscript(expr_val: QmodAnnotatedExpression, node: ast.Subscript) -> N
|
|
165
167
|
sub_type = raw_subject_type.element_type
|
166
168
|
else:
|
167
169
|
raise ClassiqExpansionError(
|
168
|
-
f"{
|
170
|
+
f"{subject_type.raw_qmod_type_name} is not subscriptable"
|
169
171
|
)
|
170
172
|
expr_val.set_type(node, sub_type)
|
171
173
|
|
@@ -192,15 +194,38 @@ def eval_subscript(expr_val: QmodAnnotatedExpression, node: ast.Subscript) -> No
|
|
192
194
|
_eval_subscript(expr_val, node)
|
193
195
|
|
194
196
|
|
197
|
+
def validate_quantum_subscript_index_properties(
|
198
|
+
index_size: Optional[int],
|
199
|
+
index_sign: Optional[bool],
|
200
|
+
index_fraction_digits: Optional[int],
|
201
|
+
array_len: Optional[int],
|
202
|
+
) -> None:
|
203
|
+
if index_sign or (index_fraction_digits is not None and index_fraction_digits > 0):
|
204
|
+
raise ClassiqValueError("Quantum index must be an unsigned integer")
|
205
|
+
if array_len == 0:
|
206
|
+
raise ClassiqValueError(
|
207
|
+
"Classical arrays indexed by a quantum variable must not be empty"
|
208
|
+
)
|
209
|
+
if array_len is None or index_size is None:
|
210
|
+
return
|
211
|
+
if 2**index_size > array_len:
|
212
|
+
adjective = "short"
|
213
|
+
elif 2**index_size < array_len:
|
214
|
+
adjective = "long"
|
215
|
+
else:
|
216
|
+
return
|
217
|
+
raise ClassiqValueError(
|
218
|
+
f"Array is too {adjective}. It has {array_len} item{s(array_len)}, but its "
|
219
|
+
f"quantum index has {index_size} bit{s(index_size)}"
|
220
|
+
)
|
221
|
+
|
222
|
+
|
195
223
|
def eval_quantum_subscript(
|
196
224
|
expr_val: QmodAnnotatedExpression, node: ast.Subscript, machine_precision: int
|
197
225
|
) -> None:
|
198
226
|
subject = node.value
|
199
227
|
subscript = node.slice
|
200
228
|
|
201
|
-
index_type = cast(QuantumType, expr_val.get_type(subscript))
|
202
|
-
if not qnum_is_qint(index_type):
|
203
|
-
raise ClassiqExpansionError("Quantum indices must be unsigned quantum numerics")
|
204
229
|
subject_type = expr_val.get_type(subject)
|
205
230
|
if not isinstance(subject_type, (ClassicalArray, ClassicalTuple)) or not all(
|
206
231
|
isinstance(element_type, (Integer, Real))
|
@@ -210,16 +235,18 @@ def eval_quantum_subscript(
|
|
210
235
|
"Only classical numeric arrays may have quantum subscripts"
|
211
236
|
)
|
212
237
|
|
238
|
+
index_type = cast(QuantumType, expr_val.get_type(subscript))
|
239
|
+
if not isinstance(index_type, QuantumScalar):
|
240
|
+
raise ClassiqExpansionError("Quantum index must be an unsigned integer")
|
241
|
+
validate_quantum_subscript_index_properties(
|
242
|
+
*get_numeric_properties(index_type), array_len(subject_type)
|
243
|
+
)
|
244
|
+
|
213
245
|
expr_val.set_quantum_subscript(node, subject, subscript)
|
214
246
|
if not expr_val.has_value(subject):
|
215
247
|
expr_val.set_type(node, QuantumNumeric())
|
216
248
|
return
|
217
249
|
|
218
250
|
items = expr_val.get_value(subject)
|
219
|
-
|
220
|
-
|
221
|
-
]
|
222
|
-
unified_numeric_type = register_info_to_quantum_type(
|
223
|
-
aggregate_numeric_types(numeric_types)
|
224
|
-
)
|
225
|
-
expr_val.set_type(node, unified_numeric_type)
|
251
|
+
result_attrs = compute_result_attrs_quantum_subscript(items, machine_precision)
|
252
|
+
expr_val.set_type(node, result_attrs.to_quantum_numeric())
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import ast
|
2
|
-
from typing import Any
|
2
|
+
from typing import TYPE_CHECKING, Any
|
3
3
|
|
4
4
|
from classiq.interface.exceptions import (
|
5
5
|
ClassiqExpansionError,
|
@@ -7,10 +7,23 @@ from classiq.interface.exceptions import (
|
|
7
7
|
)
|
8
8
|
from classiq.interface.generator.functions.classical_type import Integer, Real
|
9
9
|
from classiq.interface.generator.functions.type_name import TypeName
|
10
|
+
from classiq.interface.model.quantum_type import (
|
11
|
+
QuantumNumeric,
|
12
|
+
QuantumScalar,
|
13
|
+
)
|
10
14
|
|
11
15
|
from classiq.evaluators.qmod_annotated_expression import QmodAnnotatedExpression
|
12
16
|
from classiq.evaluators.qmod_node_evaluators.bool_op_evaluation import is_bool_type
|
13
|
-
from classiq.evaluators.qmod_node_evaluators.utils import
|
17
|
+
from classiq.evaluators.qmod_node_evaluators.utils import (
|
18
|
+
QmodType,
|
19
|
+
is_classical_type,
|
20
|
+
is_numeric_type,
|
21
|
+
)
|
22
|
+
from classiq.model_expansions.arithmetic import NumericAttributes
|
23
|
+
from classiq.model_expansions.arithmetic_compute_result_attrs import (
|
24
|
+
compute_result_attrs_bitwise_invert,
|
25
|
+
compute_result_attrs_negate,
|
26
|
+
)
|
14
27
|
|
15
28
|
|
16
29
|
def unary_op_allowed(op: ast.AST, operand_type: QmodType) -> bool:
|
@@ -19,7 +32,42 @@ def unary_op_allowed(op: ast.AST, operand_type: QmodType) -> bool:
|
|
19
32
|
return is_numeric_type(operand_type)
|
20
33
|
|
21
34
|
|
22
|
-
def
|
35
|
+
def _infer_unary_op_type(
|
36
|
+
op: ast.AST, operand_type: QmodType, machine_precision: int
|
37
|
+
) -> QmodType:
|
38
|
+
if is_classical_type(operand_type):
|
39
|
+
if isinstance(operand_type, TypeName):
|
40
|
+
# The operand is enum, return as integer
|
41
|
+
return Integer()
|
42
|
+
return operand_type
|
43
|
+
|
44
|
+
if TYPE_CHECKING:
|
45
|
+
assert isinstance(operand_type, QuantumScalar)
|
46
|
+
|
47
|
+
if isinstance(op, (ast.Not, ast.UAdd)):
|
48
|
+
return operand_type
|
49
|
+
|
50
|
+
if not operand_type.is_evaluated:
|
51
|
+
return QuantumNumeric()
|
52
|
+
|
53
|
+
operand_attrs = NumericAttributes.from_quantum_scalar(
|
54
|
+
operand_type, machine_precision
|
55
|
+
)
|
56
|
+
if isinstance(op, ast.Invert):
|
57
|
+
result_attrs = compute_result_attrs_bitwise_invert(
|
58
|
+
operand_attrs, machine_precision
|
59
|
+
)
|
60
|
+
elif isinstance(op, ast.USub):
|
61
|
+
result_attrs = compute_result_attrs_negate(operand_attrs, machine_precision)
|
62
|
+
else:
|
63
|
+
raise ClassiqInternalExpansionError
|
64
|
+
|
65
|
+
return result_attrs.to_quantum_numeric()
|
66
|
+
|
67
|
+
|
68
|
+
def eval_unary_op(
|
69
|
+
expr_val: QmodAnnotatedExpression, node: ast.UnaryOp, machine_precision: int
|
70
|
+
) -> None:
|
23
71
|
operand = node.operand
|
24
72
|
op = node.op
|
25
73
|
|
@@ -34,12 +82,8 @@ def eval_unary_op(expr_val: QmodAnnotatedExpression, node: ast.UnaryOp) -> None:
|
|
34
82
|
raise ClassiqExpansionError(
|
35
83
|
f"Operation {type(op).__name__!r} on a real value is not supported"
|
36
84
|
)
|
37
|
-
|
38
|
-
|
39
|
-
op_type = Integer()
|
40
|
-
else:
|
41
|
-
op_type = operand_type
|
42
|
-
expr_val.set_type(node, op_type)
|
85
|
+
|
86
|
+
expr_val.set_type(node, _infer_unary_op_type(op, operand_type, machine_precision))
|
43
87
|
|
44
88
|
if not expr_val.has_value(operand):
|
45
89
|
return
|
@@ -1,7 +1,8 @@
|
|
1
|
-
from typing import
|
1
|
+
from typing import Optional, Union, cast
|
2
2
|
|
3
3
|
import sympy
|
4
4
|
|
5
|
+
from classiq.interface.exceptions import ClassiqInternalExpansionError
|
5
6
|
from classiq.interface.generator.functions.classical_type import (
|
6
7
|
ClassicalArray,
|
7
8
|
ClassicalTuple,
|
@@ -24,10 +25,6 @@ NumberValueType = Union[IntegerValueType, RealValueType]
|
|
24
25
|
SYMPY_SYMBOLS = {sym: getattr(sympy, sym) for sym in sympy.__all__}
|
25
26
|
|
26
27
|
|
27
|
-
def get_qmod_type_name(type_: Any) -> str:
|
28
|
-
return getattr(type_, "type_name", type(type_).__name__)
|
29
|
-
|
30
|
-
|
31
28
|
def is_classical_type(qmod_type: QmodType) -> bool:
|
32
29
|
if isinstance(qmod_type, TypeName):
|
33
30
|
return qmod_type.has_classical_struct_decl or qmod_type.is_enum
|
@@ -42,6 +39,31 @@ def qnum_is_qbit(qmod_type: QuantumNumeric) -> bool:
|
|
42
39
|
)
|
43
40
|
|
44
41
|
|
42
|
+
def get_numeric_properties(
|
43
|
+
qmod_type: QuantumScalar,
|
44
|
+
) -> tuple[Optional[int], Optional[bool], Optional[int]]:
|
45
|
+
if isinstance(qmod_type, QuantumBit):
|
46
|
+
return 1, False, 0
|
47
|
+
if not isinstance(qmod_type, QuantumNumeric):
|
48
|
+
raise ClassiqInternalExpansionError
|
49
|
+
size = qmod_type.size_in_bits if qmod_type.has_size_in_bits else None
|
50
|
+
is_signed_expr = qmod_type.is_signed
|
51
|
+
if is_signed_expr is None:
|
52
|
+
is_signed = False
|
53
|
+
elif is_signed_expr.is_evaluated() and is_signed_expr.is_constant():
|
54
|
+
is_signed = is_signed_expr.to_bool_value()
|
55
|
+
else:
|
56
|
+
is_signed = None
|
57
|
+
fraction_digits_expr = qmod_type.fraction_digits
|
58
|
+
if fraction_digits_expr is None:
|
59
|
+
fraction_digits = 0
|
60
|
+
elif fraction_digits_expr.is_evaluated() and fraction_digits_expr.is_constant():
|
61
|
+
fraction_digits = fraction_digits_expr.to_int_value()
|
62
|
+
else:
|
63
|
+
fraction_digits = None
|
64
|
+
return size, is_signed, fraction_digits
|
65
|
+
|
66
|
+
|
45
67
|
def qnum_is_qint(qmod_type: QuantumType) -> bool:
|
46
68
|
return isinstance(qmod_type, QuantumBit) or (
|
47
69
|
isinstance(qmod_type, QuantumNumeric)
|