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.
Files changed (87) hide show
  1. classiq/applications/combinatorial_optimization/combinatorial_problem.py +24 -45
  2. classiq/evaluators/classical_expression.py +32 -15
  3. classiq/evaluators/qmod_annotated_expression.py +207 -0
  4. classiq/evaluators/qmod_expression_visitors/__init__.py +0 -0
  5. classiq/evaluators/qmod_expression_visitors/qmod_expression_bwc.py +134 -0
  6. classiq/evaluators/qmod_expression_visitors/qmod_expression_evaluator.py +232 -0
  7. classiq/evaluators/qmod_expression_visitors/qmod_expression_renamer.py +44 -0
  8. classiq/evaluators/qmod_expression_visitors/qmod_expression_simplifier.py +308 -0
  9. classiq/evaluators/qmod_node_evaluators/__init__.py +0 -0
  10. classiq/evaluators/qmod_node_evaluators/attribute_evaluation.py +112 -0
  11. classiq/evaluators/qmod_node_evaluators/binary_op_evaluation.py +132 -0
  12. classiq/evaluators/qmod_node_evaluators/bool_op_evaluation.py +70 -0
  13. classiq/evaluators/qmod_node_evaluators/classical_function_evaluation.py +311 -0
  14. classiq/evaluators/qmod_node_evaluators/compare_evaluation.py +107 -0
  15. classiq/evaluators/qmod_node_evaluators/constant_evaluation.py +67 -0
  16. classiq/evaluators/qmod_node_evaluators/list_evaluation.py +107 -0
  17. classiq/evaluators/qmod_node_evaluators/measurement_evaluation.py +25 -0
  18. classiq/evaluators/qmod_node_evaluators/name_evaluation.py +50 -0
  19. classiq/evaluators/qmod_node_evaluators/struct_instantiation_evaluation.py +66 -0
  20. classiq/evaluators/qmod_node_evaluators/subscript_evaluation.py +225 -0
  21. classiq/evaluators/qmod_node_evaluators/unary_op_evaluation.py +58 -0
  22. classiq/evaluators/qmod_node_evaluators/utils.py +80 -0
  23. classiq/execution/execution_session.py +53 -6
  24. classiq/interface/_version.py +1 -1
  25. classiq/interface/analyzer/analysis_params.py +1 -1
  26. classiq/interface/analyzer/result.py +1 -1
  27. classiq/interface/debug_info/debug_info.py +0 -4
  28. classiq/interface/executor/quantum_code.py +2 -2
  29. classiq/interface/generator/arith/arithmetic_expression_validator.py +5 -1
  30. classiq/interface/generator/arith/binary_ops.py +43 -51
  31. classiq/interface/generator/arith/number_utils.py +3 -2
  32. classiq/interface/generator/arith/register_user_input.py +15 -0
  33. classiq/interface/generator/arith/unary_ops.py +32 -28
  34. classiq/interface/generator/expressions/atomic_expression_functions.py +5 -0
  35. classiq/interface/generator/expressions/expression_types.py +2 -2
  36. classiq/interface/generator/expressions/proxies/classical/qmod_struct_instance.py +7 -0
  37. classiq/interface/generator/functions/builtins/internal_operators.py +2 -0
  38. classiq/interface/generator/functions/classical_function_declaration.py +0 -4
  39. classiq/interface/generator/functions/classical_type.py +0 -32
  40. classiq/interface/generator/functions/concrete_types.py +20 -0
  41. classiq/interface/generator/generated_circuit_data.py +7 -10
  42. classiq/interface/generator/quantum_program.py +6 -1
  43. classiq/interface/generator/synthesis_metadata/synthesis_execution_data.py +29 -0
  44. classiq/interface/ide/operation_registry.py +45 -0
  45. classiq/interface/ide/visual_model.py +84 -2
  46. classiq/interface/model/bounds.py +12 -2
  47. classiq/interface/model/quantum_expressions/arithmetic_operation.py +7 -4
  48. classiq/interface/model/quantum_type.py +67 -33
  49. classiq/interface/model/variable_declaration_statement.py +33 -6
  50. classiq/model_expansions/arithmetic.py +115 -0
  51. classiq/model_expansions/arithmetic_compute_result_attrs.py +71 -0
  52. classiq/model_expansions/atomic_expression_functions_defs.py +10 -6
  53. classiq/model_expansions/function_builder.py +4 -1
  54. classiq/model_expansions/generative_functions.py +15 -2
  55. classiq/model_expansions/interpreters/base_interpreter.py +7 -0
  56. classiq/model_expansions/interpreters/generative_interpreter.py +18 -1
  57. classiq/model_expansions/quantum_operations/assignment_result_processor.py +63 -21
  58. classiq/model_expansions/quantum_operations/bounds.py +7 -1
  59. classiq/model_expansions/quantum_operations/call_emitter.py +5 -2
  60. classiq/model_expansions/quantum_operations/classical_var_emitter.py +16 -0
  61. classiq/model_expansions/quantum_operations/variable_decleration.py +30 -10
  62. classiq/model_expansions/scope.py +7 -0
  63. classiq/model_expansions/scope_initialization.py +2 -0
  64. classiq/model_expansions/sympy_conversion/sympy_to_python.py +1 -1
  65. classiq/model_expansions/transformers/type_modifier_inference.py +5 -0
  66. classiq/model_expansions/transformers/var_splitter.py +1 -1
  67. classiq/model_expansions/visitors/boolean_expression_transformers.py +1 -1
  68. classiq/open_library/functions/__init__.py +0 -2
  69. classiq/open_library/functions/qaoa_penalty.py +8 -1
  70. classiq/open_library/functions/state_preparation.py +1 -32
  71. classiq/qmod/__init__.py +2 -0
  72. classiq/qmod/builtins/operations.py +66 -2
  73. classiq/qmod/classical_variable.py +74 -0
  74. classiq/qmod/declaration_inferrer.py +5 -3
  75. classiq/qmod/native/pretty_printer.py +18 -14
  76. classiq/qmod/pretty_print/pretty_printer.py +34 -15
  77. classiq/qmod/qfunc.py +2 -19
  78. classiq/qmod/qmod_variable.py +5 -8
  79. classiq/qmod/quantum_expandable.py +1 -1
  80. classiq/qmod/quantum_function.py +42 -2
  81. classiq/qmod/symbolic_type.py +2 -1
  82. classiq/qmod/write_qmod.py +3 -1
  83. {classiq-0.84.0.dist-info → classiq-0.86.0.dist-info}/METADATA +1 -1
  84. {classiq-0.84.0.dist-info → classiq-0.86.0.dist-info}/RECORD +86 -62
  85. classiq/interface/model/quantum_variable_declaration.py +0 -7
  86. /classiq/{model_expansions/sympy_conversion/arithmetics.py → evaluators/qmod_expression_visitors/sympy_wrappers.py} +0 -0
  87. {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
+ )