classiq 0.86.1__py3-none-any.whl → 0.87.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/__init__.py +2 -0
- classiq/applications/chemistry/hartree_fock.py +5 -1
- classiq/applications/chemistry/op_utils.py +2 -2
- classiq/applications/combinatorial_helpers/encoding_mapping.py +11 -15
- classiq/applications/combinatorial_helpers/encoding_utils.py +6 -6
- classiq/applications/combinatorial_helpers/memory.py +4 -4
- classiq/applications/combinatorial_helpers/optimization_model.py +5 -5
- classiq/applications/combinatorial_helpers/pauli_helpers/pauli_utils.py +6 -10
- classiq/applications/combinatorial_helpers/pyomo_utils.py +27 -26
- classiq/applications/combinatorial_helpers/sympy_utils.py +2 -2
- classiq/applications/combinatorial_helpers/transformations/encoding.py +4 -6
- classiq/applications/combinatorial_helpers/transformations/fixed_variables.py +4 -4
- classiq/applications/combinatorial_helpers/transformations/ising_converter.py +2 -2
- classiq/applications/combinatorial_helpers/transformations/penalty_support.py +3 -3
- classiq/applications/combinatorial_optimization/combinatorial_problem.py +4 -0
- classiq/applications/hamiltonian/pauli_decomposition.py +33 -1
- classiq/evaluators/argument_types.py +15 -6
- classiq/evaluators/parameter_types.py +43 -39
- classiq/evaluators/qmod_annotated_expression.py +88 -11
- classiq/evaluators/qmod_expression_visitors/out_of_place_node_transformer.py +19 -0
- classiq/evaluators/qmod_expression_visitors/qmod_expression_evaluator.py +54 -11
- classiq/evaluators/qmod_expression_visitors/qmod_expression_renamer.py +40 -25
- classiq/evaluators/qmod_expression_visitors/qmod_expression_simplifier.py +29 -59
- classiq/evaluators/qmod_node_evaluators/attribute_evaluation.py +12 -5
- classiq/evaluators/qmod_node_evaluators/binary_op_evaluation.py +175 -28
- classiq/evaluators/qmod_node_evaluators/classical_function_evaluation.py +21 -14
- classiq/evaluators/qmod_node_evaluators/compare_evaluation.py +9 -5
- classiq/evaluators/qmod_node_evaluators/constant_evaluation.py +20 -1
- classiq/evaluators/qmod_node_evaluators/min_max_evaluation.py +97 -0
- classiq/evaluators/qmod_node_evaluators/name_evaluation.py +11 -26
- classiq/evaluators/qmod_node_evaluators/numeric_attrs_utils.py +56 -0
- classiq/evaluators/qmod_node_evaluators/piecewise_evaluation.py +40 -0
- classiq/evaluators/qmod_node_evaluators/struct_instantiation_evaluation.py +2 -3
- classiq/evaluators/qmod_node_evaluators/subscript_evaluation.py +48 -21
- classiq/evaluators/qmod_node_evaluators/unary_op_evaluation.py +53 -9
- classiq/evaluators/qmod_node_evaluators/utils.py +27 -5
- classiq/evaluators/qmod_type_inference/classical_type_inference.py +188 -0
- classiq/evaluators/qmod_type_inference/quantum_type_inference.py +292 -0
- classiq/evaluators/quantum_type_utils.py +0 -131
- classiq/execution/execution_session.py +1 -1
- classiq/execution/qnn.py +4 -1
- classiq/execution/user_budgets.py +1 -1
- classiq/interface/_version.py +1 -1
- classiq/interface/backend/backend_preferences.py +10 -30
- classiq/interface/backend/quantum_backend_providers.py +63 -52
- classiq/interface/generator/arith/binary_ops.py +107 -115
- classiq/interface/generator/arith/extremum_operations.py +33 -45
- classiq/interface/generator/arith/number_utils.py +4 -1
- classiq/interface/generator/circuit_code/types_and_constants.py +0 -9
- classiq/interface/generator/compiler_keywords.py +2 -0
- classiq/interface/generator/function_param_list.py +133 -5
- classiq/interface/generator/functions/classical_type.py +59 -2
- classiq/interface/generator/functions/qmod_python_interface.py +15 -0
- classiq/interface/generator/functions/type_name.py +6 -0
- classiq/interface/generator/model/preferences/preferences.py +1 -17
- classiq/interface/generator/quantum_program.py +1 -13
- classiq/interface/helpers/model_normalizer.py +2 -2
- classiq/interface/helpers/text_utils.py +7 -2
- classiq/interface/interface_version.py +1 -1
- classiq/interface/model/classical_if.py +40 -0
- classiq/interface/model/handle_binding.py +28 -16
- classiq/interface/model/quantum_type.py +61 -2
- classiq/interface/pretty_print/expression_to_qmod.py +24 -11
- classiq/interface/pyomo_extension/__init__.py +0 -4
- classiq/interface/pyomo_extension/pyomo_sympy_bimap.py +2 -2
- classiq/model_expansions/arithmetic.py +43 -1
- classiq/model_expansions/arithmetic_compute_result_attrs.py +255 -0
- classiq/model_expansions/capturing/captured_vars.py +2 -5
- classiq/model_expansions/quantum_operations/allocate.py +22 -15
- classiq/model_expansions/quantum_operations/arithmetic/explicit_boolean_expressions.py +1 -3
- classiq/model_expansions/quantum_operations/assignment_result_processor.py +52 -71
- classiq/model_expansions/quantum_operations/bind.py +15 -7
- classiq/model_expansions/quantum_operations/call_emitter.py +2 -10
- classiq/model_expansions/quantum_operations/classical_var_emitter.py +6 -0
- classiq/open_library/functions/__init__.py +3 -0
- classiq/open_library/functions/lcu.py +117 -0
- classiq/qmod/builtins/enums.py +2 -2
- classiq/qmod/builtins/structs.py +33 -18
- classiq/qmod/pretty_print/expression_to_python.py +7 -9
- {classiq-0.86.1.dist-info → classiq-0.87.0.dist-info}/METADATA +3 -3
- {classiq-0.86.1.dist-info → classiq-0.87.0.dist-info}/RECORD +83 -89
- classiq/interface/generator/amplitude_estimation.py +0 -34
- classiq/interface/generator/function_param_list_without_self_reference.py +0 -160
- classiq/interface/generator/grover_diffuser.py +0 -93
- classiq/interface/generator/grover_operator.py +0 -106
- classiq/interface/generator/oracles/__init__.py +0 -3
- classiq/interface/generator/oracles/arithmetic_oracle.py +0 -82
- classiq/interface/generator/oracles/custom_oracle.py +0 -65
- classiq/interface/generator/oracles/oracle_abc.py +0 -76
- classiq/interface/generator/oracles/oracle_function_param_list.py +0 -6
- classiq/interface/generator/piecewise_linear_amplitude_loading.py +0 -165
- classiq/interface/generator/qpe.py +0 -169
- classiq/interface/grover/grover_modelling_params.py +0 -13
- classiq/model_expansions/transformers/var_splitter.py +0 -224
- /classiq/{interface/grover → evaluators/qmod_type_inference}/__init__.py +0 -0
- {classiq-0.86.1.dist-info → classiq-0.87.0.dist-info}/WHEEL +0 -0
@@ -1,5 +1,6 @@
|
|
1
1
|
from collections.abc import Sequence
|
2
2
|
|
3
|
+
from classiq.interface.exceptions import ClassiqExpansionError
|
3
4
|
from classiq.interface.generator.functions.port_declaration import (
|
4
5
|
PortDeclarationDirection,
|
5
6
|
)
|
@@ -8,7 +9,9 @@ from classiq.interface.model.port_declaration import AnonPortDeclaration
|
|
8
9
|
from classiq.interface.model.quantum_function_declaration import AnonPositionalArg
|
9
10
|
from classiq.interface.model.quantum_type import QuantumNumeric
|
10
11
|
|
11
|
-
from classiq.evaluators.
|
12
|
+
from classiq.evaluators.qmod_type_inference.quantum_type_inference import (
|
13
|
+
inject_quantum_type_attributes_inplace,
|
14
|
+
)
|
12
15
|
from classiq.model_expansions.scope import Evaluated, QuantumVariable
|
13
16
|
|
14
17
|
|
@@ -36,11 +39,17 @@ def add_information_from_output_arguments(
|
|
36
39
|
if parameter.direction != PortDeclarationDirection.Output:
|
37
40
|
continue
|
38
41
|
|
39
|
-
if
|
40
|
-
|
41
|
-
|
42
|
-
argument_as_quantum_symbol.quantum_type
|
43
|
-
|
42
|
+
if (
|
43
|
+
parameter.quantum_type.is_evaluated
|
44
|
+
and not inject_quantum_type_attributes_inplace(
|
45
|
+
parameter.quantum_type, argument_as_quantum_symbol.quantum_type
|
46
|
+
)
|
47
|
+
):
|
48
|
+
raise ClassiqExpansionError(
|
49
|
+
f"Argument {str(argument_as_quantum_symbol)!r} of type "
|
50
|
+
f"{argument_as_quantum_symbol.quantum_type.qmod_type_name} is "
|
51
|
+
f"incompatible with parameter {parameter.name!r} of type "
|
52
|
+
f"{parameter.quantum_type.qmod_type_name}"
|
44
53
|
)
|
45
54
|
|
46
55
|
|
@@ -45,11 +45,8 @@ from classiq.evaluators.classical_expression import (
|
|
45
45
|
from classiq.evaluators.classical_type_inference import (
|
46
46
|
infer_classical_type,
|
47
47
|
)
|
48
|
-
from classiq.evaluators.
|
49
|
-
|
50
|
-
set_element_type,
|
51
|
-
set_length,
|
52
|
-
set_size,
|
48
|
+
from classiq.evaluators.qmod_type_inference.quantum_type_inference import (
|
49
|
+
inject_quantum_type_attributes,
|
53
50
|
)
|
54
51
|
from classiq.model_expansions.closure import FunctionClosure
|
55
52
|
from classiq.model_expansions.scope import (
|
@@ -100,7 +97,7 @@ def _update_scope(
|
|
100
97
|
quantum_var = argument.as_type(QuantumVariable)
|
101
98
|
casted_argument = _cast(
|
102
99
|
parameter.quantum_type,
|
103
|
-
quantum_var
|
100
|
+
quantum_var,
|
104
101
|
parameter.name,
|
105
102
|
)
|
106
103
|
closure.scope[parameter.name] = Evaluated(
|
@@ -134,12 +131,17 @@ def _update_operand_signature_environment(
|
|
134
131
|
|
135
132
|
|
136
133
|
def _cast(
|
137
|
-
parameter_type: QuantumType,
|
134
|
+
parameter_type: QuantumType, argument: QuantumVariable, param_name: str
|
138
135
|
) -> QuantumSymbol:
|
139
|
-
|
140
|
-
|
136
|
+
updated_type = inject_quantum_type_attributes(argument.quantum_type, parameter_type)
|
137
|
+
if updated_type is None:
|
138
|
+
raise ClassiqExpansionError(
|
139
|
+
f"Argument {str(argument)!r} of type "
|
140
|
+
f"{argument.quantum_type.qmod_type_name} is incompatible with parameter "
|
141
|
+
f"{param_name!r} of type {parameter_type.qmod_type_name}"
|
142
|
+
)
|
141
143
|
return QuantumSymbol(
|
142
|
-
handle=HandleBinding(name=param_name), quantum_type=
|
144
|
+
handle=HandleBinding(name=param_name), quantum_type=updated_type
|
143
145
|
)
|
144
146
|
|
145
147
|
|
@@ -168,11 +170,17 @@ def _evaluate_type_from_arg(
|
|
168
170
|
parameter.quantum_type.model_copy(), inner_scope, parameter.name
|
169
171
|
)
|
170
172
|
if parameter.direction != PortDeclarationDirection.Output:
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
parameter.name,
|
173
|
+
arg_type = argument.as_type(QuantumVariable).quantum_type
|
174
|
+
updated_output_quantum_type = inject_quantum_type_attributes(
|
175
|
+
arg_type, updated_quantum_type
|
175
176
|
)
|
177
|
+
if updated_output_quantum_type is None:
|
178
|
+
raise ClassiqExpansionError(
|
179
|
+
f"Argument {str(argument.value)!r} of type "
|
180
|
+
f"{arg_type.qmod_type_name} is incompatible with parameter "
|
181
|
+
f"{parameter.name!r} of type {updated_quantum_type.qmod_type_name}"
|
182
|
+
)
|
183
|
+
updated_quantum_type = updated_output_quantum_type
|
176
184
|
return parameter.model_copy(update={"quantum_type": updated_quantum_type})
|
177
185
|
|
178
186
|
|
@@ -196,7 +204,7 @@ def _evaluate_qarray_in_quantum_symbol(
|
|
196
204
|
new_element_type = evaluate_type_in_quantum_symbol(
|
197
205
|
type_to_update.element_type, scope, param_name
|
198
206
|
)
|
199
|
-
|
207
|
+
type_to_update.element_type = new_element_type
|
200
208
|
if type_to_update.length is not None:
|
201
209
|
new_length = _eval_expr(
|
202
210
|
type_to_update.length,
|
@@ -207,13 +215,26 @@ def _evaluate_qarray_in_quantum_symbol(
|
|
207
215
|
param_name,
|
208
216
|
)
|
209
217
|
if new_length is not None:
|
210
|
-
|
218
|
+
type_to_update.length = Expression(expr=str(new_length))
|
211
219
|
return type_to_update
|
212
220
|
|
213
221
|
|
214
222
|
def _evaluate_qnum_in_quantum_symbol(
|
215
223
|
type_to_update: QuantumNumeric, scope: Scope, param_name: str
|
216
224
|
) -> QuantumNumeric:
|
225
|
+
if type_to_update.size is None:
|
226
|
+
return type_to_update
|
227
|
+
new_size = _eval_expr(
|
228
|
+
type_to_update.size,
|
229
|
+
scope,
|
230
|
+
int,
|
231
|
+
type_to_update.type_name,
|
232
|
+
"size",
|
233
|
+
param_name,
|
234
|
+
)
|
235
|
+
if new_size is not None:
|
236
|
+
type_to_update.size = Expression(expr=str(new_size))
|
237
|
+
|
217
238
|
if type_to_update.is_signed is not None:
|
218
239
|
new_is_sign = _eval_expr(
|
219
240
|
type_to_update.is_signed,
|
@@ -225,6 +246,9 @@ def _evaluate_qnum_in_quantum_symbol(
|
|
225
246
|
)
|
226
247
|
if new_is_sign is not None:
|
227
248
|
type_to_update.is_signed = Expression(expr=str(new_is_sign))
|
249
|
+
else:
|
250
|
+
type_to_update.is_signed = Expression(expr="False")
|
251
|
+
|
228
252
|
if type_to_update.fraction_digits is not None:
|
229
253
|
new_fraction_digits = _eval_expr(
|
230
254
|
type_to_update.fraction_digits,
|
@@ -236,17 +260,9 @@ def _evaluate_qnum_in_quantum_symbol(
|
|
236
260
|
)
|
237
261
|
if new_fraction_digits is not None:
|
238
262
|
type_to_update.fraction_digits = Expression(expr=str(new_fraction_digits))
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
scope,
|
243
|
-
int,
|
244
|
-
type_to_update.type_name,
|
245
|
-
"size",
|
246
|
-
param_name,
|
247
|
-
)
|
248
|
-
if new_size is not None:
|
249
|
-
set_size(type_to_update, new_size, param_name)
|
263
|
+
else:
|
264
|
+
type_to_update.fraction_digits = Expression(expr="0")
|
265
|
+
|
250
266
|
return type_to_update
|
251
267
|
|
252
268
|
|
@@ -302,18 +318,6 @@ def evaluate_types_in_quantum_symbols(
|
|
302
318
|
]
|
303
319
|
|
304
320
|
|
305
|
-
def _inject_quantum_arg_info_to_type(
|
306
|
-
parameter_type: QuantumType, argument_type: QuantumType, param_name: str
|
307
|
-
) -> QuantumType:
|
308
|
-
if argument_type.has_size_in_bits:
|
309
|
-
copy_type_information(
|
310
|
-
argument_type,
|
311
|
-
parameter_type,
|
312
|
-
param_name,
|
313
|
-
)
|
314
|
-
return parameter_type
|
315
|
-
|
316
|
-
|
317
321
|
def evaluate_type_in_classical_symbol(
|
318
322
|
type_to_update: ClassicalType, scope: Scope, param_name: str
|
319
323
|
) -> ClassicalType:
|
@@ -1,10 +1,22 @@
|
|
1
1
|
import ast
|
2
2
|
from collections.abc import Sequence
|
3
3
|
from dataclasses import dataclass
|
4
|
+
from enum import Enum
|
4
5
|
from typing import Any, Union, cast
|
5
6
|
|
7
|
+
from classiq.interface.exceptions import ClassiqInternalExpansionError
|
8
|
+
from classiq.interface.generator.expressions.proxies.classical.qmod_struct_instance import (
|
9
|
+
QmodStructInstance,
|
10
|
+
)
|
11
|
+
from classiq.interface.generator.functions.classical_type import (
|
12
|
+
ClassicalType,
|
13
|
+
)
|
6
14
|
from classiq.interface.model.handle_binding import HandleBinding
|
15
|
+
from classiq.interface.model.quantum_type import QuantumType
|
7
16
|
|
17
|
+
from classiq.evaluators.qmod_expression_visitors.out_of_place_node_transformer import (
|
18
|
+
OutOfPlaceNodeTransformer,
|
19
|
+
)
|
8
20
|
from classiq.evaluators.qmod_node_evaluators.utils import QmodType, is_classical_type
|
9
21
|
|
10
22
|
QmodExprNodeId = int
|
@@ -27,13 +39,31 @@ class ConcatenationAnnotation:
|
|
27
39
|
elements: list[QmodExprNodeId]
|
28
40
|
|
29
41
|
|
30
|
-
|
42
|
+
def qmod_val_to_str(val: Any) -> str:
|
43
|
+
if isinstance(val, QmodStructInstance):
|
44
|
+
fields = ", ".join(
|
45
|
+
f"{field_name}={qmod_val_to_str(field_val)}"
|
46
|
+
for field_name, field_val in val.fields.items()
|
47
|
+
)
|
48
|
+
return f"{val.struct_declaration.name}({fields})"
|
49
|
+
if isinstance(val, list):
|
50
|
+
return f"[{', '.join(qmod_val_to_str(item) for item in val)}]"
|
51
|
+
if isinstance(val, Enum):
|
52
|
+
return Enum.__str__(val)
|
53
|
+
if isinstance(val, (int, float, bool, complex)):
|
54
|
+
return str(val)
|
55
|
+
raise ClassiqInternalExpansionError(
|
56
|
+
f"Unrecognized value {str(val)!r} of type {type(val)}"
|
57
|
+
)
|
58
|
+
|
59
|
+
|
60
|
+
class _ExprInliner(OutOfPlaceNodeTransformer):
|
31
61
|
def __init__(self, expr_val: "QmodAnnotatedExpression") -> None:
|
32
62
|
self._expr_val = expr_val
|
33
63
|
|
34
64
|
def visit(self, node: ast.AST) -> Any:
|
35
65
|
if self._expr_val.has_value(node):
|
36
|
-
return ast.Name(id=
|
66
|
+
return ast.Name(id=qmod_val_to_str(self._expr_val.get_value(node)))
|
37
67
|
if self._expr_val.has_var(node):
|
38
68
|
return ast.Name(id=str(self._expr_val.get_var(node)))
|
39
69
|
return super().visit(node)
|
@@ -53,7 +83,7 @@ class QmodAnnotatedExpression:
|
|
53
83
|
] = {}
|
54
84
|
self._concatenations: dict[QmodExprNodeId, ConcatenationAnnotation] = {}
|
55
85
|
|
56
|
-
def
|
86
|
+
def __str__(self) -> str:
|
57
87
|
return ast.unparse(_ExprInliner(self).visit(self.root))
|
58
88
|
|
59
89
|
def has_node(self, node_id: QmodExprNodeId) -> bool:
|
@@ -91,6 +121,22 @@ class QmodAnnotatedExpression:
|
|
91
121
|
node = id(node)
|
92
122
|
return self._types[node]
|
93
123
|
|
124
|
+
def get_quantum_type(self, node: Union[ast.AST, QmodExprNodeId]) -> QuantumType:
|
125
|
+
if isinstance(node, ast.AST):
|
126
|
+
node = id(node)
|
127
|
+
qmod_type = self._types[node]
|
128
|
+
if is_classical_type(qmod_type):
|
129
|
+
raise ClassiqInternalExpansionError
|
130
|
+
return cast(QuantumType, qmod_type)
|
131
|
+
|
132
|
+
def get_classical_type(self, node: Union[ast.AST, QmodExprNodeId]) -> ClassicalType:
|
133
|
+
if isinstance(node, ast.AST):
|
134
|
+
node = id(node)
|
135
|
+
qmod_type = self._types[node]
|
136
|
+
if not is_classical_type(qmod_type):
|
137
|
+
raise ClassiqInternalExpansionError
|
138
|
+
return cast(ClassicalType, qmod_type)
|
139
|
+
|
94
140
|
def set_var(self, node: Union[ast.AST, QmodExprNodeId], var: HandleBinding) -> None:
|
95
141
|
var = var.collapse()
|
96
142
|
if isinstance(node, ast.AST):
|
@@ -183,14 +229,15 @@ class QmodAnnotatedExpression:
|
|
183
229
|
) -> None:
|
184
230
|
if isinstance(node, ast.AST):
|
185
231
|
node = id(node)
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
232
|
+
inlined_elements: list[QmodExprNodeId] = []
|
233
|
+
for element in elements:
|
234
|
+
if isinstance(element, ast.AST):
|
235
|
+
element = id(element)
|
236
|
+
if element not in self._concatenations:
|
237
|
+
inlined_elements.append(element)
|
238
|
+
else:
|
239
|
+
inlined_elements.extend(self._concatenations.pop(element).elements)
|
240
|
+
self._concatenations[node] = ConcatenationAnnotation(elements=inlined_elements)
|
194
241
|
|
195
242
|
def has_concatenation(self, node: Union[ast.AST, QmodExprNodeId]) -> bool:
|
196
243
|
if isinstance(node, ast.AST):
|
@@ -205,3 +252,33 @@ class QmodAnnotatedExpression:
|
|
205
252
|
|
206
253
|
def get_quantum_vars(self) -> dict[QmodExprNodeId, HandleBinding]:
|
207
254
|
return self._quantum_vars
|
255
|
+
|
256
|
+
def clear_node_data(self, node: Union[ast.AST, QmodExprNodeId]) -> None:
|
257
|
+
if isinstance(node, ast.AST):
|
258
|
+
node = id(node)
|
259
|
+
self._node_mapping.pop(node, None)
|
260
|
+
self._values.pop(node, None)
|
261
|
+
self._types.pop(node, None)
|
262
|
+
self._classical_vars.pop(node, None)
|
263
|
+
self._quantum_vars.pop(node, None)
|
264
|
+
qs = self._quantum_subscripts.pop(node, None)
|
265
|
+
if qs is not None:
|
266
|
+
self.clear_node_data(qs.value)
|
267
|
+
self.clear_node_data(qs.index)
|
268
|
+
qta = self._quantum_type_attrs.pop(node, None)
|
269
|
+
if qta is not None:
|
270
|
+
self.clear_node_data(qta.value)
|
271
|
+
cnct = self._concatenations.pop(node, None)
|
272
|
+
if cnct is not None:
|
273
|
+
for element in cnct.elements:
|
274
|
+
self.clear_node_data(element)
|
275
|
+
|
276
|
+
def _add_data_from(self, other: "QmodAnnotatedExpression") -> None:
|
277
|
+
self._node_mapping |= other._node_mapping
|
278
|
+
self._values |= other._values
|
279
|
+
self._types |= other._types
|
280
|
+
self._classical_vars |= other._classical_vars
|
281
|
+
self._quantum_vars |= other._quantum_vars
|
282
|
+
self._quantum_subscripts |= other._quantum_subscripts
|
283
|
+
self._quantum_type_attrs |= other._quantum_type_attrs
|
284
|
+
self._concatenations |= other._concatenations
|
@@ -0,0 +1,19 @@
|
|
1
|
+
import ast
|
2
|
+
from typing import Any
|
3
|
+
|
4
|
+
|
5
|
+
class OutOfPlaceNodeTransformer(ast.NodeVisitor):
|
6
|
+
def generic_visit(self, node: ast.AST) -> ast.AST:
|
7
|
+
new_fields: dict[str, Any] = {}
|
8
|
+
for field, field_val in ast.iter_fields(node):
|
9
|
+
if isinstance(field_val, list):
|
10
|
+
new_fields[field] = [
|
11
|
+
new_val
|
12
|
+
for item in field_val
|
13
|
+
if (new_val := self.visit(item)) is not None
|
14
|
+
]
|
15
|
+
elif isinstance(field_val, ast.AST):
|
16
|
+
new_fields[field] = self.visit(field_val)
|
17
|
+
else:
|
18
|
+
new_fields[field] = field_val
|
19
|
+
return type(node)(**new_fields)
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import ast
|
2
2
|
from collections.abc import Mapping, Sequence
|
3
3
|
from enum import IntEnum
|
4
|
-
from typing import Any, Callable, Optional
|
4
|
+
from typing import Any, Callable, Optional, cast
|
5
5
|
|
6
6
|
from classiq.interface.exceptions import (
|
7
7
|
ClassiqExpansionError,
|
@@ -31,13 +31,16 @@ from classiq.evaluators.qmod_node_evaluators.compare_evaluation import eval_comp
|
|
31
31
|
from classiq.evaluators.qmod_node_evaluators.constant_evaluation import (
|
32
32
|
eval_constant,
|
33
33
|
eval_enum_member,
|
34
|
+
try_eval_qmod_literal,
|
34
35
|
try_eval_sympy_constant,
|
35
36
|
)
|
36
37
|
from classiq.evaluators.qmod_node_evaluators.list_evaluation import eval_list
|
37
38
|
from classiq.evaluators.qmod_node_evaluators.measurement_evaluation import (
|
38
39
|
eval_measurement,
|
39
40
|
)
|
41
|
+
from classiq.evaluators.qmod_node_evaluators.min_max_evaluation import eval_min_max_op
|
40
42
|
from classiq.evaluators.qmod_node_evaluators.name_evaluation import eval_name
|
43
|
+
from classiq.evaluators.qmod_node_evaluators.piecewise_evaluation import eval_piecewise
|
41
44
|
from classiq.evaluators.qmod_node_evaluators.struct_instantiation_evaluation import (
|
42
45
|
eval_struct_instantiation,
|
43
46
|
)
|
@@ -74,6 +77,7 @@ class QmodExpressionEvaluator(ast.NodeVisitor):
|
|
74
77
|
self,
|
75
78
|
expr_val: QmodAnnotatedExpression,
|
76
79
|
*,
|
80
|
+
treat_qnum_as_float: bool = False,
|
77
81
|
machine_precision: int = DEFAULT_MACHINE_PRECISION,
|
78
82
|
classical_struct_declarations: Optional[Sequence[StructDeclaration]] = None,
|
79
83
|
enum_declarations: Optional[Sequence[EnumDeclaration]] = None,
|
@@ -84,6 +88,7 @@ class QmodExpressionEvaluator(ast.NodeVisitor):
|
|
84
88
|
scope: Optional[dict[str, Any]] = None,
|
85
89
|
) -> None:
|
86
90
|
self._expr_val = expr_val
|
91
|
+
self._treat_qnum_as_float = treat_qnum_as_float
|
87
92
|
self._machine_precision = machine_precision
|
88
93
|
self._classical_struct_decls = nameables_to_dict(
|
89
94
|
classical_struct_declarations or []
|
@@ -109,22 +114,19 @@ class QmodExpressionEvaluator(ast.NodeVisitor):
|
|
109
114
|
|
110
115
|
def visit_BinOp(self, node: ast.BinOp) -> None:
|
111
116
|
super().generic_visit(node)
|
112
|
-
eval_binary_op(
|
117
|
+
eval_binary_op(
|
118
|
+
self._expr_val, node, self._treat_qnum_as_float, self._machine_precision
|
119
|
+
)
|
113
120
|
|
114
121
|
def visit_UnaryOp(self, node: ast.UnaryOp) -> None:
|
115
122
|
super().generic_visit(node)
|
116
|
-
eval_unary_op(self._expr_val, node)
|
123
|
+
eval_unary_op(self._expr_val, node, self._machine_precision)
|
117
124
|
|
118
125
|
def visit_Compare(self, node: ast.Compare) -> None:
|
119
126
|
super().generic_visit(node)
|
120
127
|
eval_compare(self._expr_val, node)
|
121
128
|
|
122
129
|
def visit_Call(self, node: ast.Call) -> None:
|
123
|
-
for arg in node.args:
|
124
|
-
self.visit(arg)
|
125
|
-
for kwarg in node.keywords:
|
126
|
-
self.visit(kwarg)
|
127
|
-
|
128
130
|
func = node.func
|
129
131
|
if not isinstance(func, ast.Name):
|
130
132
|
raise ClassiqExpansionError(
|
@@ -132,16 +134,37 @@ class QmodExpressionEvaluator(ast.NodeVisitor):
|
|
132
134
|
)
|
133
135
|
func_name = func.id
|
134
136
|
|
137
|
+
if func_name == "Piecewise":
|
138
|
+
self._eval_piecewise(node)
|
139
|
+
return
|
140
|
+
|
141
|
+
for arg in node.args:
|
142
|
+
self.visit(arg)
|
143
|
+
for kwarg in node.keywords:
|
144
|
+
self.visit(kwarg)
|
145
|
+
|
135
146
|
if func_name == "measure":
|
136
147
|
eval_measurement(self._expr_val, node)
|
137
148
|
return
|
138
149
|
|
150
|
+
if func_name in ("min", "max"):
|
151
|
+
eval_min_max_op(
|
152
|
+
self._expr_val,
|
153
|
+
node,
|
154
|
+
func_name,
|
155
|
+
self._treat_qnum_as_float,
|
156
|
+
self._machine_precision,
|
157
|
+
)
|
158
|
+
return
|
159
|
+
|
160
|
+
# FIXME: Remove (CLS-3241)
|
139
161
|
if func_name in self._classical_struct_decls:
|
140
162
|
eval_struct_instantiation(
|
141
163
|
self._expr_val, node, self._classical_struct_decls[func_name]
|
142
164
|
)
|
143
165
|
return
|
144
166
|
|
167
|
+
# FIXME: Remove (CLS-3241)
|
145
168
|
if func_name in self._classical_function_callables:
|
146
169
|
if func_name not in self._classical_function_declarations:
|
147
170
|
raise ClassiqInternalExpansionError
|
@@ -167,6 +190,21 @@ class QmodExpressionEvaluator(ast.NodeVisitor):
|
|
167
190
|
|
168
191
|
raise ClassiqExpansionError(f"{func.id!r} is undefined")
|
169
192
|
|
193
|
+
def _eval_piecewise(self, node: ast.Call) -> None:
|
194
|
+
if (
|
195
|
+
len(node.args) == 0
|
196
|
+
or len(node.keywords) != 0
|
197
|
+
or not all(
|
198
|
+
isinstance(arg, ast.Tuple) and len(arg.elts) == 2 for arg in node.args
|
199
|
+
)
|
200
|
+
):
|
201
|
+
raise ClassiqExpansionError("Malformed Piecewise expression")
|
202
|
+
args = [(arg.elts[0], arg.elts[1]) for arg in cast(list[ast.Tuple], node.args)]
|
203
|
+
for value, cond in args:
|
204
|
+
self.visit(value)
|
205
|
+
self.visit(cond)
|
206
|
+
eval_piecewise(self._expr_val, node, args)
|
207
|
+
|
170
208
|
def visit_Constant(self, node: ast.Constant) -> None:
|
171
209
|
eval_constant(self._expr_val, node)
|
172
210
|
|
@@ -194,12 +232,15 @@ class QmodExpressionEvaluator(ast.NodeVisitor):
|
|
194
232
|
eval_subscript(self._expr_val, node)
|
195
233
|
|
196
234
|
def visit_Name(self, node: ast.Name) -> None:
|
235
|
+
if node.id in self._scope:
|
236
|
+
eval_name(self._expr_val, node, self._scope[node.id])
|
237
|
+
return
|
197
238
|
if try_eval_sympy_constant(self._expr_val, node):
|
198
239
|
return
|
240
|
+
if try_eval_qmod_literal(self._expr_val, node):
|
241
|
+
return
|
199
242
|
|
200
|
-
|
201
|
-
raise ClassiqExpansionError(f"Variable {node.id!r} is undefined")
|
202
|
-
eval_name(self._expr_val, node, self._scope[node.id])
|
243
|
+
raise ClassiqExpansionError(f"Variable {node.id!r} is undefined")
|
203
244
|
|
204
245
|
def visit_List(self, node: ast.List) -> None:
|
205
246
|
super().generic_visit(node)
|
@@ -209,6 +250,7 @@ class QmodExpressionEvaluator(ast.NodeVisitor):
|
|
209
250
|
def evaluate_qmod_expression(
|
210
251
|
expr: str,
|
211
252
|
*,
|
253
|
+
treat_qnum_as_float: bool = False,
|
212
254
|
machine_precision: int = DEFAULT_MACHINE_PRECISION,
|
213
255
|
classical_struct_declarations: Optional[Sequence[StructDeclaration]] = None,
|
214
256
|
enum_declarations: Optional[Sequence[EnumDeclaration]] = None,
|
@@ -222,6 +264,7 @@ def evaluate_qmod_expression(
|
|
222
264
|
expr_value = QmodAnnotatedExpression(expr_ast)
|
223
265
|
QmodExpressionEvaluator(
|
224
266
|
expr_value,
|
267
|
+
treat_qnum_as_float=treat_qnum_as_float,
|
225
268
|
machine_precision=machine_precision,
|
226
269
|
classical_struct_declarations=classical_struct_declarations,
|
227
270
|
enum_declarations=enum_declarations,
|
@@ -1,6 +1,4 @@
|
|
1
|
-
from
|
2
|
-
|
3
|
-
from classiq.interface.model.handle_binding import HandleBinding, NestedHandleBinding
|
1
|
+
from classiq.interface.model.handle_binding import HandleBinding
|
4
2
|
|
5
3
|
from classiq.evaluators.qmod_annotated_expression import (
|
6
4
|
QmodAnnotatedExpression,
|
@@ -8,37 +6,54 @@ from classiq.evaluators.qmod_annotated_expression import (
|
|
8
6
|
)
|
9
7
|
|
10
8
|
|
11
|
-
def
|
9
|
+
def replace_expression_vars(
|
12
10
|
expr_val: QmodAnnotatedExpression,
|
13
11
|
renaming: dict[HandleBinding, HandleBinding],
|
14
|
-
) ->
|
12
|
+
) -> None:
|
15
13
|
if len(renaming) == 0:
|
16
|
-
return
|
14
|
+
return
|
17
15
|
all_vars = expr_val.get_classical_vars() | expr_val.get_quantum_vars()
|
18
16
|
for node_id, var in all_vars.items():
|
19
|
-
renamed_var =
|
20
|
-
|
21
|
-
|
22
|
-
|
17
|
+
renamed_var = var
|
18
|
+
for source, target in renaming.items():
|
19
|
+
if renamed_var.name == source.name:
|
20
|
+
renamed_var = renamed_var.replace_prefix(source, target)
|
21
|
+
if renamed_var is var:
|
22
|
+
continue
|
23
|
+
node_type = expr_val.get_type(node_id)
|
24
|
+
expr_val.clear_node_data(node_id)
|
25
|
+
expr_val.set_type(node_id, node_type)
|
26
|
+
expr_val.set_var(node_id, renamed_var)
|
27
|
+
return
|
23
28
|
|
24
29
|
|
25
|
-
def
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
30
|
+
def replace_expression_type_attrs(
|
31
|
+
expr_val: QmodAnnotatedExpression,
|
32
|
+
renaming: dict[tuple[HandleBinding, str], HandleBinding],
|
33
|
+
) -> None:
|
34
|
+
if len(renaming) == 0:
|
35
|
+
return
|
36
|
+
type_attrs = dict(expr_val.get_quantum_type_attributes())
|
37
|
+
for node_id, ta in type_attrs.items():
|
38
|
+
var = expr_val.get_var(ta.value)
|
39
|
+
renamed_var = var
|
40
|
+
renamed_attr = ta.attr
|
41
|
+
for (source, attr), target in renaming.items():
|
42
|
+
if renamed_attr == attr and renamed_var.name == source.name:
|
43
|
+
renamed_var = renamed_var.replace_prefix(source, target)
|
44
|
+
if renamed_var is var:
|
45
|
+
continue
|
46
|
+
node_type = expr_val.get_type(node_id)
|
47
|
+
expr_val.clear_node_data(node_id)
|
48
|
+
expr_val.set_type(node_id, node_type)
|
49
|
+
expr_val.set_var(node_id, renamed_var)
|
50
|
+
return
|
36
51
|
|
37
52
|
|
38
|
-
def
|
53
|
+
def replace_expression_nodes(
|
39
54
|
expr_val: QmodAnnotatedExpression,
|
40
|
-
renaming: dict[QmodExprNodeId,
|
55
|
+
renaming: dict[QmodExprNodeId, str],
|
41
56
|
) -> str:
|
42
57
|
for node_id, renamed_var in renaming.items():
|
43
|
-
expr_val.set_var(node_id, renamed_var)
|
44
|
-
return expr_val
|
58
|
+
expr_val.set_var(node_id, HandleBinding(name=f"{renamed_var}"))
|
59
|
+
return str(expr_val)
|