classiq 0.85.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 +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.0.dist-info}/METADATA +1 -1
- {classiq-0.85.0.dist-info → classiq-0.86.0.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.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,311 @@
|
|
1
|
+
import ast
|
2
|
+
from typing import Any, Callable, Optional, Union, cast
|
3
|
+
|
4
|
+
import sympy
|
5
|
+
|
6
|
+
from classiq.interface.exceptions import (
|
7
|
+
ClassiqExpansionError,
|
8
|
+
ClassiqInternalExpansionError,
|
9
|
+
)
|
10
|
+
from classiq.interface.generator.functions.classical_function_declaration import (
|
11
|
+
ClassicalFunctionDeclaration,
|
12
|
+
)
|
13
|
+
from classiq.interface.generator.functions.classical_type import (
|
14
|
+
Bool,
|
15
|
+
ClassicalArray,
|
16
|
+
ClassicalTuple,
|
17
|
+
ClassicalType,
|
18
|
+
Integer,
|
19
|
+
Real,
|
20
|
+
)
|
21
|
+
from classiq.interface.generator.functions.type_name import TypeName
|
22
|
+
from classiq.interface.helpers.backward_compatibility import zip_strict
|
23
|
+
from classiq.interface.helpers.pydantic_model_helpers import nameables_to_dict
|
24
|
+
|
25
|
+
from classiq.evaluators.qmod_annotated_expression import QmodAnnotatedExpression
|
26
|
+
from classiq.evaluators.qmod_node_evaluators.utils import (
|
27
|
+
SYMPY_SYMBOLS,
|
28
|
+
QmodType,
|
29
|
+
array_len,
|
30
|
+
element_types,
|
31
|
+
get_qmod_type_name,
|
32
|
+
)
|
33
|
+
|
34
|
+
MIN_MAX_FUNCTION_NAMES = {"min", "Min", "max", "Max"}
|
35
|
+
|
36
|
+
|
37
|
+
def _check_classical_array_arg_type(
|
38
|
+
param_type: Union[ClassicalArray, ClassicalTuple], arg_type: ClassicalType
|
39
|
+
) -> bool:
|
40
|
+
if not isinstance(arg_type, (ClassicalArray, ClassicalTuple)):
|
41
|
+
return False
|
42
|
+
param_len = array_len(param_type)
|
43
|
+
arg_len = array_len(arg_type)
|
44
|
+
if param_len is not None and arg_len is not None and param_len != arg_len:
|
45
|
+
return False
|
46
|
+
if isinstance(param_type, ClassicalArray):
|
47
|
+
if isinstance(arg_type, ClassicalArray):
|
48
|
+
return check_classical_arg_type(
|
49
|
+
param_type.element_type, arg_type.element_type
|
50
|
+
)
|
51
|
+
return all(
|
52
|
+
check_classical_arg_type(param_type.element_type, arg_element_type)
|
53
|
+
for arg_element_type in arg_type.element_types
|
54
|
+
)
|
55
|
+
if isinstance(arg_type, ClassicalArray):
|
56
|
+
return all(
|
57
|
+
check_classical_arg_type(param_element_type, arg_type.element_type)
|
58
|
+
for param_element_type in param_type.element_types
|
59
|
+
)
|
60
|
+
return all(
|
61
|
+
check_classical_arg_type(param_element_type, arg_element_type)
|
62
|
+
for param_element_type, arg_element_type in zip_strict(
|
63
|
+
param_type.element_types, arg_type.element_types, strict=True
|
64
|
+
)
|
65
|
+
)
|
66
|
+
|
67
|
+
|
68
|
+
def check_classical_arg_type(
|
69
|
+
param_type: ClassicalType, arg_type: ClassicalType
|
70
|
+
) -> bool:
|
71
|
+
if isinstance(param_type, (Integer, Real)):
|
72
|
+
return isinstance(arg_type, (Integer, Real))
|
73
|
+
if isinstance(param_type, Bool):
|
74
|
+
return isinstance(arg_type, Bool)
|
75
|
+
if isinstance(param_type, (ClassicalArray, ClassicalTuple)):
|
76
|
+
return _check_classical_array_arg_type(param_type, arg_type)
|
77
|
+
if not isinstance(param_type, TypeName):
|
78
|
+
raise ClassiqInternalExpansionError
|
79
|
+
if not isinstance(arg_type, TypeName):
|
80
|
+
return False
|
81
|
+
if param_type.is_enum or param_type.has_classical_struct_decl:
|
82
|
+
return arg_type.name == param_type.name
|
83
|
+
raise ClassiqInternalExpansionError
|
84
|
+
|
85
|
+
|
86
|
+
def _check_classical_arg(
|
87
|
+
func_name: str, param_name: str, param_type: ClassicalType, arg_type: QmodType
|
88
|
+
) -> None:
|
89
|
+
if not isinstance(arg_type, ClassicalType) or not check_classical_arg_type(
|
90
|
+
param_type, arg_type
|
91
|
+
):
|
92
|
+
raise ClassiqExpansionError(
|
93
|
+
f"Parameter {param_name!r} of function {func_name!r} expects a "
|
94
|
+
f"{get_qmod_type_name(param_type)} argument, but got a "
|
95
|
+
f"{get_qmod_type_name(arg_type)}"
|
96
|
+
)
|
97
|
+
|
98
|
+
|
99
|
+
def check_classical_func_call(
|
100
|
+
expr_val: QmodAnnotatedExpression,
|
101
|
+
node: ast.Call,
|
102
|
+
decl: ClassicalFunctionDeclaration,
|
103
|
+
) -> None:
|
104
|
+
expected_num_args = len(decl.param_decls)
|
105
|
+
actual_num_args = len(node.args) + len(node.keywords)
|
106
|
+
if expected_num_args != actual_num_args:
|
107
|
+
raise ClassiqExpansionError(
|
108
|
+
f"Function {decl.name!r} takes {expected_num_args} arguments but "
|
109
|
+
f"{actual_num_args} were given"
|
110
|
+
)
|
111
|
+
params = list(decl.param_decls)
|
112
|
+
assigned_params: set[str] = set()
|
113
|
+
for arg in node.args:
|
114
|
+
param = params.pop(0)
|
115
|
+
assigned_params.add(param.name)
|
116
|
+
_check_classical_arg(
|
117
|
+
decl.name, param.name, param.classical_type, expr_val.get_type(arg)
|
118
|
+
)
|
119
|
+
remaining_params = nameables_to_dict(params)
|
120
|
+
for kwarg in node.keywords:
|
121
|
+
if kwarg.arg is None:
|
122
|
+
raise ClassiqExpansionError("Star argument syntax is not supported")
|
123
|
+
if kwarg.arg not in remaining_params:
|
124
|
+
if kwarg.arg in assigned_params:
|
125
|
+
raise ClassiqExpansionError(
|
126
|
+
f"Function {decl.name!r} got multiple values for parameter "
|
127
|
+
f"{kwarg.arg!r}"
|
128
|
+
)
|
129
|
+
raise ClassiqExpansionError(
|
130
|
+
f"Function {decl.name!r} has no parameter named {kwarg.arg}"
|
131
|
+
)
|
132
|
+
assigned_params.add(kwarg.arg)
|
133
|
+
param = remaining_params.pop(kwarg.arg)
|
134
|
+
_check_classical_arg(
|
135
|
+
decl.name, param.name, param.classical_type, expr_val.get_type(kwarg.value)
|
136
|
+
)
|
137
|
+
|
138
|
+
|
139
|
+
def eval_symbolic_function(
|
140
|
+
expr_val: QmodAnnotatedExpression,
|
141
|
+
node: ast.Call,
|
142
|
+
decl: ClassicalFunctionDeclaration,
|
143
|
+
) -> None:
|
144
|
+
check_classical_func_call(expr_val, node, decl)
|
145
|
+
if decl.return_type is None:
|
146
|
+
raise ClassiqInternalExpansionError
|
147
|
+
expr_val.set_type(node, decl.return_type)
|
148
|
+
|
149
|
+
|
150
|
+
def eval_function(
|
151
|
+
expr_val: QmodAnnotatedExpression,
|
152
|
+
node: ast.Call,
|
153
|
+
decl: ClassicalFunctionDeclaration,
|
154
|
+
func: Callable,
|
155
|
+
) -> None:
|
156
|
+
eval_symbolic_function(expr_val, node, decl)
|
157
|
+
|
158
|
+
args = node.args
|
159
|
+
kwargs = {kwarg.arg: kwarg.value for kwarg in node.keywords}
|
160
|
+
if None in kwargs:
|
161
|
+
raise ClassiqInternalExpansionError
|
162
|
+
if not all(expr_val.has_value(arg) for arg in args) or not all(
|
163
|
+
expr_val.has_value(kwarg) for kwarg in kwargs.values()
|
164
|
+
):
|
165
|
+
return
|
166
|
+
arg_values = [expr_val.get_value(arg) for arg in args]
|
167
|
+
kwarg_values = {
|
168
|
+
cast(str, kwarg_name): expr_val.get_value(kwarg_value)
|
169
|
+
for kwarg_name, kwarg_value in kwargs.items()
|
170
|
+
}
|
171
|
+
expr_val.set_value(node, func(*arg_values, **kwarg_values))
|
172
|
+
|
173
|
+
|
174
|
+
def try_eval_sympy_function(
|
175
|
+
expr_val: QmodAnnotatedExpression, node: ast.Call, func_name: str
|
176
|
+
) -> bool:
|
177
|
+
sympy_func = SYMPY_SYMBOLS.get(func_name)
|
178
|
+
if not isinstance(sympy_func, sympy.FunctionClass):
|
179
|
+
return False
|
180
|
+
_validate_no_kwargs(node)
|
181
|
+
args: Optional[list[Any]] = None
|
182
|
+
sympy_ret_val: Any = None
|
183
|
+
if all(expr_val.has_value(arg) for arg in node.args):
|
184
|
+
args = [expr_val.get_value(arg) for arg in node.args]
|
185
|
+
sympy_ret_val = sympy_func(*args)
|
186
|
+
if not isinstance(sympy_ret_val, sympy.Expr):
|
187
|
+
raise ClassiqInternalExpansionError
|
188
|
+
ret_type: QmodType
|
189
|
+
ret_val: Any = None
|
190
|
+
if hasattr(sympy_func, "is_Boolean") and sympy_func.is_Boolean:
|
191
|
+
ret_type = Bool()
|
192
|
+
if args is not None:
|
193
|
+
ret_val = bool(sympy_ret_val)
|
194
|
+
elif hasattr(sympy_func, "is_Integer") and sympy_func.is_Integer:
|
195
|
+
ret_type = Integer()
|
196
|
+
if args is not None:
|
197
|
+
ret_val = int(sympy_ret_val)
|
198
|
+
else:
|
199
|
+
ret_type = Real()
|
200
|
+
if sympy_ret_val is not None:
|
201
|
+
if sympy_ret_val.is_imaginary:
|
202
|
+
ret_val = complex(sympy_ret_val)
|
203
|
+
elif sympy_ret_val.is_real:
|
204
|
+
ret_val = float(sympy_ret_val)
|
205
|
+
else:
|
206
|
+
raise ClassiqInternalExpansionError
|
207
|
+
expr_val.set_type(node, ret_type)
|
208
|
+
if ret_val is not None:
|
209
|
+
expr_val.set_value(node, ret_val)
|
210
|
+
return True
|
211
|
+
|
212
|
+
|
213
|
+
def try_eval_builtin_function(
|
214
|
+
expr_val: QmodAnnotatedExpression, node: ast.Call, func_name: str
|
215
|
+
) -> bool:
|
216
|
+
args_are_int = all(isinstance(expr_val.get_type(arg), Integer) for arg in node.args)
|
217
|
+
args_are_real = all(
|
218
|
+
isinstance(expr_val.get_type(arg), (Integer, Real)) for arg in node.args
|
219
|
+
)
|
220
|
+
arg_is_int_list = (
|
221
|
+
len(node.args) == 1
|
222
|
+
and isinstance(
|
223
|
+
arg_type := expr_val.get_type(node.args[0]),
|
224
|
+
(ClassicalArray, ClassicalTuple),
|
225
|
+
)
|
226
|
+
and all(
|
227
|
+
isinstance(element_type, Integer)
|
228
|
+
for element_type in element_types(arg_type)
|
229
|
+
)
|
230
|
+
)
|
231
|
+
arg_is_real_list = (
|
232
|
+
len(node.args) == 1
|
233
|
+
and isinstance(
|
234
|
+
arg_type := expr_val.get_type(node.args[0]),
|
235
|
+
(ClassicalArray, ClassicalTuple),
|
236
|
+
)
|
237
|
+
and all(
|
238
|
+
isinstance(element_type, (Integer, Real))
|
239
|
+
for element_type in element_types(arg_type)
|
240
|
+
)
|
241
|
+
)
|
242
|
+
args_have_values = all(expr_val.has_value(arg) for arg in node.args)
|
243
|
+
|
244
|
+
if func_name in MIN_MAX_FUNCTION_NAMES:
|
245
|
+
if not try_eval_sympy_function(expr_val, node, func_name.capitalize()):
|
246
|
+
return False
|
247
|
+
if args_are_int:
|
248
|
+
expr_val.set_type(node, Integer())
|
249
|
+
if expr_val.has_value(node):
|
250
|
+
expr_val.set_value(node, int(expr_val.get_value(node)))
|
251
|
+
return True
|
252
|
+
|
253
|
+
if func_name == "mod_inverse":
|
254
|
+
_validate_no_kwargs(node)
|
255
|
+
ret_type: QmodType
|
256
|
+
if args_are_int:
|
257
|
+
ret_type = Integer()
|
258
|
+
elif args_are_real:
|
259
|
+
ret_type = Real()
|
260
|
+
else:
|
261
|
+
raise ClassiqExpansionError(
|
262
|
+
"Function 'mod_inverse' expects numeric arguments"
|
263
|
+
)
|
264
|
+
expr_val.set_type(node, ret_type)
|
265
|
+
if args_have_values:
|
266
|
+
sympy_val = sympy.mod_inverse(
|
267
|
+
*[expr_val.get_value(arg) for arg in node.args]
|
268
|
+
)
|
269
|
+
ret_val: Any
|
270
|
+
if args_are_int:
|
271
|
+
ret_val = int(sympy_val)
|
272
|
+
elif isinstance(sympy_val, sympy.Expr) and sympy_val.is_imaginary:
|
273
|
+
ret_val = complex(sympy_val)
|
274
|
+
else:
|
275
|
+
ret_val = float(sympy_val)
|
276
|
+
expr_val.set_value(node, ret_val)
|
277
|
+
return True
|
278
|
+
|
279
|
+
if func_name == "sum":
|
280
|
+
_validate_no_kwargs(node)
|
281
|
+
if arg_is_int_list:
|
282
|
+
ret_type = Integer()
|
283
|
+
elif arg_is_real_list:
|
284
|
+
ret_type = Real()
|
285
|
+
else:
|
286
|
+
raise ClassiqExpansionError("Function 'sum' expects numeric arguments")
|
287
|
+
expr_val.set_type(node, ret_type)
|
288
|
+
if args_have_values:
|
289
|
+
expr_val.set_value(node, sum(expr_val.get_value(node.args[0])))
|
290
|
+
return True
|
291
|
+
|
292
|
+
if func_name == "sqrt":
|
293
|
+
_validate_no_kwargs(node)
|
294
|
+
expr_val.set_type(node, Real())
|
295
|
+
if args_have_values:
|
296
|
+
expr_val.set_value(
|
297
|
+
node, sympy.sqrt(*[expr_val.get_value(arg) for arg in node.args])
|
298
|
+
)
|
299
|
+
return True
|
300
|
+
|
301
|
+
return False
|
302
|
+
|
303
|
+
|
304
|
+
def _validate_no_kwargs(node: ast.Call) -> None:
|
305
|
+
if not isinstance(node.func, ast.Name):
|
306
|
+
raise ClassiqInternalExpansionError
|
307
|
+
if len(node.keywords) > 0:
|
308
|
+
raise ClassiqExpansionError(
|
309
|
+
f"Keyword argument syntax is not supported for built-in function "
|
310
|
+
f"{node.func.id!r}"
|
311
|
+
)
|
@@ -0,0 +1,107 @@
|
|
1
|
+
import ast
|
2
|
+
|
3
|
+
from classiq.interface.exceptions import (
|
4
|
+
ClassiqExpansionError,
|
5
|
+
ClassiqInternalExpansionError,
|
6
|
+
)
|
7
|
+
from classiq.interface.generator.functions.classical_type import (
|
8
|
+
Bool,
|
9
|
+
ClassicalArray,
|
10
|
+
ClassicalTuple,
|
11
|
+
Integer,
|
12
|
+
Real,
|
13
|
+
)
|
14
|
+
from classiq.interface.generator.functions.type_name import TypeName
|
15
|
+
from classiq.interface.model.quantum_type import (
|
16
|
+
QuantumBit,
|
17
|
+
QuantumNumeric,
|
18
|
+
QuantumScalar,
|
19
|
+
)
|
20
|
+
|
21
|
+
from classiq.evaluators.qmod_annotated_expression import QmodAnnotatedExpression
|
22
|
+
from classiq.evaluators.qmod_node_evaluators.list_evaluation import list_allowed
|
23
|
+
from classiq.evaluators.qmod_node_evaluators.utils import (
|
24
|
+
QmodType,
|
25
|
+
element_types,
|
26
|
+
get_qmod_type_name,
|
27
|
+
is_classical_integer,
|
28
|
+
is_classical_type,
|
29
|
+
qnum_is_qbit,
|
30
|
+
)
|
31
|
+
|
32
|
+
|
33
|
+
def comparison_allowed(
|
34
|
+
left: QmodType, right: QmodType, inequality: bool = False
|
35
|
+
) -> bool:
|
36
|
+
if isinstance(left, Bool):
|
37
|
+
return isinstance(right, (Bool, QuantumBit)) or (
|
38
|
+
isinstance(right, QuantumNumeric) and qnum_is_qbit(right)
|
39
|
+
)
|
40
|
+
if isinstance(left, Real) or is_classical_integer(left):
|
41
|
+
return isinstance(right, (Real, QuantumScalar)) or is_classical_integer(right)
|
42
|
+
if isinstance(left, (ClassicalArray, ClassicalTuple)):
|
43
|
+
if inequality:
|
44
|
+
return False
|
45
|
+
if not isinstance(right, (ClassicalArray, ClassicalTuple)):
|
46
|
+
return False
|
47
|
+
return list_allowed(element_types(left) + element_types(right))
|
48
|
+
if isinstance(left, TypeName):
|
49
|
+
return (
|
50
|
+
left.has_classical_struct_decl
|
51
|
+
and not inequality
|
52
|
+
and isinstance(right, TypeName)
|
53
|
+
and left.name == right.name
|
54
|
+
) or (
|
55
|
+
left.is_enum
|
56
|
+
and (
|
57
|
+
isinstance(right, (Integer, Real, QuantumScalar))
|
58
|
+
or (isinstance(right, TypeName) and right.is_enum)
|
59
|
+
)
|
60
|
+
)
|
61
|
+
if isinstance(left, QuantumScalar):
|
62
|
+
return isinstance(right, (Bool, Real, QuantumScalar)) or is_classical_integer(
|
63
|
+
right
|
64
|
+
)
|
65
|
+
raise ClassiqInternalExpansionError
|
66
|
+
|
67
|
+
|
68
|
+
def eval_compare(expr_val: QmodAnnotatedExpression, node: ast.Compare) -> None:
|
69
|
+
left = node.left
|
70
|
+
if len(node.ops) != 1 or len(node.comparators) != 1:
|
71
|
+
raise ClassiqExpansionError("Multi-compare expressions are not supported")
|
72
|
+
right = node.comparators[0]
|
73
|
+
op = node.ops[0]
|
74
|
+
|
75
|
+
left_type = expr_val.get_type(left)
|
76
|
+
right_type = expr_val.get_type(right)
|
77
|
+
if not comparison_allowed(left_type, right_type, not isinstance(op, ast.Eq)):
|
78
|
+
raise ClassiqExpansionError(
|
79
|
+
f"Cannot compare {get_qmod_type_name(left_type)} {ast.unparse(left)!r} "
|
80
|
+
f"and {get_qmod_type_name(right_type)} {ast.unparse(right)!r}"
|
81
|
+
)
|
82
|
+
qmod_type: QmodType
|
83
|
+
if not is_classical_type(left_type) or not is_classical_type(right_type):
|
84
|
+
qmod_type = QuantumBit()
|
85
|
+
else:
|
86
|
+
qmod_type = Bool()
|
87
|
+
expr_val.set_type(node, qmod_type)
|
88
|
+
|
89
|
+
if not expr_val.has_value(left) or not expr_val.has_value(right):
|
90
|
+
return
|
91
|
+
|
92
|
+
left_value = expr_val.get_value(left)
|
93
|
+
right_value = expr_val.get_value(right)
|
94
|
+
if isinstance(op, ast.Eq):
|
95
|
+
expr_val.set_value(node, left_value == right_value)
|
96
|
+
elif isinstance(op, ast.NotEq):
|
97
|
+
expr_val.set_value(node, left_value != right_value)
|
98
|
+
elif isinstance(op, ast.Lt):
|
99
|
+
expr_val.set_value(node, left_value < right_value)
|
100
|
+
elif isinstance(op, ast.Gt):
|
101
|
+
expr_val.set_value(node, left_value > right_value)
|
102
|
+
elif isinstance(op, ast.LtE):
|
103
|
+
expr_val.set_value(node, left_value <= right_value)
|
104
|
+
elif isinstance(op, ast.GtE):
|
105
|
+
expr_val.set_value(node, left_value >= right_value)
|
106
|
+
else:
|
107
|
+
raise ClassiqExpansionError(f"Unsupported comparison {type(op).__name__!r}")
|
@@ -0,0 +1,67 @@
|
|
1
|
+
import ast
|
2
|
+
from enum import IntEnum
|
3
|
+
from typing import Any
|
4
|
+
|
5
|
+
import sympy
|
6
|
+
|
7
|
+
from classiq.interface.exceptions import (
|
8
|
+
ClassiqExpansionError,
|
9
|
+
)
|
10
|
+
from classiq.interface.generator.functions.classical_type import Bool, Integer, Real
|
11
|
+
from classiq.interface.generator.functions.type_name import Enum
|
12
|
+
|
13
|
+
from classiq.evaluators.qmod_annotated_expression import QmodAnnotatedExpression
|
14
|
+
from classiq.evaluators.qmod_node_evaluators.utils import SYMPY_SYMBOLS, QmodType
|
15
|
+
|
16
|
+
|
17
|
+
def eval_enum_member(
|
18
|
+
expr_val: QmodAnnotatedExpression, node: ast.Attribute, enum: type[IntEnum]
|
19
|
+
) -> None:
|
20
|
+
enum_name = enum.__name__
|
21
|
+
enum_members = { # type: ignore[var-annotated]
|
22
|
+
member.name: member for member in list(enum)
|
23
|
+
}
|
24
|
+
attr = node.attr
|
25
|
+
if attr not in enum_members:
|
26
|
+
raise ClassiqExpansionError(f"Enum {enum_name} has no member named {attr!r}")
|
27
|
+
|
28
|
+
expr_val.set_type(node, Enum(name=enum_name))
|
29
|
+
expr_val.set_value(node, enum_members[attr])
|
30
|
+
|
31
|
+
|
32
|
+
def eval_constant(expr_val: QmodAnnotatedExpression, node: ast.Constant) -> None:
|
33
|
+
value = node.value
|
34
|
+
expr_val.set_value(node, value)
|
35
|
+
constant_type: QmodType
|
36
|
+
if isinstance(value, bool):
|
37
|
+
constant_type = Bool()
|
38
|
+
elif isinstance(value, int):
|
39
|
+
constant_type = Integer()
|
40
|
+
elif isinstance(value, (float, complex)):
|
41
|
+
constant_type = Real()
|
42
|
+
else:
|
43
|
+
raise ClassiqExpansionError(f"Unsupported constant {str(value)!r}")
|
44
|
+
expr_val.set_type(node, constant_type)
|
45
|
+
|
46
|
+
|
47
|
+
def try_eval_sympy_constant(expr_val: QmodAnnotatedExpression, node: ast.Name) -> bool:
|
48
|
+
sympy_val = SYMPY_SYMBOLS.get(node.id)
|
49
|
+
if not isinstance(sympy_val, sympy.Expr) or not sympy_val.is_constant():
|
50
|
+
return False
|
51
|
+
constant_type: QmodType
|
52
|
+
constant_value: Any
|
53
|
+
if sympy_val.is_Integer:
|
54
|
+
constant_type = Integer()
|
55
|
+
constant_value = int(sympy_val)
|
56
|
+
elif sympy_val.is_Boolean:
|
57
|
+
constant_type = Bool()
|
58
|
+
constant_value = bool(sympy_val)
|
59
|
+
elif sympy_val.is_imaginary:
|
60
|
+
constant_type = Real()
|
61
|
+
constant_value = complex(sympy_val)
|
62
|
+
else:
|
63
|
+
constant_type = Real()
|
64
|
+
constant_value = float(sympy_val)
|
65
|
+
expr_val.set_type(node, constant_type)
|
66
|
+
expr_val.set_value(node, constant_value)
|
67
|
+
return True
|
@@ -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
|