classiq 0.84.0__py3-none-any.whl → 0.86.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/applications/combinatorial_optimization/combinatorial_problem.py +24 -45
- classiq/evaluators/classical_expression.py +32 -15
- classiq/evaluators/qmod_annotated_expression.py +207 -0
- classiq/evaluators/qmod_expression_visitors/__init__.py +0 -0
- classiq/evaluators/qmod_expression_visitors/qmod_expression_bwc.py +134 -0
- classiq/evaluators/qmod_expression_visitors/qmod_expression_evaluator.py +232 -0
- classiq/evaluators/qmod_expression_visitors/qmod_expression_renamer.py +44 -0
- classiq/evaluators/qmod_expression_visitors/qmod_expression_simplifier.py +308 -0
- classiq/evaluators/qmod_node_evaluators/__init__.py +0 -0
- classiq/evaluators/qmod_node_evaluators/attribute_evaluation.py +112 -0
- classiq/evaluators/qmod_node_evaluators/binary_op_evaluation.py +132 -0
- classiq/evaluators/qmod_node_evaluators/bool_op_evaluation.py +70 -0
- classiq/evaluators/qmod_node_evaluators/classical_function_evaluation.py +311 -0
- classiq/evaluators/qmod_node_evaluators/compare_evaluation.py +107 -0
- classiq/evaluators/qmod_node_evaluators/constant_evaluation.py +67 -0
- classiq/evaluators/qmod_node_evaluators/list_evaluation.py +107 -0
- classiq/evaluators/qmod_node_evaluators/measurement_evaluation.py +25 -0
- classiq/evaluators/qmod_node_evaluators/name_evaluation.py +50 -0
- classiq/evaluators/qmod_node_evaluators/struct_instantiation_evaluation.py +66 -0
- classiq/evaluators/qmod_node_evaluators/subscript_evaluation.py +225 -0
- classiq/evaluators/qmod_node_evaluators/unary_op_evaluation.py +58 -0
- classiq/evaluators/qmod_node_evaluators/utils.py +80 -0
- classiq/execution/execution_session.py +53 -6
- classiq/interface/_version.py +1 -1
- classiq/interface/analyzer/analysis_params.py +1 -1
- classiq/interface/analyzer/result.py +1 -1
- classiq/interface/debug_info/debug_info.py +0 -4
- classiq/interface/executor/quantum_code.py +2 -2
- classiq/interface/generator/arith/arithmetic_expression_validator.py +5 -1
- classiq/interface/generator/arith/binary_ops.py +43 -51
- classiq/interface/generator/arith/number_utils.py +3 -2
- classiq/interface/generator/arith/register_user_input.py +15 -0
- classiq/interface/generator/arith/unary_ops.py +32 -28
- classiq/interface/generator/expressions/atomic_expression_functions.py +5 -0
- classiq/interface/generator/expressions/expression_types.py +2 -2
- classiq/interface/generator/expressions/proxies/classical/qmod_struct_instance.py +7 -0
- classiq/interface/generator/functions/builtins/internal_operators.py +2 -0
- classiq/interface/generator/functions/classical_function_declaration.py +0 -4
- classiq/interface/generator/functions/classical_type.py +0 -32
- classiq/interface/generator/functions/concrete_types.py +20 -0
- classiq/interface/generator/generated_circuit_data.py +7 -10
- classiq/interface/generator/quantum_program.py +6 -1
- classiq/interface/generator/synthesis_metadata/synthesis_execution_data.py +29 -0
- classiq/interface/ide/operation_registry.py +45 -0
- classiq/interface/ide/visual_model.py +84 -2
- classiq/interface/model/bounds.py +12 -2
- classiq/interface/model/quantum_expressions/arithmetic_operation.py +7 -4
- classiq/interface/model/quantum_type.py +67 -33
- classiq/interface/model/variable_declaration_statement.py +33 -6
- classiq/model_expansions/arithmetic.py +115 -0
- classiq/model_expansions/arithmetic_compute_result_attrs.py +71 -0
- classiq/model_expansions/atomic_expression_functions_defs.py +10 -6
- classiq/model_expansions/function_builder.py +4 -1
- classiq/model_expansions/generative_functions.py +15 -2
- classiq/model_expansions/interpreters/base_interpreter.py +7 -0
- classiq/model_expansions/interpreters/generative_interpreter.py +18 -1
- classiq/model_expansions/quantum_operations/assignment_result_processor.py +63 -21
- classiq/model_expansions/quantum_operations/bounds.py +7 -1
- classiq/model_expansions/quantum_operations/call_emitter.py +5 -2
- classiq/model_expansions/quantum_operations/classical_var_emitter.py +16 -0
- classiq/model_expansions/quantum_operations/variable_decleration.py +30 -10
- classiq/model_expansions/scope.py +7 -0
- classiq/model_expansions/scope_initialization.py +2 -0
- classiq/model_expansions/sympy_conversion/sympy_to_python.py +1 -1
- classiq/model_expansions/transformers/type_modifier_inference.py +5 -0
- classiq/model_expansions/transformers/var_splitter.py +1 -1
- classiq/model_expansions/visitors/boolean_expression_transformers.py +1 -1
- classiq/open_library/functions/__init__.py +0 -2
- classiq/open_library/functions/qaoa_penalty.py +8 -1
- classiq/open_library/functions/state_preparation.py +1 -32
- classiq/qmod/__init__.py +2 -0
- classiq/qmod/builtins/operations.py +66 -2
- classiq/qmod/classical_variable.py +74 -0
- classiq/qmod/declaration_inferrer.py +5 -3
- classiq/qmod/native/pretty_printer.py +18 -14
- classiq/qmod/pretty_print/pretty_printer.py +34 -15
- classiq/qmod/qfunc.py +2 -19
- classiq/qmod/qmod_variable.py +5 -8
- classiq/qmod/quantum_expandable.py +1 -1
- classiq/qmod/quantum_function.py +42 -2
- classiq/qmod/symbolic_type.py +2 -1
- classiq/qmod/write_qmod.py +3 -1
- {classiq-0.84.0.dist-info → classiq-0.86.0.dist-info}/METADATA +1 -1
- {classiq-0.84.0.dist-info → classiq-0.86.0.dist-info}/RECORD +86 -62
- classiq/interface/model/quantum_variable_declaration.py +0 -7
- /classiq/{model_expansions/sympy_conversion/arithmetics.py → evaluators/qmod_expression_visitors/sympy_wrappers.py} +0 -0
- {classiq-0.84.0.dist-info → classiq-0.86.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,107 @@
|
|
1
|
+
import ast
|
2
|
+
from typing import Optional, cast
|
3
|
+
|
4
|
+
from classiq.interface.exceptions import (
|
5
|
+
ClassiqExpansionError,
|
6
|
+
)
|
7
|
+
from classiq.interface.generator.expressions.expression import Expression
|
8
|
+
from classiq.interface.generator.functions.classical_type import (
|
9
|
+
Bool,
|
10
|
+
ClassicalArray,
|
11
|
+
ClassicalTuple,
|
12
|
+
ClassicalType,
|
13
|
+
Real,
|
14
|
+
)
|
15
|
+
from classiq.interface.generator.functions.type_name import TypeName
|
16
|
+
from classiq.interface.model.quantum_type import QuantumBitvector, QuantumType
|
17
|
+
|
18
|
+
from classiq.evaluators.qmod_annotated_expression import QmodAnnotatedExpression
|
19
|
+
from classiq.evaluators.qmod_node_evaluators.utils import (
|
20
|
+
element_types,
|
21
|
+
is_classical_integer,
|
22
|
+
is_classical_type,
|
23
|
+
)
|
24
|
+
|
25
|
+
|
26
|
+
def _list_allowed(left: ClassicalType, right: ClassicalType) -> bool:
|
27
|
+
for type_1, type_2 in ((left, right), (right, left)):
|
28
|
+
if isinstance(type_1, Real) or is_classical_integer(type_1):
|
29
|
+
return isinstance(type_2, Real) or is_classical_integer(type_2)
|
30
|
+
|
31
|
+
if isinstance(left, Bool):
|
32
|
+
return isinstance(right, Bool)
|
33
|
+
|
34
|
+
if isinstance(left, (ClassicalArray, ClassicalTuple)) and isinstance(
|
35
|
+
right, (ClassicalArray, ClassicalTuple)
|
36
|
+
):
|
37
|
+
return list_allowed(element_types(left) + element_types(right))
|
38
|
+
|
39
|
+
if isinstance(left, TypeName) and left.has_classical_struct_decl:
|
40
|
+
return isinstance(right, TypeName) and left.name == right.name
|
41
|
+
|
42
|
+
return False
|
43
|
+
|
44
|
+
|
45
|
+
def _has_empty_tuple(classical_type: ClassicalType) -> bool:
|
46
|
+
if isinstance(classical_type, ClassicalArray):
|
47
|
+
return _has_empty_tuple(classical_type.element_type)
|
48
|
+
if isinstance(classical_type, ClassicalTuple):
|
49
|
+
return len(classical_type.element_types) == 0 or any(
|
50
|
+
_has_empty_tuple(element_type)
|
51
|
+
for element_type in classical_type.element_types
|
52
|
+
)
|
53
|
+
return False
|
54
|
+
|
55
|
+
|
56
|
+
def list_allowed(classical_types: list[ClassicalType]) -> bool:
|
57
|
+
if len(classical_types) < 2:
|
58
|
+
return True
|
59
|
+
|
60
|
+
element_without_empty_tuple: Optional[ClassicalType] = None
|
61
|
+
for classical_type in classical_types:
|
62
|
+
if not _has_empty_tuple(classical_type):
|
63
|
+
element_without_empty_tuple = classical_type
|
64
|
+
break
|
65
|
+
|
66
|
+
if element_without_empty_tuple is not None:
|
67
|
+
return all(
|
68
|
+
element_without_empty_tuple is other_type
|
69
|
+
or _list_allowed(element_without_empty_tuple, other_type)
|
70
|
+
for other_type in classical_types
|
71
|
+
)
|
72
|
+
# FIXME: optimize using ClassicalTuple.raw_type (CLS-3163)
|
73
|
+
return all(
|
74
|
+
element_type is other_type or _list_allowed(classical_types[0], other_type)
|
75
|
+
for i, element_type in enumerate(classical_types[:-1])
|
76
|
+
for other_type in classical_types[i + 1 :]
|
77
|
+
)
|
78
|
+
|
79
|
+
|
80
|
+
def eval_list(expr_val: QmodAnnotatedExpression, node: ast.List) -> None:
|
81
|
+
item_types = [expr_val.get_type(item) for item in node.elts]
|
82
|
+
are_classical = [is_classical_type(item_type) for item_type in item_types]
|
83
|
+
all_classical = all(are_classical)
|
84
|
+
all_quantum = not any(are_classical)
|
85
|
+
if not all_classical and not all_quantum:
|
86
|
+
raise ClassiqExpansionError(
|
87
|
+
"Lists cannot contain both classical and quantum expressions"
|
88
|
+
)
|
89
|
+
if len(item_types) > 0 and all_quantum:
|
90
|
+
expr_val.set_concatenation(node, node.elts)
|
91
|
+
if all(
|
92
|
+
cast(QuantumType, item_type).has_size_in_bits for item_type in item_types
|
93
|
+
):
|
94
|
+
total_size = sum(
|
95
|
+
[cast(QuantumType, item_type).size_in_bits for item_type in item_types]
|
96
|
+
)
|
97
|
+
concat_type = QuantumBitvector(length=Expression(expr=str(total_size)))
|
98
|
+
else:
|
99
|
+
concat_type = QuantumBitvector()
|
100
|
+
expr_val.set_type(node, concat_type)
|
101
|
+
return
|
102
|
+
if not list_allowed(cast(list[ClassicalType], item_types)):
|
103
|
+
raise ClassiqExpansionError("All list items must have the same type")
|
104
|
+
|
105
|
+
if all(expr_val.has_value(item) for item in node.elts):
|
106
|
+
expr_val.set_value(node, [expr_val.get_value(item) for item in node.elts])
|
107
|
+
expr_val.set_type(node, ClassicalTuple(element_types=item_types))
|
@@ -0,0 +1,25 @@
|
|
1
|
+
import ast
|
2
|
+
|
3
|
+
from classiq.interface.exceptions import (
|
4
|
+
ClassiqExpansionError,
|
5
|
+
)
|
6
|
+
from classiq.interface.generator.functions.classical_type import (
|
7
|
+
Bool,
|
8
|
+
)
|
9
|
+
from classiq.interface.model.quantum_type import QuantumBit
|
10
|
+
|
11
|
+
from classiq.evaluators.qmod_annotated_expression import QmodAnnotatedExpression
|
12
|
+
from classiq.evaluators.qmod_node_evaluators.utils import (
|
13
|
+
is_classical_type,
|
14
|
+
)
|
15
|
+
|
16
|
+
|
17
|
+
def eval_measurement(expr_val: QmodAnnotatedExpression, node: ast.Call) -> None:
|
18
|
+
if len(node.args) != 1 or len(node.keywords) != 0:
|
19
|
+
raise ClassiqExpansionError("'measure' expects one positional argument")
|
20
|
+
arg_type = expr_val.get_type(node.args[0])
|
21
|
+
if is_classical_type(arg_type):
|
22
|
+
raise ClassiqExpansionError("'measure' expects a quantum input")
|
23
|
+
if not isinstance(arg_type, QuantumBit):
|
24
|
+
raise ClassiqExpansionError("Currently, 'measure' only supports quantum bits")
|
25
|
+
expr_val.set_type(node, Bool())
|
@@ -0,0 +1,50 @@
|
|
1
|
+
import ast
|
2
|
+
from typing import Any
|
3
|
+
|
4
|
+
from classiq.interface.exceptions import (
|
5
|
+
ClassiqInternalExpansionError,
|
6
|
+
)
|
7
|
+
from classiq.interface.generator.expressions.proxies.classical.qmod_struct_instance import (
|
8
|
+
QmodStructInstance,
|
9
|
+
)
|
10
|
+
from classiq.interface.generator.functions.classical_type import (
|
11
|
+
Bool,
|
12
|
+
ClassicalTuple,
|
13
|
+
ClassicalType,
|
14
|
+
Integer,
|
15
|
+
Real,
|
16
|
+
)
|
17
|
+
from classiq.interface.generator.functions.type_name import Struct
|
18
|
+
from classiq.interface.model.handle_binding import HandleBinding
|
19
|
+
from classiq.interface.model.quantum_type import QuantumType
|
20
|
+
|
21
|
+
from classiq.evaluators.qmod_annotated_expression import QmodAnnotatedExpression
|
22
|
+
|
23
|
+
|
24
|
+
def _infer_classical_type(value: Any) -> ClassicalType:
|
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
|
40
|
+
|
41
|
+
|
42
|
+
def eval_name(expr_val: QmodAnnotatedExpression, node: ast.Name, value: Any) -> None:
|
43
|
+
if isinstance(value, (bool, int, float, complex, list, QmodStructInstance)):
|
44
|
+
expr_val.set_type(node, _infer_classical_type(value))
|
45
|
+
expr_val.set_value(node, value)
|
46
|
+
elif isinstance(value, (ClassicalType, QuantumType)):
|
47
|
+
expr_val.set_type(node, value)
|
48
|
+
expr_val.set_var(node, HandleBinding(name=node.id))
|
49
|
+
else:
|
50
|
+
raise ClassiqInternalExpansionError
|
@@ -0,0 +1,66 @@
|
|
1
|
+
import ast
|
2
|
+
from typing import cast
|
3
|
+
|
4
|
+
from classiq.interface.exceptions import (
|
5
|
+
ClassiqExpansionError,
|
6
|
+
)
|
7
|
+
from classiq.interface.generator.expressions.proxies.classical.qmod_struct_instance import (
|
8
|
+
QmodStructInstance,
|
9
|
+
)
|
10
|
+
from classiq.interface.generator.functions.type_name import Struct
|
11
|
+
from classiq.interface.generator.types.struct_declaration import StructDeclaration
|
12
|
+
from classiq.interface.helpers.text_utils import readable_list, s
|
13
|
+
|
14
|
+
from classiq.evaluators.qmod_annotated_expression import QmodAnnotatedExpression
|
15
|
+
from classiq.evaluators.qmod_node_evaluators.compare_evaluation import (
|
16
|
+
comparison_allowed,
|
17
|
+
)
|
18
|
+
from classiq.evaluators.qmod_node_evaluators.utils import get_qmod_type_name
|
19
|
+
|
20
|
+
|
21
|
+
def eval_struct_instantiation(
|
22
|
+
expr_val: QmodAnnotatedExpression, node: ast.Call, decl: StructDeclaration
|
23
|
+
) -> None:
|
24
|
+
if len(node.args) > 0 or any(kwarg.arg is None for kwarg in node.keywords):
|
25
|
+
raise ClassiqExpansionError(
|
26
|
+
"Classical structs must be instantiated using keyword arguments"
|
27
|
+
)
|
28
|
+
|
29
|
+
field_assignments = {cast(str, kwarg.arg): kwarg.value for kwarg in node.keywords}
|
30
|
+
assigned_fields = list(field_assignments)
|
31
|
+
expected_fields = list(decl.variables)
|
32
|
+
if set(assigned_fields) != set(expected_fields):
|
33
|
+
raise ClassiqExpansionError(
|
34
|
+
f"Struct {decl.name} has field{s(expected_fields)} "
|
35
|
+
f"{readable_list(expected_fields, quote=True)} but was instantiated "
|
36
|
+
f"using the field{s(assigned_fields)} "
|
37
|
+
f"{readable_list(assigned_fields, quote=True)}"
|
38
|
+
)
|
39
|
+
|
40
|
+
for field_name, field_value in field_assignments.items():
|
41
|
+
assignment_type = expr_val.get_type(field_value)
|
42
|
+
expected_type = decl.variables[field_name]
|
43
|
+
if not comparison_allowed(assignment_type, expected_type):
|
44
|
+
raise ClassiqExpansionError(
|
45
|
+
f"Cannot assign value of type {get_qmod_type_name(assignment_type)} "
|
46
|
+
f"to field {field_name!r} of type {get_qmod_type_name(expected_type)}"
|
47
|
+
)
|
48
|
+
|
49
|
+
classical_type = Struct(name=decl.name)
|
50
|
+
classical_type.set_classical_struct_decl(decl)
|
51
|
+
expr_val.set_type(node, classical_type)
|
52
|
+
|
53
|
+
if any(
|
54
|
+
not expr_val.has_value(field_value)
|
55
|
+
for field_value in field_assignments.values()
|
56
|
+
):
|
57
|
+
return
|
58
|
+
|
59
|
+
struct_val = QmodStructInstance(
|
60
|
+
decl,
|
61
|
+
{
|
62
|
+
field_name: expr_val.get_value(field_value)
|
63
|
+
for field_name, field_value in field_assignments.items()
|
64
|
+
},
|
65
|
+
)
|
66
|
+
expr_val.set_value(node, struct_val)
|
@@ -0,0 +1,225 @@
|
|
1
|
+
import ast
|
2
|
+
from typing import Optional, cast
|
3
|
+
|
4
|
+
from classiq.interface.exceptions import (
|
5
|
+
ClassiqExpansionError,
|
6
|
+
ClassiqInternalExpansionError,
|
7
|
+
)
|
8
|
+
from classiq.interface.generator.arith.arithmetic import (
|
9
|
+
aggregate_numeric_types,
|
10
|
+
compute_arithmetic_result_type,
|
11
|
+
)
|
12
|
+
from classiq.interface.generator.expressions.expression import Expression
|
13
|
+
from classiq.interface.generator.functions.classical_type import (
|
14
|
+
ClassicalArray,
|
15
|
+
ClassicalTuple,
|
16
|
+
Integer,
|
17
|
+
Real,
|
18
|
+
)
|
19
|
+
from classiq.interface.model.handle_binding import (
|
20
|
+
SlicedHandleBinding,
|
21
|
+
SubscriptHandleBinding,
|
22
|
+
)
|
23
|
+
from classiq.interface.model.quantum_type import (
|
24
|
+
QuantumBitvector,
|
25
|
+
QuantumNumeric,
|
26
|
+
QuantumType,
|
27
|
+
register_info_to_quantum_type,
|
28
|
+
)
|
29
|
+
|
30
|
+
from classiq.evaluators.qmod_annotated_expression import QmodAnnotatedExpression
|
31
|
+
from classiq.evaluators.qmod_node_evaluators.utils import (
|
32
|
+
QmodType,
|
33
|
+
element_types,
|
34
|
+
get_qmod_type_name,
|
35
|
+
qnum_is_qint,
|
36
|
+
)
|
37
|
+
|
38
|
+
|
39
|
+
def _eval_slice(expr_val: QmodAnnotatedExpression, node: ast.Subscript) -> None:
|
40
|
+
subject = node.value
|
41
|
+
slice_ = cast(ast.Slice, node.slice)
|
42
|
+
start = cast(ast.AST, slice_.lower)
|
43
|
+
stop = cast(ast.AST, slice_.upper)
|
44
|
+
if slice_.step is not None:
|
45
|
+
raise ClassiqExpansionError("Slice step is not supported")
|
46
|
+
|
47
|
+
start_type = expr_val.get_type(start)
|
48
|
+
stop_type = expr_val.get_type(stop)
|
49
|
+
for index_type in (start_type, stop_type):
|
50
|
+
if not isinstance(index_type, Integer):
|
51
|
+
raise ClassiqExpansionError(
|
52
|
+
f"Slice indices must be integers, not {get_qmod_type_name(index_type)!r}"
|
53
|
+
)
|
54
|
+
|
55
|
+
start_val: Optional[int] = None
|
56
|
+
if expr_val.has_value(start):
|
57
|
+
start_val = cast(int, expr_val.get_value(start))
|
58
|
+
if start_val < 0:
|
59
|
+
raise ClassiqExpansionError("Slice indices must be positive integers")
|
60
|
+
stop_val: Optional[int] = None
|
61
|
+
if expr_val.has_value(stop):
|
62
|
+
stop_val = cast(int, expr_val.get_value(stop))
|
63
|
+
if start_val is not None and stop_val < start_val:
|
64
|
+
raise ClassiqExpansionError(
|
65
|
+
"Slice upper bound must be greater or equal to the lower bound"
|
66
|
+
)
|
67
|
+
|
68
|
+
subject_type = expr_val.get_type(subject)
|
69
|
+
slice_type: QmodType
|
70
|
+
if isinstance(subject_type, ClassicalArray):
|
71
|
+
if subject_type.has_length and (
|
72
|
+
(start_val is not None and start_val >= subject_type.length_value)
|
73
|
+
or (stop_val is not None and stop_val > subject_type.length_value)
|
74
|
+
):
|
75
|
+
raise ClassiqExpansionError("Array index out of range")
|
76
|
+
length_expr: Optional[Expression] = None
|
77
|
+
if start_val is not None and stop_val is not None:
|
78
|
+
length_expr = Expression(expr=str(stop_val - start_val))
|
79
|
+
slice_type = ClassicalArray(
|
80
|
+
element_type=subject_type.element_type, length=length_expr
|
81
|
+
)
|
82
|
+
elif isinstance(subject_type, ClassicalTuple):
|
83
|
+
if start_val is not None and stop_val is not None:
|
84
|
+
if start_val >= len(subject_type.element_types) or stop_val > len(
|
85
|
+
subject_type.element_types
|
86
|
+
):
|
87
|
+
raise ClassiqExpansionError("Array index out of range")
|
88
|
+
slice_type = ClassicalTuple(
|
89
|
+
element_types=subject_type.element_types[start_val:stop_val]
|
90
|
+
)
|
91
|
+
else:
|
92
|
+
slice_type = subject_type.get_raw_type()
|
93
|
+
elif isinstance(subject_type, QuantumBitvector):
|
94
|
+
if start_val is not None and stop_val is not None:
|
95
|
+
if subject_type.has_length and (
|
96
|
+
start_val >= subject_type.length_value
|
97
|
+
or stop_val > subject_type.length_value
|
98
|
+
):
|
99
|
+
raise ClassiqExpansionError("Array index out of range")
|
100
|
+
slice_length = Expression(expr=str(stop_val - start_val))
|
101
|
+
else:
|
102
|
+
slice_length = None
|
103
|
+
slice_type = QuantumBitvector(
|
104
|
+
element_type=subject_type.element_type, length=slice_length
|
105
|
+
)
|
106
|
+
else:
|
107
|
+
raise ClassiqExpansionError(
|
108
|
+
f"{get_qmod_type_name(subject_type)} is not subscriptable"
|
109
|
+
)
|
110
|
+
expr_val.set_type(node, slice_type)
|
111
|
+
|
112
|
+
if start_val is None or stop_val is None:
|
113
|
+
return
|
114
|
+
if expr_val.has_value(subject):
|
115
|
+
subject_val = expr_val.get_value(subject)
|
116
|
+
expr_val.set_value(node, subject_val[start_val:stop_val])
|
117
|
+
elif expr_val.has_var(subject):
|
118
|
+
subject_var = expr_val.get_var(subject)
|
119
|
+
expr_val.set_var(
|
120
|
+
node,
|
121
|
+
SlicedHandleBinding(
|
122
|
+
base_handle=subject_var,
|
123
|
+
start=Expression(expr=str(start_val)),
|
124
|
+
end=Expression(expr=str(stop_val)),
|
125
|
+
),
|
126
|
+
)
|
127
|
+
expr_val.remove_var(subject)
|
128
|
+
|
129
|
+
|
130
|
+
def _eval_subscript(expr_val: QmodAnnotatedExpression, node: ast.Subscript) -> None:
|
131
|
+
subject = node.value
|
132
|
+
subscript = node.slice
|
133
|
+
|
134
|
+
index_type = expr_val.get_type(subscript)
|
135
|
+
if not isinstance(index_type, Integer):
|
136
|
+
raise ClassiqExpansionError(
|
137
|
+
f"Array indices must be integers or slices, not {get_qmod_type_name(index_type)}"
|
138
|
+
)
|
139
|
+
|
140
|
+
sub_val: Optional[int] = None
|
141
|
+
if expr_val.has_value(subscript):
|
142
|
+
sub_val = cast(int, expr_val.get_value(subscript))
|
143
|
+
if sub_val < 0:
|
144
|
+
raise ClassiqExpansionError("Array indices must be positive integers")
|
145
|
+
|
146
|
+
subject_type = expr_val.get_type(subject)
|
147
|
+
sub_type: QmodType
|
148
|
+
if isinstance(subject_type, (ClassicalArray, QuantumBitvector)):
|
149
|
+
if (
|
150
|
+
sub_val is not None
|
151
|
+
and subject_type.has_length
|
152
|
+
and sub_val >= subject_type.length_value
|
153
|
+
):
|
154
|
+
raise ClassiqExpansionError("Array index out of range")
|
155
|
+
sub_type = subject_type.element_type
|
156
|
+
elif isinstance(subject_type, ClassicalTuple):
|
157
|
+
if sub_val is not None:
|
158
|
+
if sub_val >= len(subject_type.element_types):
|
159
|
+
raise ClassiqExpansionError("Array index out of range")
|
160
|
+
sub_type = subject_type.element_types[sub_val]
|
161
|
+
else:
|
162
|
+
raw_subject_type = subject_type.get_raw_type()
|
163
|
+
if not isinstance(raw_subject_type, ClassicalArray):
|
164
|
+
raise ClassiqInternalExpansionError
|
165
|
+
sub_type = raw_subject_type.element_type
|
166
|
+
else:
|
167
|
+
raise ClassiqExpansionError(
|
168
|
+
f"{get_qmod_type_name(subject_type)} is not subscriptable"
|
169
|
+
)
|
170
|
+
expr_val.set_type(node, sub_type)
|
171
|
+
|
172
|
+
if sub_val is None:
|
173
|
+
return
|
174
|
+
if expr_val.has_value(subject):
|
175
|
+
subject_val = expr_val.get_value(subject)
|
176
|
+
expr_val.set_value(node, subject_val[sub_val])
|
177
|
+
elif expr_val.has_var(subject):
|
178
|
+
subject_var = expr_val.get_var(subject)
|
179
|
+
expr_val.set_var(
|
180
|
+
node,
|
181
|
+
SubscriptHandleBinding(
|
182
|
+
base_handle=subject_var, index=Expression(expr=str(sub_val))
|
183
|
+
),
|
184
|
+
)
|
185
|
+
expr_val.remove_var(subject)
|
186
|
+
|
187
|
+
|
188
|
+
def eval_subscript(expr_val: QmodAnnotatedExpression, node: ast.Subscript) -> None:
|
189
|
+
if isinstance(node.slice, ast.Slice):
|
190
|
+
_eval_slice(expr_val, node)
|
191
|
+
else:
|
192
|
+
_eval_subscript(expr_val, node)
|
193
|
+
|
194
|
+
|
195
|
+
def eval_quantum_subscript(
|
196
|
+
expr_val: QmodAnnotatedExpression, node: ast.Subscript, machine_precision: int
|
197
|
+
) -> None:
|
198
|
+
subject = node.value
|
199
|
+
subscript = node.slice
|
200
|
+
|
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
|
+
subject_type = expr_val.get_type(subject)
|
205
|
+
if not isinstance(subject_type, (ClassicalArray, ClassicalTuple)) or not all(
|
206
|
+
isinstance(element_type, (Integer, Real))
|
207
|
+
for element_type in element_types(subject_type)
|
208
|
+
):
|
209
|
+
raise ClassiqExpansionError(
|
210
|
+
"Only classical numeric arrays may have quantum subscripts"
|
211
|
+
)
|
212
|
+
|
213
|
+
expr_val.set_quantum_subscript(node, subject, subscript)
|
214
|
+
if not expr_val.has_value(subject):
|
215
|
+
expr_val.set_type(node, QuantumNumeric())
|
216
|
+
return
|
217
|
+
|
218
|
+
items = expr_val.get_value(subject)
|
219
|
+
numeric_types = [
|
220
|
+
compute_arithmetic_result_type(str(num), {}, machine_precision) for num in items
|
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)
|
@@ -0,0 +1,58 @@
|
|
1
|
+
import ast
|
2
|
+
from typing import Any
|
3
|
+
|
4
|
+
from classiq.interface.exceptions import (
|
5
|
+
ClassiqExpansionError,
|
6
|
+
ClassiqInternalExpansionError,
|
7
|
+
)
|
8
|
+
from classiq.interface.generator.functions.classical_type import Integer, Real
|
9
|
+
from classiq.interface.generator.functions.type_name import TypeName
|
10
|
+
|
11
|
+
from classiq.evaluators.qmod_annotated_expression import QmodAnnotatedExpression
|
12
|
+
from classiq.evaluators.qmod_node_evaluators.bool_op_evaluation import is_bool_type
|
13
|
+
from classiq.evaluators.qmod_node_evaluators.utils import QmodType, is_numeric_type
|
14
|
+
|
15
|
+
|
16
|
+
def unary_op_allowed(op: ast.AST, operand_type: QmodType) -> bool:
|
17
|
+
if isinstance(op, ast.Not):
|
18
|
+
return is_bool_type(operand_type)
|
19
|
+
return is_numeric_type(operand_type)
|
20
|
+
|
21
|
+
|
22
|
+
def eval_unary_op(expr_val: QmodAnnotatedExpression, node: ast.UnaryOp) -> None:
|
23
|
+
operand = node.operand
|
24
|
+
op = node.op
|
25
|
+
|
26
|
+
operand_type = expr_val.get_type(operand)
|
27
|
+
if not unary_op_allowed(op, operand_type):
|
28
|
+
expected_val_type = "Boolean" if isinstance(op, ast.Not) else "scalar"
|
29
|
+
raise ClassiqExpansionError(
|
30
|
+
f"The operand of the unary operator {type(op).__name__!r} must be "
|
31
|
+
f"a {expected_val_type} value"
|
32
|
+
)
|
33
|
+
if isinstance(op, ast.Invert) and isinstance(operand_type, Real):
|
34
|
+
raise ClassiqExpansionError(
|
35
|
+
f"Operation {type(op).__name__!r} on a real value is not supported"
|
36
|
+
)
|
37
|
+
op_type: QmodType
|
38
|
+
if isinstance(operand_type, TypeName):
|
39
|
+
op_type = Integer()
|
40
|
+
else:
|
41
|
+
op_type = operand_type
|
42
|
+
expr_val.set_type(node, op_type)
|
43
|
+
|
44
|
+
if not expr_val.has_value(operand):
|
45
|
+
return
|
46
|
+
operand_value = expr_val.get_value(operand)
|
47
|
+
constant_value: Any
|
48
|
+
if isinstance(op, ast.Not):
|
49
|
+
constant_value = not operand_value
|
50
|
+
elif isinstance(op, ast.Invert):
|
51
|
+
constant_value = ~operand_value
|
52
|
+
elif isinstance(op, ast.UAdd):
|
53
|
+
constant_value = +operand_value
|
54
|
+
elif isinstance(op, ast.USub):
|
55
|
+
constant_value = -operand_value
|
56
|
+
else:
|
57
|
+
raise ClassiqInternalExpansionError
|
58
|
+
expr_val.set_value(node, constant_value)
|
@@ -0,0 +1,80 @@
|
|
1
|
+
from typing import Any, Optional, Union, cast
|
2
|
+
|
3
|
+
import sympy
|
4
|
+
|
5
|
+
from classiq.interface.generator.functions.classical_type import (
|
6
|
+
ClassicalArray,
|
7
|
+
ClassicalTuple,
|
8
|
+
ClassicalType,
|
9
|
+
Integer,
|
10
|
+
Real,
|
11
|
+
)
|
12
|
+
from classiq.interface.generator.functions.type_name import TypeName
|
13
|
+
from classiq.interface.model.quantum_type import (
|
14
|
+
QuantumBit,
|
15
|
+
QuantumNumeric,
|
16
|
+
QuantumScalar,
|
17
|
+
QuantumType,
|
18
|
+
)
|
19
|
+
|
20
|
+
QmodType = Union[ClassicalType, QuantumType]
|
21
|
+
IntegerValueType = int
|
22
|
+
RealValueType = Union[float, complex]
|
23
|
+
NumberValueType = Union[IntegerValueType, RealValueType]
|
24
|
+
SYMPY_SYMBOLS = {sym: getattr(sympy, sym) for sym in sympy.__all__}
|
25
|
+
|
26
|
+
|
27
|
+
def get_qmod_type_name(type_: Any) -> str:
|
28
|
+
return getattr(type_, "type_name", type(type_).__name__)
|
29
|
+
|
30
|
+
|
31
|
+
def is_classical_type(qmod_type: QmodType) -> bool:
|
32
|
+
if isinstance(qmod_type, TypeName):
|
33
|
+
return qmod_type.has_classical_struct_decl or qmod_type.is_enum
|
34
|
+
return isinstance(qmod_type, ClassicalType)
|
35
|
+
|
36
|
+
|
37
|
+
def qnum_is_qbit(qmod_type: QuantumNumeric) -> bool:
|
38
|
+
return (
|
39
|
+
(not qmod_type.has_size_in_bits or qmod_type.size_in_bits == 1)
|
40
|
+
and (not qmod_type.has_sign or not qmod_type.sign_value)
|
41
|
+
and (not qmod_type.has_fraction_digits or qmod_type.fraction_digits_value == 0)
|
42
|
+
)
|
43
|
+
|
44
|
+
|
45
|
+
def qnum_is_qint(qmod_type: QuantumType) -> bool:
|
46
|
+
return isinstance(qmod_type, QuantumBit) or (
|
47
|
+
isinstance(qmod_type, QuantumNumeric)
|
48
|
+
and (not qmod_type.has_sign or not qmod_type.sign_value)
|
49
|
+
and (not qmod_type.has_fraction_digits or qmod_type.fraction_digits_value == 0)
|
50
|
+
)
|
51
|
+
|
52
|
+
|
53
|
+
def element_types(
|
54
|
+
classical_type: Union[ClassicalArray, ClassicalTuple],
|
55
|
+
) -> list[ClassicalType]:
|
56
|
+
if isinstance(classical_type, ClassicalArray):
|
57
|
+
return [classical_type.element_type]
|
58
|
+
return cast(list[ClassicalType], classical_type.element_types)
|
59
|
+
|
60
|
+
|
61
|
+
def array_len(
|
62
|
+
classical_type: Union[ClassicalArray, ClassicalTuple],
|
63
|
+
) -> Optional[int]:
|
64
|
+
if isinstance(classical_type, ClassicalTuple):
|
65
|
+
return len(classical_type.element_types)
|
66
|
+
if classical_type.has_length:
|
67
|
+
return classical_type.length_value
|
68
|
+
return None
|
69
|
+
|
70
|
+
|
71
|
+
def is_numeric_type(qmod_type: QmodType) -> bool:
|
72
|
+
return isinstance(qmod_type, (Integer, Real, QuantumScalar)) or (
|
73
|
+
isinstance(qmod_type, TypeName) and qmod_type.is_enum
|
74
|
+
)
|
75
|
+
|
76
|
+
|
77
|
+
def is_classical_integer(qmod_type: QmodType) -> bool:
|
78
|
+
return isinstance(qmod_type, Integer) or (
|
79
|
+
isinstance(qmod_type, TypeName) and qmod_type.is_enum
|
80
|
+
)
|