classiq 0.86.1__py3-none-any.whl → 0.88.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.
Potentially problematic release.
This version of classiq might be problematic. Click here for more details.
- classiq/__init__.py +2 -0
- classiq/applications/__init__.py +1 -2
- classiq/applications/chemistry/hartree_fock.py +5 -1
- classiq/applications/chemistry/op_utils.py +2 -2
- classiq/applications/combinatorial_helpers/combinatorial_problem_utils.py +1 -1
- 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 +34 -2
- classiq/evaluators/argument_types.py +15 -6
- classiq/evaluators/parameter_types.py +43 -39
- classiq/evaluators/qmod_annotated_expression.py +117 -17
- classiq/evaluators/qmod_expression_visitors/out_of_place_node_transformer.py +19 -0
- classiq/evaluators/qmod_expression_visitors/qmod_expression_bwc.py +0 -5
- classiq/evaluators/qmod_expression_visitors/qmod_expression_evaluator.py +66 -16
- classiq/evaluators/qmod_expression_visitors/qmod_expression_renamer.py +48 -26
- classiq/evaluators/qmod_expression_visitors/qmod_expression_simplifier.py +65 -72
- classiq/evaluators/qmod_node_evaluators/attribute_evaluation.py +13 -6
- classiq/evaluators/qmod_node_evaluators/binary_op_evaluation.py +175 -28
- classiq/evaluators/qmod_node_evaluators/classical_function_evaluation.py +36 -19
- classiq/evaluators/qmod_node_evaluators/compare_evaluation.py +17 -5
- classiq/evaluators/qmod_node_evaluators/constant_evaluation.py +24 -2
- 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 +3 -4
- classiq/evaluators/qmod_node_evaluators/subscript_evaluation.py +51 -24
- classiq/evaluators/qmod_node_evaluators/unary_op_evaluation.py +53 -9
- classiq/evaluators/qmod_node_evaluators/utils.py +28 -6
- classiq/evaluators/qmod_type_inference/classical_type_inference.py +188 -0
- classiq/evaluators/qmod_type_inference/quantum_type_inference.py +330 -0
- classiq/evaluators/quantum_type_utils.py +0 -131
- classiq/evaluators/type_type_match.py +1 -1
- classiq/execution/execution_session.py +18 -3
- 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/execution/primitives.py +1 -0
- classiq/interface/generator/application_apis/__init__.py +0 -1
- 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/expressions/atomic_expression_functions.py +0 -2
- classiq/interface/generator/function_param_list.py +129 -5
- classiq/interface/generator/functions/classical_type.py +67 -2
- classiq/interface/generator/functions/qmod_python_interface.py +15 -0
- classiq/interface/generator/functions/type_name.py +12 -0
- classiq/interface/generator/model/preferences/preferences.py +1 -17
- classiq/interface/generator/quantum_program.py +1 -13
- classiq/interface/generator/transpiler_basis_gates.py +5 -1
- classiq/interface/generator/types/builtin_enum_declarations.py +0 -8
- 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 +48 -0
- classiq/interface/model/classical_parameter_declaration.py +4 -0
- classiq/interface/model/handle_binding.py +28 -16
- classiq/interface/model/port_declaration.py +12 -0
- classiq/interface/model/quantum_function_declaration.py +12 -0
- classiq/interface/model/quantum_type.py +117 -2
- classiq/interface/pretty_print/expression_to_qmod.py +7 -8
- 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 +23 -16
- 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/model_expansions/quantum_operations/handle_evaluator.py +2 -8
- classiq/open_library/functions/__init__.py +4 -0
- classiq/open_library/functions/lcu.py +117 -0
- classiq/open_library/functions/state_preparation.py +47 -5
- classiq/qmod/builtins/__init__.py +0 -3
- classiq/qmod/builtins/classical_functions.py +0 -28
- classiq/qmod/builtins/enums.py +26 -20
- classiq/qmod/builtins/functions/__init__.py +0 -5
- classiq/qmod/builtins/operations.py +142 -0
- classiq/qmod/builtins/structs.py +33 -29
- classiq/qmod/native/pretty_printer.py +1 -1
- classiq/qmod/pretty_print/expression_to_python.py +1 -6
- classiq/qmod/pretty_print/pretty_printer.py +4 -1
- classiq/qmod/qmod_variable.py +94 -2
- classiq/qmod/semantics/annotation/call_annotation.py +4 -2
- classiq/qmod/semantics/annotation/qstruct_annotator.py +20 -5
- {classiq-0.86.1.dist-info → classiq-0.88.0.dist-info}/METADATA +5 -5
- {classiq-0.86.1.dist-info → classiq-0.88.0.dist-info}/RECORD +106 -124
- classiq/applications/finance/__init__.py +0 -15
- classiq/interface/finance/finance_modelling_params.py +0 -11
- classiq/interface/finance/function_input.py +0 -102
- classiq/interface/finance/gaussian_model_input.py +0 -50
- classiq/interface/finance/log_normal_model_input.py +0 -40
- classiq/interface/finance/model_input.py +0 -22
- classiq/interface/generator/amplitude_estimation.py +0 -34
- classiq/interface/generator/application_apis/finance_declarations.py +0 -108
- classiq/interface/generator/expressions/enums/__init__.py +0 -0
- classiq/interface/generator/expressions/enums/finance_functions.py +0 -12
- classiq/interface/generator/finance.py +0 -107
- 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/__init__.py +0 -0
- classiq/interface/grover/grover_modelling_params.py +0 -13
- classiq/model_expansions/transformers/var_splitter.py +0 -224
- classiq/qmod/builtins/functions/finance.py +0 -34
- /classiq/{interface/finance → evaluators/qmod_type_inference}/__init__.py +0 -0
- {classiq-0.86.1.dist-info → classiq-0.88.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
|
-
from collections.abc import Sequence
|
|
2
|
+
from collections.abc import Mapping, 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"struct_literal({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)
|
|
@@ -52,8 +82,9 @@ class QmodAnnotatedExpression:
|
|
|
52
82
|
QmodExprNodeId, QuantumTypeAttributeAnnotation
|
|
53
83
|
] = {}
|
|
54
84
|
self._concatenations: dict[QmodExprNodeId, ConcatenationAnnotation] = {}
|
|
85
|
+
self._locked = False
|
|
55
86
|
|
|
56
|
-
def
|
|
87
|
+
def __str__(self) -> str:
|
|
57
88
|
return ast.unparse(_ExprInliner(self).visit(self.root))
|
|
58
89
|
|
|
59
90
|
def has_node(self, node_id: QmodExprNodeId) -> bool:
|
|
@@ -63,6 +94,8 @@ class QmodAnnotatedExpression:
|
|
|
63
94
|
return self._node_mapping[node_id]
|
|
64
95
|
|
|
65
96
|
def set_value(self, node: Union[ast.AST, QmodExprNodeId], value: Any) -> None:
|
|
97
|
+
if self._locked:
|
|
98
|
+
raise ClassiqInternalExpansionError("QAE is locked")
|
|
66
99
|
if isinstance(node, ast.AST):
|
|
67
100
|
node = id(node)
|
|
68
101
|
self._values[node] = value
|
|
@@ -80,6 +113,8 @@ class QmodAnnotatedExpression:
|
|
|
80
113
|
def set_type(
|
|
81
114
|
self, node: Union[ast.AST, QmodExprNodeId], qmod_type: QmodType
|
|
82
115
|
) -> None:
|
|
116
|
+
if self._locked:
|
|
117
|
+
raise ClassiqInternalExpansionError("QAE is locked")
|
|
83
118
|
if isinstance(node, ast.AST):
|
|
84
119
|
node_id = id(node)
|
|
85
120
|
self._node_mapping[node_id] = node
|
|
@@ -91,7 +126,25 @@ class QmodAnnotatedExpression:
|
|
|
91
126
|
node = id(node)
|
|
92
127
|
return self._types[node]
|
|
93
128
|
|
|
129
|
+
def get_quantum_type(self, node: Union[ast.AST, QmodExprNodeId]) -> QuantumType:
|
|
130
|
+
if isinstance(node, ast.AST):
|
|
131
|
+
node = id(node)
|
|
132
|
+
qmod_type = self._types[node]
|
|
133
|
+
if is_classical_type(qmod_type):
|
|
134
|
+
raise ClassiqInternalExpansionError
|
|
135
|
+
return cast(QuantumType, qmod_type)
|
|
136
|
+
|
|
137
|
+
def get_classical_type(self, node: Union[ast.AST, QmodExprNodeId]) -> ClassicalType:
|
|
138
|
+
if isinstance(node, ast.AST):
|
|
139
|
+
node = id(node)
|
|
140
|
+
qmod_type = self._types[node]
|
|
141
|
+
if not is_classical_type(qmod_type):
|
|
142
|
+
raise ClassiqInternalExpansionError
|
|
143
|
+
return cast(ClassicalType, qmod_type)
|
|
144
|
+
|
|
94
145
|
def set_var(self, node: Union[ast.AST, QmodExprNodeId], var: HandleBinding) -> None:
|
|
146
|
+
if self._locked:
|
|
147
|
+
raise ClassiqInternalExpansionError("QAE is locked")
|
|
95
148
|
var = var.collapse()
|
|
96
149
|
if isinstance(node, ast.AST):
|
|
97
150
|
node = id(node)
|
|
@@ -119,6 +172,8 @@ class QmodAnnotatedExpression:
|
|
|
119
172
|
return node in self._quantum_vars
|
|
120
173
|
|
|
121
174
|
def remove_var(self, node: Union[ast.AST, QmodExprNodeId]) -> None:
|
|
175
|
+
if self._locked:
|
|
176
|
+
raise ClassiqInternalExpansionError("QAE is locked")
|
|
122
177
|
if isinstance(node, ast.AST):
|
|
123
178
|
node = id(node)
|
|
124
179
|
if node in self._classical_vars:
|
|
@@ -132,6 +187,8 @@ class QmodAnnotatedExpression:
|
|
|
132
187
|
value: Union[ast.AST, QmodExprNodeId],
|
|
133
188
|
index: Union[ast.AST, QmodExprNodeId],
|
|
134
189
|
) -> None:
|
|
190
|
+
if self._locked:
|
|
191
|
+
raise ClassiqInternalExpansionError("QAE is locked")
|
|
135
192
|
if isinstance(node, ast.AST):
|
|
136
193
|
node = id(node)
|
|
137
194
|
if isinstance(value, ast.AST):
|
|
@@ -149,7 +206,7 @@ class QmodAnnotatedExpression:
|
|
|
149
206
|
|
|
150
207
|
def get_quantum_subscripts(
|
|
151
208
|
self,
|
|
152
|
-
) ->
|
|
209
|
+
) -> Mapping[QmodExprNodeId, QuantumSubscriptAnnotation]:
|
|
153
210
|
return self._quantum_subscripts
|
|
154
211
|
|
|
155
212
|
def set_quantum_type_attr(
|
|
@@ -158,6 +215,8 @@ class QmodAnnotatedExpression:
|
|
|
158
215
|
value: Union[ast.AST, QmodExprNodeId],
|
|
159
216
|
attr: str,
|
|
160
217
|
) -> None:
|
|
218
|
+
if self._locked:
|
|
219
|
+
raise ClassiqInternalExpansionError("QAE is locked")
|
|
161
220
|
if isinstance(node, ast.AST):
|
|
162
221
|
node = id(node)
|
|
163
222
|
if isinstance(value, ast.AST):
|
|
@@ -173,7 +232,7 @@ class QmodAnnotatedExpression:
|
|
|
173
232
|
|
|
174
233
|
def get_quantum_type_attributes(
|
|
175
234
|
self,
|
|
176
|
-
) ->
|
|
235
|
+
) -> Mapping[QmodExprNodeId, QuantumTypeAttributeAnnotation]:
|
|
177
236
|
return self._quantum_type_attrs
|
|
178
237
|
|
|
179
238
|
def set_concatenation(
|
|
@@ -181,27 +240,68 @@ class QmodAnnotatedExpression:
|
|
|
181
240
|
node: Union[ast.AST, QmodExprNodeId],
|
|
182
241
|
elements: Sequence[Union[ast.AST, QmodExprNodeId]],
|
|
183
242
|
) -> None:
|
|
243
|
+
if self._locked:
|
|
244
|
+
raise ClassiqInternalExpansionError("QAE is locked")
|
|
184
245
|
if isinstance(node, ast.AST):
|
|
185
246
|
node = id(node)
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
247
|
+
inlined_elements: list[QmodExprNodeId] = []
|
|
248
|
+
for element in elements:
|
|
249
|
+
if isinstance(element, ast.AST):
|
|
250
|
+
element = id(element)
|
|
251
|
+
if element not in self._concatenations:
|
|
252
|
+
inlined_elements.append(element)
|
|
253
|
+
else:
|
|
254
|
+
inlined_elements.extend(self._concatenations.pop(element).elements)
|
|
255
|
+
self._concatenations[node] = ConcatenationAnnotation(elements=inlined_elements)
|
|
194
256
|
|
|
195
257
|
def has_concatenation(self, node: Union[ast.AST, QmodExprNodeId]) -> bool:
|
|
196
258
|
if isinstance(node, ast.AST):
|
|
197
259
|
node = id(node)
|
|
198
260
|
return node in self._concatenations
|
|
199
261
|
|
|
200
|
-
def get_concatenations(self) ->
|
|
262
|
+
def get_concatenations(self) -> Mapping[QmodExprNodeId, ConcatenationAnnotation]:
|
|
201
263
|
return self._concatenations
|
|
202
264
|
|
|
203
|
-
def get_classical_vars(self) ->
|
|
265
|
+
def get_classical_vars(self) -> Mapping[QmodExprNodeId, HandleBinding]:
|
|
204
266
|
return self._classical_vars
|
|
205
267
|
|
|
206
|
-
def get_quantum_vars(self) ->
|
|
268
|
+
def get_quantum_vars(self) -> Mapping[QmodExprNodeId, HandleBinding]:
|
|
207
269
|
return self._quantum_vars
|
|
270
|
+
|
|
271
|
+
def clear_node_data(self, node: Union[ast.AST, QmodExprNodeId]) -> None:
|
|
272
|
+
if isinstance(node, ast.AST):
|
|
273
|
+
node = id(node)
|
|
274
|
+
self._node_mapping.pop(node, None)
|
|
275
|
+
self._values.pop(node, None)
|
|
276
|
+
self._types.pop(node, None)
|
|
277
|
+
self._classical_vars.pop(node, None)
|
|
278
|
+
self._quantum_vars.pop(node, None)
|
|
279
|
+
qs = self._quantum_subscripts.pop(node, None)
|
|
280
|
+
if qs is not None:
|
|
281
|
+
self.clear_node_data(qs.value)
|
|
282
|
+
self.clear_node_data(qs.index)
|
|
283
|
+
qta = self._quantum_type_attrs.pop(node, None)
|
|
284
|
+
if qta is not None:
|
|
285
|
+
self.clear_node_data(qta.value)
|
|
286
|
+
cnct = self._concatenations.pop(node, None)
|
|
287
|
+
if cnct is not None:
|
|
288
|
+
for element in cnct.elements:
|
|
289
|
+
self.clear_node_data(element)
|
|
290
|
+
|
|
291
|
+
def _add_data_from(self, other: "QmodAnnotatedExpression") -> None:
|
|
292
|
+
self._node_mapping |= other._node_mapping
|
|
293
|
+
self._values |= other._values
|
|
294
|
+
self._types |= other._types
|
|
295
|
+
self._classical_vars |= other._classical_vars
|
|
296
|
+
self._quantum_vars |= other._quantum_vars
|
|
297
|
+
self._quantum_subscripts |= other._quantum_subscripts
|
|
298
|
+
self._quantum_type_attrs |= other._quantum_type_attrs
|
|
299
|
+
self._concatenations |= other._concatenations
|
|
300
|
+
|
|
301
|
+
def clone(self) -> "QmodAnnotatedExpression":
|
|
302
|
+
expr_val = QmodAnnotatedExpression(self.root)
|
|
303
|
+
expr_val._add_data_from(self)
|
|
304
|
+
return expr_val
|
|
305
|
+
|
|
306
|
+
def lock(self) -> None:
|
|
307
|
+
self._locked = True
|
|
@@ -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)
|
|
@@ -73,11 +73,6 @@ class QmodExpressionBwc(ast.NodeTransformer):
|
|
|
73
73
|
return node
|
|
74
74
|
return ast.Compare(left=args[0], ops=[ast.GtE()], comparators=[args[1]])
|
|
75
75
|
|
|
76
|
-
if func == "struct_literal":
|
|
77
|
-
if num_args != 1:
|
|
78
|
-
return node
|
|
79
|
-
return ast.Call(func=node.args[0], args=[], keywords=node.keywords)
|
|
80
|
-
|
|
81
76
|
if func == "do_subscript":
|
|
82
77
|
if num_args != 2 or num_kwargs != 0:
|
|
83
78
|
return node
|
|
@@ -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 []
|
|
@@ -98,8 +103,8 @@ class QmodExpressionEvaluator(ast.NodeVisitor):
|
|
|
98
103
|
|
|
99
104
|
def visit(self, node: ast.AST) -> None:
|
|
100
105
|
if not isinstance(node, _SUPPORTED_NODES):
|
|
101
|
-
raise
|
|
102
|
-
f"Syntax error: {type(node).__name__!r} is not
|
|
106
|
+
raise ClassiqExpansionError(
|
|
107
|
+
f"Syntax error: {type(node).__name__!r} is not a valid Qmod expression"
|
|
103
108
|
)
|
|
104
109
|
super().visit(node)
|
|
105
110
|
|
|
@@ -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,42 @@ 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 kwarg in node.keywords:
|
|
142
|
+
self.visit(kwarg)
|
|
143
|
+
|
|
144
|
+
if (
|
|
145
|
+
func_name == "struct_literal"
|
|
146
|
+
and len(node.args) == 1
|
|
147
|
+
and isinstance(node.args[0], ast.Name)
|
|
148
|
+
and (struct_name := node.args[0].id) in self._classical_struct_decls
|
|
149
|
+
):
|
|
150
|
+
eval_struct_instantiation(
|
|
151
|
+
self._expr_val, node, self._classical_struct_decls[struct_name]
|
|
152
|
+
)
|
|
153
|
+
return
|
|
154
|
+
|
|
155
|
+
for arg in node.args:
|
|
156
|
+
self.visit(arg)
|
|
157
|
+
|
|
135
158
|
if func_name == "measure":
|
|
136
159
|
eval_measurement(self._expr_val, node)
|
|
137
160
|
return
|
|
138
161
|
|
|
139
|
-
if func_name in
|
|
140
|
-
|
|
141
|
-
self._expr_val,
|
|
162
|
+
if func_name in ("min", "max"):
|
|
163
|
+
eval_min_max_op(
|
|
164
|
+
self._expr_val,
|
|
165
|
+
node,
|
|
166
|
+
func_name,
|
|
167
|
+
self._treat_qnum_as_float,
|
|
168
|
+
self._machine_precision,
|
|
142
169
|
)
|
|
143
170
|
return
|
|
144
171
|
|
|
172
|
+
# FIXME: Remove (CLS-3241)
|
|
145
173
|
if func_name in self._classical_function_callables:
|
|
146
174
|
if func_name not in self._classical_function_declarations:
|
|
147
175
|
raise ClassiqInternalExpansionError
|
|
@@ -153,6 +181,7 @@ class QmodExpressionEvaluator(ast.NodeVisitor):
|
|
|
153
181
|
)
|
|
154
182
|
return
|
|
155
183
|
|
|
184
|
+
# FIXME: Remove (CLS-3241)
|
|
156
185
|
if func_name in self._classical_function_declarations:
|
|
157
186
|
eval_symbolic_function(
|
|
158
187
|
self._expr_val, node, self._classical_function_declarations[func_name]
|
|
@@ -167,6 +196,21 @@ class QmodExpressionEvaluator(ast.NodeVisitor):
|
|
|
167
196
|
|
|
168
197
|
raise ClassiqExpansionError(f"{func.id!r} is undefined")
|
|
169
198
|
|
|
199
|
+
def _eval_piecewise(self, node: ast.Call) -> None:
|
|
200
|
+
if (
|
|
201
|
+
len(node.args) == 0
|
|
202
|
+
or len(node.keywords) != 0
|
|
203
|
+
or not all(
|
|
204
|
+
isinstance(arg, ast.Tuple) and len(arg.elts) == 2 for arg in node.args
|
|
205
|
+
)
|
|
206
|
+
):
|
|
207
|
+
raise ClassiqExpansionError("Malformed Piecewise expression")
|
|
208
|
+
args = [(arg.elts[0], arg.elts[1]) for arg in cast(list[ast.Tuple], node.args)]
|
|
209
|
+
for value, cond in args:
|
|
210
|
+
self.visit(value)
|
|
211
|
+
self.visit(cond)
|
|
212
|
+
eval_piecewise(self._expr_val, node, args)
|
|
213
|
+
|
|
170
214
|
def visit_Constant(self, node: ast.Constant) -> None:
|
|
171
215
|
eval_constant(self._expr_val, node)
|
|
172
216
|
|
|
@@ -194,12 +238,15 @@ class QmodExpressionEvaluator(ast.NodeVisitor):
|
|
|
194
238
|
eval_subscript(self._expr_val, node)
|
|
195
239
|
|
|
196
240
|
def visit_Name(self, node: ast.Name) -> None:
|
|
241
|
+
if node.id in self._scope:
|
|
242
|
+
eval_name(self._expr_val, node, self._scope[node.id])
|
|
243
|
+
return
|
|
197
244
|
if try_eval_sympy_constant(self._expr_val, node):
|
|
198
245
|
return
|
|
246
|
+
if try_eval_qmod_literal(self._expr_val, node):
|
|
247
|
+
return
|
|
199
248
|
|
|
200
|
-
|
|
201
|
-
raise ClassiqExpansionError(f"Variable {node.id!r} is undefined")
|
|
202
|
-
eval_name(self._expr_val, node, self._scope[node.id])
|
|
249
|
+
raise ClassiqExpansionError(f"Variable {node.id!r} is undefined")
|
|
203
250
|
|
|
204
251
|
def visit_List(self, node: ast.List) -> None:
|
|
205
252
|
super().generic_visit(node)
|
|
@@ -209,6 +256,7 @@ class QmodExpressionEvaluator(ast.NodeVisitor):
|
|
|
209
256
|
def evaluate_qmod_expression(
|
|
210
257
|
expr: str,
|
|
211
258
|
*,
|
|
259
|
+
treat_qnum_as_float: bool = False,
|
|
212
260
|
machine_precision: int = DEFAULT_MACHINE_PRECISION,
|
|
213
261
|
classical_struct_declarations: Optional[Sequence[StructDeclaration]] = None,
|
|
214
262
|
enum_declarations: Optional[Sequence[EnumDeclaration]] = None,
|
|
@@ -222,6 +270,7 @@ def evaluate_qmod_expression(
|
|
|
222
270
|
expr_value = QmodAnnotatedExpression(expr_ast)
|
|
223
271
|
QmodExpressionEvaluator(
|
|
224
272
|
expr_value,
|
|
273
|
+
treat_qnum_as_float=treat_qnum_as_float,
|
|
225
274
|
machine_precision=machine_precision,
|
|
226
275
|
classical_struct_declarations=classical_struct_declarations,
|
|
227
276
|
enum_declarations=enum_declarations,
|
|
@@ -229,4 +278,5 @@ def evaluate_qmod_expression(
|
|
|
229
278
|
classical_function_callables=classical_function_callables,
|
|
230
279
|
scope=scope,
|
|
231
280
|
).visit(expr_value.root)
|
|
281
|
+
expr_value.lock()
|
|
232
282
|
return expr_value
|