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.
Files changed (59) hide show
  1. classiq/applications/combinatorial_optimization/combinatorial_problem.py +4 -3
  2. classiq/evaluators/qmod_annotated_expression.py +207 -0
  3. classiq/evaluators/qmod_expression_visitors/__init__.py +0 -0
  4. classiq/evaluators/qmod_expression_visitors/qmod_expression_bwc.py +134 -0
  5. classiq/evaluators/qmod_expression_visitors/qmod_expression_evaluator.py +232 -0
  6. classiq/evaluators/qmod_expression_visitors/qmod_expression_renamer.py +44 -0
  7. classiq/evaluators/qmod_expression_visitors/qmod_expression_simplifier.py +308 -0
  8. classiq/evaluators/qmod_node_evaluators/__init__.py +0 -0
  9. classiq/evaluators/qmod_node_evaluators/attribute_evaluation.py +112 -0
  10. classiq/evaluators/qmod_node_evaluators/binary_op_evaluation.py +132 -0
  11. classiq/evaluators/qmod_node_evaluators/bool_op_evaluation.py +70 -0
  12. classiq/evaluators/qmod_node_evaluators/classical_function_evaluation.py +311 -0
  13. classiq/evaluators/qmod_node_evaluators/compare_evaluation.py +107 -0
  14. classiq/evaluators/qmod_node_evaluators/constant_evaluation.py +67 -0
  15. classiq/evaluators/qmod_node_evaluators/list_evaluation.py +107 -0
  16. classiq/evaluators/qmod_node_evaluators/measurement_evaluation.py +25 -0
  17. classiq/evaluators/qmod_node_evaluators/name_evaluation.py +50 -0
  18. classiq/evaluators/qmod_node_evaluators/struct_instantiation_evaluation.py +66 -0
  19. classiq/evaluators/qmod_node_evaluators/subscript_evaluation.py +225 -0
  20. classiq/evaluators/qmod_node_evaluators/unary_op_evaluation.py +58 -0
  21. classiq/evaluators/qmod_node_evaluators/utils.py +80 -0
  22. classiq/execution/execution_session.py +4 -0
  23. classiq/interface/_version.py +1 -1
  24. classiq/interface/analyzer/analysis_params.py +1 -1
  25. classiq/interface/analyzer/result.py +1 -1
  26. classiq/interface/executor/quantum_code.py +2 -2
  27. classiq/interface/generator/arith/arithmetic_expression_validator.py +5 -1
  28. classiq/interface/generator/arith/binary_ops.py +43 -51
  29. classiq/interface/generator/arith/number_utils.py +3 -2
  30. classiq/interface/generator/arith/register_user_input.py +15 -0
  31. classiq/interface/generator/arith/unary_ops.py +32 -28
  32. classiq/interface/generator/expressions/expression_types.py +2 -2
  33. classiq/interface/generator/expressions/proxies/classical/qmod_struct_instance.py +7 -0
  34. classiq/interface/generator/functions/classical_function_declaration.py +0 -4
  35. classiq/interface/generator/functions/classical_type.py +0 -32
  36. classiq/interface/generator/generated_circuit_data.py +2 -0
  37. classiq/interface/generator/quantum_program.py +6 -1
  38. classiq/interface/generator/synthesis_metadata/synthesis_execution_data.py +29 -0
  39. classiq/interface/ide/operation_registry.py +2 -2
  40. classiq/interface/ide/visual_model.py +22 -1
  41. classiq/interface/model/quantum_type.py +67 -33
  42. classiq/model_expansions/arithmetic.py +115 -0
  43. classiq/model_expansions/arithmetic_compute_result_attrs.py +71 -0
  44. classiq/model_expansions/atomic_expression_functions_defs.py +5 -5
  45. classiq/model_expansions/generative_functions.py +15 -2
  46. classiq/model_expansions/interpreters/base_interpreter.py +7 -0
  47. classiq/model_expansions/interpreters/generative_interpreter.py +2 -0
  48. classiq/model_expansions/quantum_operations/call_emitter.py +5 -2
  49. classiq/model_expansions/scope_initialization.py +2 -0
  50. classiq/model_expansions/sympy_conversion/sympy_to_python.py +1 -1
  51. classiq/model_expansions/transformers/type_modifier_inference.py +5 -0
  52. classiq/model_expansions/visitors/boolean_expression_transformers.py +1 -1
  53. classiq/qmod/builtins/operations.py +1 -1
  54. classiq/qmod/declaration_inferrer.py +5 -3
  55. classiq/qmod/write_qmod.py +3 -1
  56. {classiq-0.85.0.dist-info → classiq-0.86.0.dist-info}/METADATA +1 -1
  57. {classiq-0.85.0.dist-info → classiq-0.86.0.dist-info}/RECORD +59 -37
  58. /classiq/{model_expansions/sympy_conversion/arithmetics.py → evaluators/qmod_expression_visitors/sympy_wrappers.py} +0 -0
  59. {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