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,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)
|
|
@@ -63,7 +85,7 @@ def array_len(
|
|
|
63
85
|
) -> Optional[int]:
|
|
64
86
|
if isinstance(classical_type, ClassicalTuple):
|
|
65
87
|
return len(classical_type.element_types)
|
|
66
|
-
if classical_type.
|
|
88
|
+
if classical_type.has_constant_length:
|
|
67
89
|
return classical_type.length_value
|
|
68
90
|
return None
|
|
69
91
|
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
from enum import IntEnum
|
|
2
|
+
from typing import Any, Optional, Union
|
|
3
|
+
|
|
4
|
+
import sympy
|
|
5
|
+
|
|
6
|
+
from classiq.interface.exceptions import ClassiqInternalExpansionError
|
|
7
|
+
from classiq.interface.generator.expressions.expression import Expression
|
|
8
|
+
from classiq.interface.generator.expressions.proxies.classical.qmod_struct_instance import (
|
|
9
|
+
QmodStructInstance,
|
|
10
|
+
)
|
|
11
|
+
from classiq.interface.generator.functions.classical_type import (
|
|
12
|
+
Bool,
|
|
13
|
+
ClassicalArray,
|
|
14
|
+
ClassicalTuple,
|
|
15
|
+
ClassicalType,
|
|
16
|
+
Integer,
|
|
17
|
+
Real,
|
|
18
|
+
)
|
|
19
|
+
from classiq.interface.generator.functions.type_name import Enum, Struct, TypeName
|
|
20
|
+
from classiq.interface.generator.types.struct_declaration import StructDeclaration
|
|
21
|
+
from classiq.interface.helpers.backward_compatibility import zip_strict
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _copy_generative_flag(
|
|
25
|
+
from_type: ClassicalType, to_type: Optional[ClassicalType]
|
|
26
|
+
) -> Optional[ClassicalType]:
|
|
27
|
+
if to_type is None:
|
|
28
|
+
return None
|
|
29
|
+
if from_type.is_generative:
|
|
30
|
+
return to_type.set_generative()
|
|
31
|
+
return to_type
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def infer_classical_type(value: Any) -> ClassicalType:
|
|
35
|
+
if isinstance(value, IntEnum):
|
|
36
|
+
return Enum(name=type(value).__name__)
|
|
37
|
+
if isinstance(value, bool):
|
|
38
|
+
return Bool()
|
|
39
|
+
if isinstance(value, int):
|
|
40
|
+
return Integer()
|
|
41
|
+
if isinstance(value, (float, complex)):
|
|
42
|
+
return Real()
|
|
43
|
+
if isinstance(value, list):
|
|
44
|
+
return ClassicalTuple(
|
|
45
|
+
element_types=[infer_classical_type(item) for item in value]
|
|
46
|
+
)
|
|
47
|
+
if isinstance(value, QmodStructInstance):
|
|
48
|
+
struct_name = value.struct_declaration.name
|
|
49
|
+
classical_type = Struct(name=struct_name)
|
|
50
|
+
classical_type.set_classical_struct_decl(
|
|
51
|
+
StructDeclaration(
|
|
52
|
+
name=struct_name,
|
|
53
|
+
variables={
|
|
54
|
+
field_name: infer_classical_type(field_value)
|
|
55
|
+
for field_name, field_value in value.fields.items()
|
|
56
|
+
},
|
|
57
|
+
)
|
|
58
|
+
)
|
|
59
|
+
return classical_type
|
|
60
|
+
# FIXME: Remove sympy compatibility (CLS-3214)
|
|
61
|
+
if (
|
|
62
|
+
isinstance(value, sympy.Basic)
|
|
63
|
+
and len(value.free_symbols) == 0
|
|
64
|
+
and value.is_complex
|
|
65
|
+
):
|
|
66
|
+
return Real()
|
|
67
|
+
raise ClassiqInternalExpansionError
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _inject_classical_array_attributes(
|
|
71
|
+
from_type: ClassicalType, to_type: Union[ClassicalArray, ClassicalTuple]
|
|
72
|
+
) -> Optional[ClassicalType]:
|
|
73
|
+
if isinstance(to_type, ClassicalArray):
|
|
74
|
+
if isinstance(from_type, ClassicalArray):
|
|
75
|
+
length: Optional[Expression]
|
|
76
|
+
if from_type.has_constant_length:
|
|
77
|
+
if (
|
|
78
|
+
to_type.has_constant_length
|
|
79
|
+
and from_type.length_value != to_type.length_value
|
|
80
|
+
):
|
|
81
|
+
return None
|
|
82
|
+
length = from_type.length
|
|
83
|
+
else:
|
|
84
|
+
length = to_type.length
|
|
85
|
+
element_type = inject_classical_type_attributes(
|
|
86
|
+
from_type.element_type, to_type.element_type
|
|
87
|
+
)
|
|
88
|
+
if element_type is None:
|
|
89
|
+
return None
|
|
90
|
+
return ClassicalArray(element_type=element_type, length=length)
|
|
91
|
+
if isinstance(from_type, ClassicalTuple):
|
|
92
|
+
if (
|
|
93
|
+
to_type.has_constant_length
|
|
94
|
+
and len(from_type.element_types) != to_type.length_value
|
|
95
|
+
):
|
|
96
|
+
return None
|
|
97
|
+
element_types = [
|
|
98
|
+
inject_classical_type_attributes(element_type, to_type.element_type)
|
|
99
|
+
for element_type in from_type.element_types
|
|
100
|
+
]
|
|
101
|
+
if None in element_types:
|
|
102
|
+
return None
|
|
103
|
+
return ClassicalTuple(element_types=element_types)
|
|
104
|
+
return None
|
|
105
|
+
if isinstance(from_type, ClassicalArray):
|
|
106
|
+
if from_type.has_constant_length and from_type.length_value != len(
|
|
107
|
+
to_type.element_types
|
|
108
|
+
):
|
|
109
|
+
return None
|
|
110
|
+
element_types = [
|
|
111
|
+
inject_classical_type_attributes(from_type.element_type, element_type)
|
|
112
|
+
for element_type in to_type.element_types
|
|
113
|
+
]
|
|
114
|
+
if None in element_types:
|
|
115
|
+
return None
|
|
116
|
+
return ClassicalTuple(element_types=element_types)
|
|
117
|
+
if isinstance(from_type, ClassicalTuple):
|
|
118
|
+
if len(from_type.element_types) != len(to_type.element_types):
|
|
119
|
+
return None
|
|
120
|
+
element_types = [
|
|
121
|
+
inject_classical_type_attributes(from_element_type, to_element_type)
|
|
122
|
+
for from_element_type, to_element_type in zip_strict(
|
|
123
|
+
from_type.element_types, to_type.element_types, strict=True
|
|
124
|
+
)
|
|
125
|
+
]
|
|
126
|
+
if None in element_types:
|
|
127
|
+
return None
|
|
128
|
+
return ClassicalTuple(element_types=element_types)
|
|
129
|
+
return None
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _inject_classical_type_name_attributes(
|
|
133
|
+
from_type: ClassicalType, to_type: TypeName
|
|
134
|
+
) -> Optional[ClassicalType]:
|
|
135
|
+
if to_type.is_enum:
|
|
136
|
+
if isinstance(from_type, Integer) or (
|
|
137
|
+
isinstance(from_type, TypeName) and from_type.name == to_type.name
|
|
138
|
+
):
|
|
139
|
+
return Enum(name=to_type.name)
|
|
140
|
+
return None
|
|
141
|
+
if to_type.has_classical_struct_decl:
|
|
142
|
+
if not isinstance(from_type, TypeName) or from_type.name != to_type.name:
|
|
143
|
+
return None
|
|
144
|
+
classical_type = Struct(name=to_type.name)
|
|
145
|
+
field_types = {
|
|
146
|
+
field_name: inject_classical_type_attributes(
|
|
147
|
+
from_type.classical_struct_decl.variables[field_name],
|
|
148
|
+
to_type.classical_struct_decl.variables[field_name],
|
|
149
|
+
)
|
|
150
|
+
for field_name in to_type.classical_struct_decl.variables
|
|
151
|
+
}
|
|
152
|
+
if None in field_types.values():
|
|
153
|
+
return None
|
|
154
|
+
classical_type.set_classical_struct_decl(
|
|
155
|
+
StructDeclaration(name=to_type.name, variables=field_types)
|
|
156
|
+
)
|
|
157
|
+
return classical_type
|
|
158
|
+
return None
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def inject_classical_type_attributes(
|
|
162
|
+
from_type: ClassicalType, to_type: ClassicalType
|
|
163
|
+
) -> Optional[ClassicalType]:
|
|
164
|
+
if isinstance(to_type, Bool):
|
|
165
|
+
if isinstance(from_type, Bool):
|
|
166
|
+
return _copy_generative_flag(to_type, Bool())
|
|
167
|
+
return None
|
|
168
|
+
if isinstance(to_type, Integer):
|
|
169
|
+
if isinstance(from_type, (Integer, Real)) or (
|
|
170
|
+
isinstance(from_type, TypeName) and from_type.is_enum
|
|
171
|
+
):
|
|
172
|
+
return _copy_generative_flag(to_type, Integer())
|
|
173
|
+
return None
|
|
174
|
+
if isinstance(to_type, Real):
|
|
175
|
+
if isinstance(from_type, (Integer, Real)) or (
|
|
176
|
+
isinstance(from_type, TypeName) and from_type.is_enum
|
|
177
|
+
):
|
|
178
|
+
return _copy_generative_flag(to_type, Real())
|
|
179
|
+
return None
|
|
180
|
+
if isinstance(to_type, (ClassicalArray, ClassicalTuple)):
|
|
181
|
+
return _copy_generative_flag(
|
|
182
|
+
to_type, _inject_classical_array_attributes(from_type, to_type)
|
|
183
|
+
)
|
|
184
|
+
if isinstance(to_type, TypeName):
|
|
185
|
+
return _copy_generative_flag(
|
|
186
|
+
to_type, _inject_classical_type_name_attributes(from_type, to_type)
|
|
187
|
+
)
|
|
188
|
+
return None
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING, Optional
|
|
2
|
+
|
|
3
|
+
from classiq.interface.exceptions import (
|
|
4
|
+
ClassiqExpansionError,
|
|
5
|
+
ClassiqInternalExpansionError,
|
|
6
|
+
)
|
|
7
|
+
from classiq.interface.generator.expressions.expression import Expression
|
|
8
|
+
from classiq.interface.generator.functions.type_name import TypeName
|
|
9
|
+
from classiq.interface.model.quantum_type import (
|
|
10
|
+
QuantumBit,
|
|
11
|
+
QuantumBitvector,
|
|
12
|
+
QuantumNumeric,
|
|
13
|
+
QuantumType,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
from classiq.evaluators.quantum_type_utils import set_bounds
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _normalized_qnum(quantum_type: QuantumType) -> QuantumType:
|
|
20
|
+
if not isinstance(quantum_type, QuantumNumeric):
|
|
21
|
+
return quantum_type
|
|
22
|
+
normalized_qnum = QuantumNumeric(
|
|
23
|
+
size=quantum_type.size,
|
|
24
|
+
is_signed=(
|
|
25
|
+
quantum_type.is_signed
|
|
26
|
+
if quantum_type.is_signed is not None
|
|
27
|
+
else Expression(expr="False")
|
|
28
|
+
),
|
|
29
|
+
fraction_digits=(
|
|
30
|
+
quantum_type.fraction_digits
|
|
31
|
+
if quantum_type.fraction_digits is not None
|
|
32
|
+
else Expression(expr="0")
|
|
33
|
+
),
|
|
34
|
+
)
|
|
35
|
+
normalized_qnum.set_bounds(quantum_type.get_bounds())
|
|
36
|
+
return normalized_qnum
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _same_shape(quantum_type_1: QuantumType, quantum_type_2: QuantumType) -> bool:
|
|
40
|
+
if isinstance(quantum_type_1, QuantumBit) and isinstance(
|
|
41
|
+
quantum_type_2, QuantumBit
|
|
42
|
+
):
|
|
43
|
+
return True
|
|
44
|
+
if (
|
|
45
|
+
isinstance(quantum_type_1, QuantumNumeric)
|
|
46
|
+
and isinstance(quantum_type_2, QuantumNumeric)
|
|
47
|
+
and (
|
|
48
|
+
not quantum_type_1.has_size_in_bits
|
|
49
|
+
or not quantum_type_2.has_size_in_bits
|
|
50
|
+
or quantum_type_1.size_in_bits == quantum_type_2.size_in_bits
|
|
51
|
+
)
|
|
52
|
+
):
|
|
53
|
+
return True
|
|
54
|
+
if (
|
|
55
|
+
isinstance(quantum_type_1, QuantumBitvector)
|
|
56
|
+
and isinstance(quantum_type_2, QuantumBitvector)
|
|
57
|
+
and (
|
|
58
|
+
not quantum_type_1.has_constant_length
|
|
59
|
+
or not quantum_type_2.has_constant_length
|
|
60
|
+
or quantum_type_1.length_value == quantum_type_2.length_value
|
|
61
|
+
)
|
|
62
|
+
and _same_shape(quantum_type_1.element_type, quantum_type_2.element_type)
|
|
63
|
+
):
|
|
64
|
+
return True
|
|
65
|
+
return (
|
|
66
|
+
isinstance(quantum_type_1, TypeName)
|
|
67
|
+
and quantum_type_1.has_fields
|
|
68
|
+
and isinstance(quantum_type_2, TypeName)
|
|
69
|
+
and quantum_type_2.has_fields
|
|
70
|
+
and quantum_type_1.name == quantum_type_2.name
|
|
71
|
+
and all(
|
|
72
|
+
_same_shape(
|
|
73
|
+
quantum_type_1.fields[field_name], quantum_type_2.fields[field_name]
|
|
74
|
+
)
|
|
75
|
+
for field_name in quantum_type_1.fields
|
|
76
|
+
)
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _inject_qnum_type_attributes(
|
|
81
|
+
from_type: QuantumType, to_type: QuantumNumeric
|
|
82
|
+
) -> QuantumNumeric:
|
|
83
|
+
size: Optional[Expression]
|
|
84
|
+
if from_type.has_size_in_bits:
|
|
85
|
+
size = Expression(expr=str(from_type.size_in_bits))
|
|
86
|
+
else:
|
|
87
|
+
size = to_type.size
|
|
88
|
+
if to_type.is_signed is not None:
|
|
89
|
+
if to_type.is_signed.is_evaluated():
|
|
90
|
+
is_signed = Expression(expr=str(to_type.sign_value))
|
|
91
|
+
else:
|
|
92
|
+
is_signed = Expression(expr="False")
|
|
93
|
+
else:
|
|
94
|
+
is_signed = None
|
|
95
|
+
if to_type.fraction_digits is not None:
|
|
96
|
+
if to_type.fraction_digits.is_evaluated():
|
|
97
|
+
fraction_digits = Expression(expr=str(to_type.fraction_digits_value))
|
|
98
|
+
else:
|
|
99
|
+
fraction_digits = Expression(expr="0")
|
|
100
|
+
else:
|
|
101
|
+
fraction_digits = None
|
|
102
|
+
if isinstance(from_type, QuantumNumeric) and not to_type.has_size_in_bits:
|
|
103
|
+
if (
|
|
104
|
+
is_signed is None
|
|
105
|
+
and from_type.is_signed is not None
|
|
106
|
+
and from_type.is_signed.is_evaluated()
|
|
107
|
+
):
|
|
108
|
+
is_signed = from_type.is_signed
|
|
109
|
+
if (
|
|
110
|
+
fraction_digits is None
|
|
111
|
+
and from_type.fraction_digits is not None
|
|
112
|
+
and from_type.fraction_digits.is_evaluated()
|
|
113
|
+
):
|
|
114
|
+
fraction_digits = from_type.fraction_digits
|
|
115
|
+
if size is not None and is_signed is None:
|
|
116
|
+
is_signed = Expression(expr="False")
|
|
117
|
+
if size is not None and fraction_digits is None:
|
|
118
|
+
fraction_digits = Expression(expr="0")
|
|
119
|
+
updated_type = QuantumNumeric(
|
|
120
|
+
size=size, is_signed=is_signed, fraction_digits=fraction_digits
|
|
121
|
+
)
|
|
122
|
+
updated_type.set_bounds(to_type.get_bounds())
|
|
123
|
+
set_bounds(_normalized_qnum(from_type), updated_type)
|
|
124
|
+
return updated_type
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def _inject_qarray_type_attributes(
|
|
128
|
+
from_type: QuantumType, to_type: QuantumBitvector
|
|
129
|
+
) -> Optional[QuantumBitvector]:
|
|
130
|
+
if _same_shape(from_type, to_type):
|
|
131
|
+
if TYPE_CHECKING:
|
|
132
|
+
assert isinstance(from_type, QuantumBitvector)
|
|
133
|
+
if to_type.has_length:
|
|
134
|
+
length = to_type.length
|
|
135
|
+
elif from_type.has_length:
|
|
136
|
+
length = from_type.length
|
|
137
|
+
elif from_type.has_size_in_bits:
|
|
138
|
+
raise ClassiqExpansionError(
|
|
139
|
+
f"Could not infer the length attribute of type {to_type.qmod_type_name}"
|
|
140
|
+
)
|
|
141
|
+
else:
|
|
142
|
+
length = None
|
|
143
|
+
element_type = inject_quantum_type_attributes(
|
|
144
|
+
from_type.element_type, to_type.element_type
|
|
145
|
+
)
|
|
146
|
+
else:
|
|
147
|
+
if to_type.has_constant_length:
|
|
148
|
+
if (
|
|
149
|
+
from_type.has_size_in_bits
|
|
150
|
+
and from_type.size_in_bits % to_type.length_value != 0
|
|
151
|
+
):
|
|
152
|
+
return None
|
|
153
|
+
length = to_type.length
|
|
154
|
+
elif from_type.has_size_in_bits:
|
|
155
|
+
if not to_type.element_type.has_size_in_bits:
|
|
156
|
+
raise ClassiqExpansionError(
|
|
157
|
+
f"Could not infer the length attribute of type "
|
|
158
|
+
f"{to_type.qmod_type_name}"
|
|
159
|
+
)
|
|
160
|
+
if from_type.size_in_bits % to_type.element_type.size_in_bits != 0:
|
|
161
|
+
return None
|
|
162
|
+
length = Expression(
|
|
163
|
+
expr=str(from_type.size_in_bits // to_type.element_type.size_in_bits)
|
|
164
|
+
)
|
|
165
|
+
else:
|
|
166
|
+
length = None
|
|
167
|
+
if length is not None and from_type.has_size_in_bits:
|
|
168
|
+
element_type = inject_quantum_type_attributes(
|
|
169
|
+
QuantumBitvector(
|
|
170
|
+
length=Expression(
|
|
171
|
+
expr=str(from_type.size_in_bits // length.to_int_value())
|
|
172
|
+
)
|
|
173
|
+
),
|
|
174
|
+
to_type.element_type,
|
|
175
|
+
)
|
|
176
|
+
else:
|
|
177
|
+
element_type = inject_quantum_type_attributes(
|
|
178
|
+
QuantumBitvector(), to_type.element_type
|
|
179
|
+
)
|
|
180
|
+
return QuantumBitvector(element_type=element_type, length=length)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def _inject_qstruct_type_attributes(
|
|
184
|
+
from_type: QuantumType, to_type: TypeName
|
|
185
|
+
) -> Optional[TypeName]:
|
|
186
|
+
if isinstance(from_type, TypeName) and from_type.name == to_type.name:
|
|
187
|
+
fields = {
|
|
188
|
+
field_name: inject_quantum_type_attributes(
|
|
189
|
+
from_type.fields[field_name], to_type.fields[field_name]
|
|
190
|
+
)
|
|
191
|
+
for field_name in to_type.fields
|
|
192
|
+
}
|
|
193
|
+
if None in fields.values():
|
|
194
|
+
return None
|
|
195
|
+
elif not from_type.has_size_in_bits:
|
|
196
|
+
fields = {
|
|
197
|
+
field_name: inject_quantum_type_attributes(QuantumBitvector(), field_type)
|
|
198
|
+
for field_name, field_type in to_type.fields.items()
|
|
199
|
+
}
|
|
200
|
+
else:
|
|
201
|
+
initialized_fields = {
|
|
202
|
+
field_name: field_type.has_size_in_bits
|
|
203
|
+
for field_name, field_type in to_type.fields.items()
|
|
204
|
+
}
|
|
205
|
+
if sum(initialized_fields.values()) == len(initialized_fields) - 1:
|
|
206
|
+
flexible_field_name = [
|
|
207
|
+
field_name
|
|
208
|
+
for field_name, is_initialized in initialized_fields.items()
|
|
209
|
+
if not is_initialized
|
|
210
|
+
][0]
|
|
211
|
+
flexible_field_size = from_type.size_in_bits - sum(
|
|
212
|
+
field_type.size_in_bits
|
|
213
|
+
for field_name, field_type in to_type.fields.items()
|
|
214
|
+
if initialized_fields[field_name]
|
|
215
|
+
)
|
|
216
|
+
flexible_field_type = inject_quantum_type_attributes(
|
|
217
|
+
QuantumBitvector(length=Expression(expr=str(flexible_field_size))),
|
|
218
|
+
to_type.fields[flexible_field_name],
|
|
219
|
+
)
|
|
220
|
+
if flexible_field_type is None:
|
|
221
|
+
return None
|
|
222
|
+
fields = {
|
|
223
|
+
field_name: (
|
|
224
|
+
flexible_field_type
|
|
225
|
+
if field_name == flexible_field_name
|
|
226
|
+
else inject_quantum_type_attributes(QuantumBitvector(), field_type)
|
|
227
|
+
)
|
|
228
|
+
for field_name, field_type in to_type.fields.items()
|
|
229
|
+
}
|
|
230
|
+
else:
|
|
231
|
+
fields = {
|
|
232
|
+
field_name: inject_quantum_type_attributes(
|
|
233
|
+
QuantumBitvector(), field_type
|
|
234
|
+
)
|
|
235
|
+
for field_name, field_type in to_type.fields.items()
|
|
236
|
+
}
|
|
237
|
+
classical_type = TypeName(name=to_type.name)
|
|
238
|
+
classical_type.set_fields(fields) # type:ignore[arg-type]
|
|
239
|
+
return classical_type
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def inject_quantum_type_attributes(
|
|
243
|
+
from_type: QuantumType, to_type: QuantumType
|
|
244
|
+
) -> Optional[QuantumType]:
|
|
245
|
+
for qmod_type in (from_type, to_type):
|
|
246
|
+
if isinstance(qmod_type, TypeName) and not qmod_type.has_fields:
|
|
247
|
+
raise ClassiqInternalExpansionError
|
|
248
|
+
if to_type.has_size_in_bits and (
|
|
249
|
+
(from_type.has_size_in_bits and to_type.size_in_bits != from_type.size_in_bits)
|
|
250
|
+
or (from_type.minimal_size_in_bits > to_type.size_in_bits)
|
|
251
|
+
):
|
|
252
|
+
return None
|
|
253
|
+
if isinstance(to_type, QuantumBit):
|
|
254
|
+
return QuantumBit()
|
|
255
|
+
if isinstance(to_type, QuantumNumeric):
|
|
256
|
+
return _inject_qnum_type_attributes(from_type, to_type)
|
|
257
|
+
if isinstance(to_type, QuantumBitvector):
|
|
258
|
+
return _inject_qarray_type_attributes(from_type, to_type)
|
|
259
|
+
if isinstance(to_type, TypeName):
|
|
260
|
+
return _inject_qstruct_type_attributes(from_type, to_type)
|
|
261
|
+
raise ClassiqInternalExpansionError
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def inject_quantum_type_attributes_inplace(
|
|
265
|
+
from_type: QuantumType, to_type: QuantumType
|
|
266
|
+
) -> bool:
|
|
267
|
+
updated_type = inject_quantum_type_attributes(from_type, to_type)
|
|
268
|
+
if updated_type is None:
|
|
269
|
+
return False
|
|
270
|
+
if isinstance(to_type, QuantumBit) and isinstance(updated_type, QuantumBit):
|
|
271
|
+
return True
|
|
272
|
+
if isinstance(to_type, QuantumNumeric) and isinstance(updated_type, QuantumNumeric):
|
|
273
|
+
to_type.size = updated_type.size
|
|
274
|
+
to_type.is_signed = updated_type.is_signed
|
|
275
|
+
to_type.fraction_digits = updated_type.fraction_digits
|
|
276
|
+
to_type.set_bounds(updated_type.get_bounds())
|
|
277
|
+
return True
|
|
278
|
+
if isinstance(to_type, QuantumBitvector) and isinstance(
|
|
279
|
+
updated_type, QuantumBitvector
|
|
280
|
+
):
|
|
281
|
+
to_type.length = updated_type.length
|
|
282
|
+
to_type.element_type = updated_type.element_type
|
|
283
|
+
return True
|
|
284
|
+
if (
|
|
285
|
+
isinstance(to_type, TypeName)
|
|
286
|
+
and to_type.has_fields
|
|
287
|
+
and isinstance(updated_type, TypeName)
|
|
288
|
+
and updated_type.has_fields
|
|
289
|
+
):
|
|
290
|
+
to_type.set_fields(updated_type.fields)
|
|
291
|
+
return True
|
|
292
|
+
raise ClassiqInternalExpansionError
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def validate_quantum_type_attributes(quantum_type: QuantumType) -> None:
|
|
296
|
+
if isinstance(quantum_type, TypeName):
|
|
297
|
+
if len(quantum_type.fields) == 0:
|
|
298
|
+
raise ClassiqExpansionError(
|
|
299
|
+
f"QStruct {quantum_type.name} must have at least one field"
|
|
300
|
+
)
|
|
301
|
+
for field_type in quantum_type.fields.values():
|
|
302
|
+
validate_quantum_type_attributes(field_type)
|
|
303
|
+
return
|
|
304
|
+
if isinstance(quantum_type, QuantumBitvector):
|
|
305
|
+
validate_quantum_type_attributes(quantum_type.element_type)
|
|
306
|
+
if quantum_type.has_constant_length and quantum_type.length_value < 1:
|
|
307
|
+
raise ClassiqExpansionError(
|
|
308
|
+
f"QArray length must be positive, got {quantum_type.length_value}"
|
|
309
|
+
)
|
|
310
|
+
return
|
|
311
|
+
if isinstance(quantum_type, QuantumNumeric):
|
|
312
|
+
if quantum_type.has_size_in_bits and quantum_type.size_in_bits < 1:
|
|
313
|
+
raise ClassiqExpansionError(
|
|
314
|
+
f"QNum size must be positive, got {quantum_type.size_in_bits}"
|
|
315
|
+
)
|
|
316
|
+
if quantum_type.has_fraction_digits:
|
|
317
|
+
if quantum_type.fraction_digits_value < 0:
|
|
318
|
+
raise ClassiqExpansionError(
|
|
319
|
+
f"QNum fraction digits must be positive, got "
|
|
320
|
+
f"{quantum_type.fraction_digits_value}"
|
|
321
|
+
)
|
|
322
|
+
if (
|
|
323
|
+
quantum_type.has_size_in_bits
|
|
324
|
+
and quantum_type.fraction_digits_value > quantum_type.size_in_bits
|
|
325
|
+
):
|
|
326
|
+
raise ClassiqExpansionError(
|
|
327
|
+
f"QNum size ({quantum_type.size_in_bits}) must be greater or "
|
|
328
|
+
f"equals than the fraction digits "
|
|
329
|
+
f"({quantum_type.fraction_digits_value})"
|
|
330
|
+
)
|