classiq 0.85.0__py3-none-any.whl → 0.86.1__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 +4 -3
- 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 +4 -0
- classiq/interface/_version.py +1 -1
- classiq/interface/analyzer/analysis_params.py +1 -1
- classiq/interface/analyzer/result.py +1 -1
- 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/expression_types.py +2 -2
- classiq/interface/generator/expressions/proxies/classical/qmod_struct_instance.py +7 -0
- classiq/interface/generator/functions/classical_function_declaration.py +0 -4
- classiq/interface/generator/functions/classical_type.py +0 -32
- classiq/interface/generator/generated_circuit_data.py +2 -0
- 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 +2 -2
- classiq/interface/ide/visual_model.py +22 -1
- classiq/interface/model/quantum_type.py +67 -33
- 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 +5 -5
- classiq/model_expansions/generative_functions.py +15 -2
- classiq/model_expansions/interpreters/base_interpreter.py +7 -0
- classiq/model_expansions/interpreters/generative_interpreter.py +2 -0
- classiq/model_expansions/quantum_operations/call_emitter.py +5 -2
- 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/visitors/boolean_expression_transformers.py +1 -1
- classiq/qmod/builtins/operations.py +1 -1
- classiq/qmod/declaration_inferrer.py +5 -3
- classiq/qmod/write_qmod.py +3 -1
- {classiq-0.85.0.dist-info → classiq-0.86.1.dist-info}/METADATA +1 -1
- {classiq-0.85.0.dist-info → classiq-0.86.1.dist-info}/RECORD +59 -37
- /classiq/{model_expansions/sympy_conversion/arithmetics.py → evaluators/qmod_expression_visitors/sympy_wrappers.py} +0 -0
- {classiq-0.85.0.dist-info → classiq-0.86.1.dist-info}/WHEEL +0 -0
@@ -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
|
+
)
|
@@ -504,6 +504,10 @@ class ExecutionSession:
|
|
504
504
|
raise NotImplementedError(
|
505
505
|
"Filtering is only supported on a single value per model output"
|
506
506
|
)
|
507
|
+
if len(legal_bitstrings) == 0:
|
508
|
+
raise ClassiqValueError(
|
509
|
+
f"The condition was false for every possible value of {output_name}"
|
510
|
+
)
|
507
511
|
else:
|
508
512
|
raise NotImplementedError(
|
509
513
|
"Filtering is only supported on QuantumBit and QuantumNumeric"
|
classiq/interface/_version.py
CHANGED
@@ -14,7 +14,7 @@ from classiq.interface.generator.model.preferences.preferences import (
|
|
14
14
|
from classiq.interface.hardware import Provider
|
15
15
|
from classiq.interface.helpers.custom_pydantic_types import PydanticNonEmptyString
|
16
16
|
|
17
|
-
MAX_KB_OF_FILE =
|
17
|
+
MAX_KB_OF_FILE = 50000
|
18
18
|
MAX_FILE_LENGTH = MAX_KB_OF_FILE * 1024
|
19
19
|
|
20
20
|
MAX_QUBITS = 100
|
@@ -95,8 +95,8 @@ class QuantumCode(QuantumBaseCode):
|
|
95
95
|
) -> Optional[ExecutionData]:
|
96
96
|
if (
|
97
97
|
synthesis_execution_data is not None
|
98
|
-
and
|
99
|
-
):
|
98
|
+
and synthesis_execution_data.function_execution
|
99
|
+
) and values.data.get("syntax") is not QuantumInstructionSet.QASM:
|
100
100
|
raise ClassiqValueError("Only QASM supports the requested configuration")
|
101
101
|
|
102
102
|
return synthesis_execution_data
|
@@ -63,6 +63,10 @@ def is_constant(expr: Union[str, Expr]) -> bool:
|
|
63
63
|
return False
|
64
64
|
|
65
65
|
|
66
|
+
def is_variable(expr: str) -> bool:
|
67
|
+
return IDENITIFIER_REGEX.fullmatch(expr) is not None
|
68
|
+
|
69
|
+
|
66
70
|
class ExpressionValidator(ast.NodeVisitor):
|
67
71
|
def __init__(
|
68
72
|
self,
|
@@ -95,7 +99,7 @@ class ExpressionValidator(ast.NodeVisitor):
|
|
95
99
|
@staticmethod
|
96
100
|
def _get_adjusted_expression(expression: str) -> str:
|
97
101
|
# This works around the simplification of the trivial expressions such as a + 0, 1 * a, etc.
|
98
|
-
if
|
102
|
+
if is_variable(expression) or is_constant(expression):
|
99
103
|
return f"0 + {expression}"
|
100
104
|
return expression
|
101
105
|
|
@@ -20,7 +20,6 @@ from classiq.interface.exceptions import ClassiqValueError
|
|
20
20
|
from classiq.interface.generator.arith import argument_utils, number_utils
|
21
21
|
from classiq.interface.generator.arith.argument_utils import (
|
22
22
|
RegisterOrConst,
|
23
|
-
as_arithmetic_info,
|
24
23
|
)
|
25
24
|
from classiq.interface.generator.arith.arithmetic_operations import (
|
26
25
|
MODULO_WITH_FRACTION_PLACES_ERROR_MSG,
|
@@ -33,6 +32,13 @@ from classiq.interface.generator.arith.register_user_input import RegisterArithm
|
|
33
32
|
from classiq.interface.generator.arith.unary_ops import Negation
|
34
33
|
from classiq.interface.generator.function_params import get_zero_input_name
|
35
34
|
|
35
|
+
from classiq.model_expansions.arithmetic import NumericAttributes
|
36
|
+
from classiq.model_expansions.arithmetic_compute_result_attrs import (
|
37
|
+
compute_result_attrs_add,
|
38
|
+
compute_result_attrs_assign,
|
39
|
+
compute_result_attrs_subtract,
|
40
|
+
)
|
41
|
+
|
36
42
|
LeftDataT = TypeVar("LeftDataT")
|
37
43
|
RightDataT = TypeVar("RightDataT")
|
38
44
|
_NumericArgumentInplaceErrorMessage: str = "Cannot inplace the numeric argument {}"
|
@@ -268,40 +274,35 @@ class Adder(InplacableBinaryOpParams[RegisterOrConst, RegisterOrConst]):
|
|
268
274
|
output_name = "sum"
|
269
275
|
|
270
276
|
def _get_result_register(self) -> RegisterArithmeticInfo:
|
271
|
-
|
277
|
+
left_attrs = NumericAttributes.from_type_or_constant(
|
272
278
|
self.left_arg, self.machine_precision
|
273
279
|
)
|
274
|
-
|
280
|
+
right_attrs = NumericAttributes.from_type_or_constant(
|
275
281
|
self.right_arg, self.machine_precision
|
276
282
|
)
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
283
|
+
|
284
|
+
if self._is_zero(self.left_arg) or self._is_zero(self.right_arg):
|
285
|
+
other_arg_attrs = (
|
286
|
+
right_attrs if self._is_zero(self.left_arg) else left_attrs
|
287
|
+
)
|
288
|
+
result_attrs = compute_result_attrs_assign(
|
289
|
+
other_arg_attrs, self.machine_precision
|
290
|
+
)
|
291
|
+
else:
|
292
|
+
result_attrs = compute_result_attrs_add(
|
293
|
+
left_attrs, right_attrs, self.machine_precision
|
294
|
+
)
|
295
|
+
|
287
296
|
return RegisterArithmeticInfo(
|
288
|
-
size=self.output_size or
|
289
|
-
fraction_places=
|
290
|
-
is_signed=self._include_sign and
|
291
|
-
bounds=
|
297
|
+
size=self.output_size or result_attrs.size,
|
298
|
+
fraction_places=result_attrs.fraction_digits,
|
299
|
+
is_signed=self._include_sign and result_attrs.is_signed,
|
300
|
+
bounds=result_attrs.bounds if self._include_sign else None,
|
292
301
|
)
|
293
302
|
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
return self.right_arg.size
|
298
|
-
return as_arithmetic_info(self.right_arg).size
|
299
|
-
elif isinstance(self.right_arg, float) and self.right_arg == 0.0:
|
300
|
-
assert isinstance(self.left_arg, RegisterArithmeticInfo)
|
301
|
-
return self.left_arg.size
|
302
|
-
|
303
|
-
integer_part_size = number_utils.bounds_to_integer_part_size(lb, ub)
|
304
|
-
return integer_part_size + fraction_places
|
303
|
+
@staticmethod
|
304
|
+
def _is_zero(arg: RegisterOrConst) -> bool:
|
305
|
+
return isinstance(arg, float) and arg == 0
|
305
306
|
|
306
307
|
|
307
308
|
class Subtractor(InplacableBinaryOpParams[RegisterOrConst, RegisterOrConst]):
|
@@ -337,41 +338,32 @@ class Subtractor(InplacableBinaryOpParams[RegisterOrConst, RegisterOrConst]):
|
|
337
338
|
return self._is_arg_trimmed_register(self.right_arg, self.machine_precision)
|
338
339
|
|
339
340
|
def _get_result_register(self) -> RegisterArithmeticInfo:
|
340
|
-
|
341
|
-
|
342
|
-
- argument_utils.upper_bound(self.effective_right_arg),
|
343
|
-
argument_utils.upper_bound(self.effective_left_arg)
|
344
|
-
- argument_utils.lower_bound(self.effective_right_arg),
|
341
|
+
left_attrs = NumericAttributes.from_type_or_constant(
|
342
|
+
self.left_arg, self.machine_precision
|
345
343
|
)
|
346
|
-
|
347
|
-
|
348
|
-
|
344
|
+
right_attrs = NumericAttributes.from_type_or_constant(
|
345
|
+
self.right_arg, self.machine_precision
|
346
|
+
)
|
347
|
+
result_attrs = compute_result_attrs_subtract(
|
348
|
+
left_attrs, right_attrs, self.machine_precision
|
349
349
|
)
|
350
350
|
|
351
|
-
size = self.output_size or
|
352
|
-
is_signed = self._include_sign and
|
351
|
+
size = self.output_size or result_attrs.size
|
352
|
+
is_signed = self._include_sign and result_attrs.is_signed
|
353
353
|
return RegisterArithmeticInfo(
|
354
354
|
size=size,
|
355
|
-
fraction_places=
|
355
|
+
fraction_places=result_attrs.fraction_digits,
|
356
356
|
is_signed=is_signed,
|
357
357
|
bounds=self._legal_bounds(
|
358
|
-
bounds,
|
358
|
+
result_attrs.bounds,
|
359
359
|
RegisterArithmeticInfo.get_maximal_bounds(
|
360
|
-
size=size,
|
360
|
+
size=size,
|
361
|
+
is_signed=is_signed,
|
362
|
+
fraction_places=result_attrs.fraction_digits,
|
361
363
|
),
|
362
364
|
),
|
363
365
|
)
|
364
366
|
|
365
|
-
def _get_output_size(
|
366
|
-
self, bounds: tuple[float, float], fraction_places: int
|
367
|
-
) -> int:
|
368
|
-
if isinstance(self.right_arg, float) and self.effective_right_arg == 0:
|
369
|
-
assert isinstance(self.effective_left_arg, RegisterArithmeticInfo)
|
370
|
-
return self.effective_left_arg.size
|
371
|
-
integer_part_size = number_utils.bounds_to_integer_part_size(*bounds)
|
372
|
-
size_needed = integer_part_size + fraction_places
|
373
|
-
return size_needed
|
374
|
-
|
375
367
|
def garbage_output_size(self) -> pydantic.NonNegativeInt:
|
376
368
|
if (
|
377
369
|
not self.left_arg_is_trimmed_register
|
@@ -79,10 +79,11 @@ def size(float_value: float) -> int:
|
|
79
79
|
|
80
80
|
|
81
81
|
def _is_extra_sign_bit_needed(*, lb: float, ub: float) -> bool:
|
82
|
-
|
82
|
+
fractions = max(fraction_places(lb), fraction_places(ub))
|
83
|
+
integer_lb = lb * 2**fractions
|
83
84
|
max_represented_number = (
|
84
85
|
2 ** (len(binary_string(integer_lb)) - 1) - 1
|
85
|
-
) / 2
|
86
|
+
) / 2**fractions
|
86
87
|
return ub > max_represented_number
|
87
88
|
|
88
89
|
|