classiq 0.85.0__py3-none-any.whl → 0.86.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- classiq/applications/combinatorial_optimization/combinatorial_problem.py +4 -3
- classiq/evaluators/qmod_annotated_expression.py +207 -0
- classiq/evaluators/qmod_expression_visitors/__init__.py +0 -0
- classiq/evaluators/qmod_expression_visitors/qmod_expression_bwc.py +134 -0
- classiq/evaluators/qmod_expression_visitors/qmod_expression_evaluator.py +232 -0
- classiq/evaluators/qmod_expression_visitors/qmod_expression_renamer.py +44 -0
- classiq/evaluators/qmod_expression_visitors/qmod_expression_simplifier.py +308 -0
- classiq/evaluators/qmod_node_evaluators/__init__.py +0 -0
- classiq/evaluators/qmod_node_evaluators/attribute_evaluation.py +112 -0
- classiq/evaluators/qmod_node_evaluators/binary_op_evaluation.py +132 -0
- classiq/evaluators/qmod_node_evaluators/bool_op_evaluation.py +70 -0
- classiq/evaluators/qmod_node_evaluators/classical_function_evaluation.py +311 -0
- classiq/evaluators/qmod_node_evaluators/compare_evaluation.py +107 -0
- classiq/evaluators/qmod_node_evaluators/constant_evaluation.py +67 -0
- classiq/evaluators/qmod_node_evaluators/list_evaluation.py +107 -0
- classiq/evaluators/qmod_node_evaluators/measurement_evaluation.py +25 -0
- classiq/evaluators/qmod_node_evaluators/name_evaluation.py +50 -0
- classiq/evaluators/qmod_node_evaluators/struct_instantiation_evaluation.py +66 -0
- classiq/evaluators/qmod_node_evaluators/subscript_evaluation.py +225 -0
- classiq/evaluators/qmod_node_evaluators/unary_op_evaluation.py +58 -0
- classiq/evaluators/qmod_node_evaluators/utils.py +80 -0
- classiq/execution/execution_session.py +4 -0
- classiq/interface/_version.py +1 -1
- classiq/interface/analyzer/analysis_params.py +1 -1
- classiq/interface/analyzer/result.py +1 -1
- classiq/interface/executor/quantum_code.py +2 -2
- classiq/interface/generator/arith/arithmetic_expression_validator.py +5 -1
- classiq/interface/generator/arith/binary_ops.py +43 -51
- classiq/interface/generator/arith/number_utils.py +3 -2
- classiq/interface/generator/arith/register_user_input.py +15 -0
- classiq/interface/generator/arith/unary_ops.py +32 -28
- classiq/interface/generator/expressions/expression_types.py +2 -2
- classiq/interface/generator/expressions/proxies/classical/qmod_struct_instance.py +7 -0
- classiq/interface/generator/functions/classical_function_declaration.py +0 -4
- classiq/interface/generator/functions/classical_type.py +0 -32
- classiq/interface/generator/generated_circuit_data.py +2 -0
- classiq/interface/generator/quantum_program.py +6 -1
- classiq/interface/generator/synthesis_metadata/synthesis_execution_data.py +29 -0
- classiq/interface/ide/operation_registry.py +2 -2
- classiq/interface/ide/visual_model.py +22 -1
- classiq/interface/model/quantum_type.py +67 -33
- classiq/model_expansions/arithmetic.py +115 -0
- classiq/model_expansions/arithmetic_compute_result_attrs.py +71 -0
- classiq/model_expansions/atomic_expression_functions_defs.py +5 -5
- classiq/model_expansions/generative_functions.py +15 -2
- classiq/model_expansions/interpreters/base_interpreter.py +7 -0
- classiq/model_expansions/interpreters/generative_interpreter.py +2 -0
- classiq/model_expansions/quantum_operations/call_emitter.py +5 -2
- classiq/model_expansions/scope_initialization.py +2 -0
- classiq/model_expansions/sympy_conversion/sympy_to_python.py +1 -1
- classiq/model_expansions/transformers/type_modifier_inference.py +5 -0
- classiq/model_expansions/visitors/boolean_expression_transformers.py +1 -1
- classiq/qmod/builtins/operations.py +1 -1
- classiq/qmod/declaration_inferrer.py +5 -3
- classiq/qmod/write_qmod.py +3 -1
- {classiq-0.85.0.dist-info → classiq-0.86.1.dist-info}/METADATA +1 -1
- {classiq-0.85.0.dist-info → classiq-0.86.1.dist-info}/RECORD +59 -37
- /classiq/{model_expansions/sympy_conversion/arithmetics.py → evaluators/qmod_expression_visitors/sympy_wrappers.py} +0 -0
- {classiq-0.85.0.dist-info → classiq-0.86.1.dist-info}/WHEEL +0 -0
@@ -46,11 +46,11 @@ class CombinatorialProblem:
|
|
46
46
|
self._es: ExecutionSession | None = None
|
47
47
|
self._optimized_params: list[float] | None = None
|
48
48
|
self.params_trace_: list[np.ndarray] = []
|
49
|
-
self.
|
49
|
+
self._cost_trace: list[float] = []
|
50
50
|
|
51
51
|
@property
|
52
|
-
def cost_trace(self) -> list:
|
53
|
-
return self.
|
52
|
+
def cost_trace(self) -> list[float]:
|
53
|
+
return self._cost_trace
|
54
54
|
|
55
55
|
@property
|
56
56
|
def params_trace(self) -> list[np.ndarray]:
|
@@ -119,6 +119,7 @@ class CombinatorialProblem:
|
|
119
119
|
)
|
120
120
|
_optimized_params = cast(list[float], result[-1][1]["params"])
|
121
121
|
self._optimized_params = _optimized_params
|
122
|
+
self._cost_trace = [cost for cost, _ in result]
|
122
123
|
self._es = _es
|
123
124
|
return _optimized_params
|
124
125
|
|
@@ -0,0 +1,207 @@
|
|
1
|
+
import ast
|
2
|
+
from collections.abc import Sequence
|
3
|
+
from dataclasses import dataclass
|
4
|
+
from typing import Any, Union, cast
|
5
|
+
|
6
|
+
from classiq.interface.model.handle_binding import HandleBinding
|
7
|
+
|
8
|
+
from classiq.evaluators.qmod_node_evaluators.utils import QmodType, is_classical_type
|
9
|
+
|
10
|
+
QmodExprNodeId = int
|
11
|
+
|
12
|
+
|
13
|
+
@dataclass(frozen=True)
|
14
|
+
class QuantumSubscriptAnnotation:
|
15
|
+
value: QmodExprNodeId
|
16
|
+
index: QmodExprNodeId
|
17
|
+
|
18
|
+
|
19
|
+
@dataclass(frozen=True)
|
20
|
+
class QuantumTypeAttributeAnnotation:
|
21
|
+
value: QmodExprNodeId
|
22
|
+
attr: str
|
23
|
+
|
24
|
+
|
25
|
+
@dataclass(frozen=True)
|
26
|
+
class ConcatenationAnnotation:
|
27
|
+
elements: list[QmodExprNodeId]
|
28
|
+
|
29
|
+
|
30
|
+
class _ExprInliner(ast.NodeTransformer):
|
31
|
+
def __init__(self, expr_val: "QmodAnnotatedExpression") -> None:
|
32
|
+
self._expr_val = expr_val
|
33
|
+
|
34
|
+
def visit(self, node: ast.AST) -> Any:
|
35
|
+
if self._expr_val.has_value(node):
|
36
|
+
return ast.Name(id=str(self._expr_val.get_value(node)))
|
37
|
+
if self._expr_val.has_var(node):
|
38
|
+
return ast.Name(id=str(self._expr_val.get_var(node)))
|
39
|
+
return super().visit(node)
|
40
|
+
|
41
|
+
|
42
|
+
class QmodAnnotatedExpression:
|
43
|
+
def __init__(self, expr_ast: ast.AST) -> None:
|
44
|
+
self.root = expr_ast
|
45
|
+
self._node_mapping: dict[QmodExprNodeId, ast.AST] = {}
|
46
|
+
self._values: dict[QmodExprNodeId, Any] = {}
|
47
|
+
self._types: dict[QmodExprNodeId, QmodType] = {}
|
48
|
+
self._classical_vars: dict[QmodExprNodeId, HandleBinding] = {}
|
49
|
+
self._quantum_vars: dict[QmodExprNodeId, HandleBinding] = {}
|
50
|
+
self._quantum_subscripts: dict[QmodExprNodeId, QuantumSubscriptAnnotation] = {}
|
51
|
+
self._quantum_type_attrs: dict[
|
52
|
+
QmodExprNodeId, QuantumTypeAttributeAnnotation
|
53
|
+
] = {}
|
54
|
+
self._concatenations: dict[QmodExprNodeId, ConcatenationAnnotation] = {}
|
55
|
+
|
56
|
+
def to_qmod_expr(self) -> str:
|
57
|
+
return ast.unparse(_ExprInliner(self).visit(self.root))
|
58
|
+
|
59
|
+
def has_node(self, node_id: QmodExprNodeId) -> bool:
|
60
|
+
return node_id in self._node_mapping
|
61
|
+
|
62
|
+
def get_node(self, node_id: QmodExprNodeId) -> ast.AST:
|
63
|
+
return self._node_mapping[node_id]
|
64
|
+
|
65
|
+
def set_value(self, node: Union[ast.AST, QmodExprNodeId], value: Any) -> None:
|
66
|
+
if isinstance(node, ast.AST):
|
67
|
+
node = id(node)
|
68
|
+
self._values[node] = value
|
69
|
+
|
70
|
+
def get_value(self, node: Union[ast.AST, QmodExprNodeId]) -> Any:
|
71
|
+
if isinstance(node, ast.AST):
|
72
|
+
node = id(node)
|
73
|
+
return self._values[node]
|
74
|
+
|
75
|
+
def has_value(self, node: Union[ast.AST, QmodExprNodeId]) -> bool:
|
76
|
+
if isinstance(node, ast.AST):
|
77
|
+
node = id(node)
|
78
|
+
return node in self._values
|
79
|
+
|
80
|
+
def set_type(
|
81
|
+
self, node: Union[ast.AST, QmodExprNodeId], qmod_type: QmodType
|
82
|
+
) -> None:
|
83
|
+
if isinstance(node, ast.AST):
|
84
|
+
node_id = id(node)
|
85
|
+
self._node_mapping[node_id] = node
|
86
|
+
node = id(node)
|
87
|
+
self._types[node] = qmod_type
|
88
|
+
|
89
|
+
def get_type(self, node: Union[ast.AST, QmodExprNodeId]) -> QmodType:
|
90
|
+
if isinstance(node, ast.AST):
|
91
|
+
node = id(node)
|
92
|
+
return self._types[node]
|
93
|
+
|
94
|
+
def set_var(self, node: Union[ast.AST, QmodExprNodeId], var: HandleBinding) -> None:
|
95
|
+
var = var.collapse()
|
96
|
+
if isinstance(node, ast.AST):
|
97
|
+
node = id(node)
|
98
|
+
if is_classical_type(self.get_type(node)):
|
99
|
+
self._classical_vars[node] = var
|
100
|
+
else:
|
101
|
+
self._quantum_vars[node] = var
|
102
|
+
|
103
|
+
def get_var(self, node: Union[ast.AST, QmodExprNodeId]) -> HandleBinding:
|
104
|
+
if isinstance(node, ast.AST):
|
105
|
+
node = id(node)
|
106
|
+
return (self._classical_vars | self._quantum_vars)[node]
|
107
|
+
|
108
|
+
def has_var(self, node: Union[ast.AST, QmodExprNodeId]) -> bool:
|
109
|
+
return self.has_classical_var(node) or self.has_quantum_var(node)
|
110
|
+
|
111
|
+
def has_classical_var(self, node: Union[ast.AST, QmodExprNodeId]) -> bool:
|
112
|
+
if isinstance(node, ast.AST):
|
113
|
+
node = id(node)
|
114
|
+
return node in self._classical_vars
|
115
|
+
|
116
|
+
def has_quantum_var(self, node: Union[ast.AST, QmodExprNodeId]) -> bool:
|
117
|
+
if isinstance(node, ast.AST):
|
118
|
+
node = id(node)
|
119
|
+
return node in self._quantum_vars
|
120
|
+
|
121
|
+
def remove_var(self, node: Union[ast.AST, QmodExprNodeId]) -> None:
|
122
|
+
if isinstance(node, ast.AST):
|
123
|
+
node = id(node)
|
124
|
+
if node in self._classical_vars:
|
125
|
+
self._classical_vars.pop(node)
|
126
|
+
else:
|
127
|
+
self._quantum_vars.pop(node)
|
128
|
+
|
129
|
+
def set_quantum_subscript(
|
130
|
+
self,
|
131
|
+
node: Union[ast.AST, QmodExprNodeId],
|
132
|
+
value: Union[ast.AST, QmodExprNodeId],
|
133
|
+
index: Union[ast.AST, QmodExprNodeId],
|
134
|
+
) -> None:
|
135
|
+
if isinstance(node, ast.AST):
|
136
|
+
node = id(node)
|
137
|
+
if isinstance(value, ast.AST):
|
138
|
+
value = id(value)
|
139
|
+
if isinstance(index, ast.AST):
|
140
|
+
index = id(index)
|
141
|
+
self._quantum_subscripts[node] = QuantumSubscriptAnnotation(
|
142
|
+
value=value, index=index
|
143
|
+
)
|
144
|
+
|
145
|
+
def has_quantum_subscript(self, node: Union[ast.AST, QmodExprNodeId]) -> bool:
|
146
|
+
if isinstance(node, ast.AST):
|
147
|
+
node = id(node)
|
148
|
+
return node in self._quantum_subscripts
|
149
|
+
|
150
|
+
def get_quantum_subscripts(
|
151
|
+
self,
|
152
|
+
) -> dict[QmodExprNodeId, QuantumSubscriptAnnotation]:
|
153
|
+
return self._quantum_subscripts
|
154
|
+
|
155
|
+
def set_quantum_type_attr(
|
156
|
+
self,
|
157
|
+
node: Union[ast.AST, QmodExprNodeId],
|
158
|
+
value: Union[ast.AST, QmodExprNodeId],
|
159
|
+
attr: str,
|
160
|
+
) -> None:
|
161
|
+
if isinstance(node, ast.AST):
|
162
|
+
node = id(node)
|
163
|
+
if isinstance(value, ast.AST):
|
164
|
+
value = id(value)
|
165
|
+
self._quantum_type_attrs[node] = QuantumTypeAttributeAnnotation(
|
166
|
+
value=value, attr=attr
|
167
|
+
)
|
168
|
+
|
169
|
+
def has_quantum_type_attribute(self, node: Union[ast.AST, QmodExprNodeId]) -> bool:
|
170
|
+
if isinstance(node, ast.AST):
|
171
|
+
node = id(node)
|
172
|
+
return node in self._quantum_type_attrs
|
173
|
+
|
174
|
+
def get_quantum_type_attributes(
|
175
|
+
self,
|
176
|
+
) -> dict[QmodExprNodeId, QuantumTypeAttributeAnnotation]:
|
177
|
+
return self._quantum_type_attrs
|
178
|
+
|
179
|
+
def set_concatenation(
|
180
|
+
self,
|
181
|
+
node: Union[ast.AST, QmodExprNodeId],
|
182
|
+
elements: Sequence[Union[ast.AST, QmodExprNodeId]],
|
183
|
+
) -> None:
|
184
|
+
if isinstance(node, ast.AST):
|
185
|
+
node = id(node)
|
186
|
+
elements = cast(
|
187
|
+
list[QmodExprNodeId],
|
188
|
+
[
|
189
|
+
id(element) if isinstance(element, ast.AST) else element
|
190
|
+
for element in elements
|
191
|
+
],
|
192
|
+
)
|
193
|
+
self._concatenations[node] = ConcatenationAnnotation(elements=elements)
|
194
|
+
|
195
|
+
def has_concatenation(self, node: Union[ast.AST, QmodExprNodeId]) -> bool:
|
196
|
+
if isinstance(node, ast.AST):
|
197
|
+
node = id(node)
|
198
|
+
return node in self._concatenations
|
199
|
+
|
200
|
+
def get_concatenations(self) -> dict[QmodExprNodeId, ConcatenationAnnotation]:
|
201
|
+
return self._concatenations
|
202
|
+
|
203
|
+
def get_classical_vars(self) -> dict[QmodExprNodeId, HandleBinding]:
|
204
|
+
return self._classical_vars
|
205
|
+
|
206
|
+
def get_quantum_vars(self) -> dict[QmodExprNodeId, HandleBinding]:
|
207
|
+
return self._quantum_vars
|
File without changes
|
@@ -0,0 +1,134 @@
|
|
1
|
+
import ast
|
2
|
+
from typing import Any, cast
|
3
|
+
|
4
|
+
import sympy
|
5
|
+
|
6
|
+
MAX_PIECEWISE_LOOPS = 1000
|
7
|
+
|
8
|
+
|
9
|
+
# FIXME: Remove with deprecation (CLS-3214)
|
10
|
+
class QmodExpressionBwc(ast.NodeTransformer):
|
11
|
+
def visit_Call(self, node: ast.Call) -> Any:
|
12
|
+
node = cast(ast.Call, self.generic_visit(node))
|
13
|
+
if not isinstance(node.func, ast.Name):
|
14
|
+
return node
|
15
|
+
func = node.func.id
|
16
|
+
args = node.args
|
17
|
+
kwargs = node.keywords
|
18
|
+
num_args = len(args)
|
19
|
+
num_kwargs = len(kwargs)
|
20
|
+
|
21
|
+
if func == "BitwiseNot":
|
22
|
+
if num_args != 1 or num_kwargs != 0:
|
23
|
+
return node
|
24
|
+
return ast.UnaryOp(op=ast.Invert(), operand=args[0])
|
25
|
+
if func == "LShift":
|
26
|
+
if num_args != 2 or num_kwargs != 0:
|
27
|
+
return node
|
28
|
+
return ast.BinOp(left=args[0], op=ast.LShift(), right=args[1])
|
29
|
+
if func == "RShift":
|
30
|
+
if num_args != 2 or num_kwargs != 0:
|
31
|
+
return node
|
32
|
+
return ast.BinOp(left=args[0], op=ast.RShift(), right=args[1])
|
33
|
+
if func == "BitwiseOr":
|
34
|
+
if num_args != 2 or num_kwargs != 0:
|
35
|
+
return node
|
36
|
+
return ast.BinOp(left=args[0], op=ast.BitOr(), right=args[1])
|
37
|
+
if func == "BitwiseXor":
|
38
|
+
if num_args != 2 or num_kwargs != 0:
|
39
|
+
return node
|
40
|
+
return ast.BinOp(left=args[0], op=ast.BitXor(), right=args[1])
|
41
|
+
if func == "BitwiseAnd":
|
42
|
+
if num_args != 2 or num_kwargs != 0:
|
43
|
+
return node
|
44
|
+
return ast.BinOp(left=args[0], op=ast.BitAnd(), right=args[1])
|
45
|
+
|
46
|
+
if func == "LogicalXor":
|
47
|
+
if num_args != 2 or num_kwargs != 0:
|
48
|
+
return node
|
49
|
+
return ast.BinOp(left=args[0], op=ast.BitXor(), right=args[1])
|
50
|
+
|
51
|
+
if func == "Eq":
|
52
|
+
if num_args != 2 or num_kwargs != 0:
|
53
|
+
return node
|
54
|
+
return ast.Compare(left=args[0], ops=[ast.Eq()], comparators=[args[1]])
|
55
|
+
if func == "Ne":
|
56
|
+
if num_args != 2 or num_kwargs != 0:
|
57
|
+
return node
|
58
|
+
return ast.Compare(left=args[0], ops=[ast.NotEq()], comparators=[args[1]])
|
59
|
+
if func == "Lt":
|
60
|
+
if num_args != 2 or num_kwargs != 0:
|
61
|
+
return node
|
62
|
+
return ast.Compare(left=args[0], ops=[ast.Lt()], comparators=[args[1]])
|
63
|
+
if func == "Le":
|
64
|
+
if num_args != 2 or num_kwargs != 0:
|
65
|
+
return node
|
66
|
+
return ast.Compare(left=args[0], ops=[ast.LtE()], comparators=[args[1]])
|
67
|
+
if func == "Gt":
|
68
|
+
if num_args != 2 or num_kwargs != 0:
|
69
|
+
return node
|
70
|
+
return ast.Compare(left=args[0], ops=[ast.Gt()], comparators=[args[1]])
|
71
|
+
if func == "Ge":
|
72
|
+
if num_args != 2 or num_kwargs != 0:
|
73
|
+
return node
|
74
|
+
return ast.Compare(left=args[0], ops=[ast.GtE()], comparators=[args[1]])
|
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
|
+
if func == "do_subscript":
|
82
|
+
if num_args != 2 or num_kwargs != 0:
|
83
|
+
return node
|
84
|
+
return ast.Subscript(value=args[0], slice=args[1])
|
85
|
+
|
86
|
+
if func == "get_field":
|
87
|
+
if num_args != 2 or num_kwargs != 0:
|
88
|
+
return node
|
89
|
+
attr = args[1]
|
90
|
+
if not isinstance(attr, ast.Constant):
|
91
|
+
return node
|
92
|
+
return ast.Attribute(value=args[0], attr=attr.value)
|
93
|
+
|
94
|
+
if func == "Piecewise":
|
95
|
+
if num_args == 0:
|
96
|
+
return node
|
97
|
+
first_piece = args[0]
|
98
|
+
if not isinstance(first_piece, ast.Tuple) or len(first_piece.elts) != 2:
|
99
|
+
return node
|
100
|
+
first_cond = first_piece.elts[1]
|
101
|
+
if isinstance(first_cond, ast.BinOp):
|
102
|
+
first_cond = first_cond.right
|
103
|
+
if not isinstance(first_cond, ast.Compare) or len(first_cond.ops) != 1:
|
104
|
+
return node
|
105
|
+
index_var_node = first_cond.left
|
106
|
+
if not isinstance(index_var_node, ast.Name):
|
107
|
+
return node
|
108
|
+
index_var = index_var_node.id
|
109
|
+
last_cond = args[-1]
|
110
|
+
if not isinstance(last_cond, ast.Tuple) or len(last_cond.elts) != 2:
|
111
|
+
return node
|
112
|
+
last_value = last_cond.elts[0]
|
113
|
+
if not isinstance(last_value, ast.Constant) and (
|
114
|
+
not isinstance(last_value, ast.UnaryOp)
|
115
|
+
or not isinstance(last_value.operand, ast.Constant)
|
116
|
+
):
|
117
|
+
return node
|
118
|
+
dummy_var_name = f"{index_var}_not_it"
|
119
|
+
last_cond.elts[0] = ast.Name(id=dummy_var_name)
|
120
|
+
items: list = []
|
121
|
+
idx = 0
|
122
|
+
for idx in range(MAX_PIECEWISE_LOOPS):
|
123
|
+
item = sympy.sympify(ast.unparse(node), locals={index_var: idx})
|
124
|
+
if str(item) == dummy_var_name:
|
125
|
+
items.append(last_value)
|
126
|
+
break
|
127
|
+
items.append(ast.parse(str(item), mode="eval").body)
|
128
|
+
if idx == MAX_PIECEWISE_LOOPS:
|
129
|
+
return node
|
130
|
+
return ast.Subscript(
|
131
|
+
value=ast.List(elts=items), slice=ast.Name(id=index_var)
|
132
|
+
)
|
133
|
+
|
134
|
+
return node
|
@@ -0,0 +1,232 @@
|
|
1
|
+
import ast
|
2
|
+
from collections.abc import Mapping, Sequence
|
3
|
+
from enum import IntEnum
|
4
|
+
from typing import Any, Callable, Optional
|
5
|
+
|
6
|
+
from classiq.interface.exceptions import (
|
7
|
+
ClassiqExpansionError,
|
8
|
+
ClassiqInternalExpansionError,
|
9
|
+
)
|
10
|
+
from classiq.interface.generator.arith.machine_precision import (
|
11
|
+
DEFAULT_MACHINE_PRECISION,
|
12
|
+
)
|
13
|
+
from classiq.interface.generator.functions.classical_function_declaration import (
|
14
|
+
ClassicalFunctionDeclaration,
|
15
|
+
)
|
16
|
+
from classiq.interface.generator.types.enum_declaration import EnumDeclaration
|
17
|
+
from classiq.interface.generator.types.struct_declaration import StructDeclaration
|
18
|
+
from classiq.interface.helpers.pydantic_model_helpers import nameables_to_dict
|
19
|
+
|
20
|
+
from classiq.evaluators.qmod_annotated_expression import QmodAnnotatedExpression
|
21
|
+
from classiq.evaluators.qmod_node_evaluators.attribute_evaluation import eval_attribute
|
22
|
+
from classiq.evaluators.qmod_node_evaluators.binary_op_evaluation import eval_binary_op
|
23
|
+
from classiq.evaluators.qmod_node_evaluators.bool_op_evaluation import eval_bool_op
|
24
|
+
from classiq.evaluators.qmod_node_evaluators.classical_function_evaluation import (
|
25
|
+
eval_function,
|
26
|
+
eval_symbolic_function,
|
27
|
+
try_eval_builtin_function,
|
28
|
+
try_eval_sympy_function,
|
29
|
+
)
|
30
|
+
from classiq.evaluators.qmod_node_evaluators.compare_evaluation import eval_compare
|
31
|
+
from classiq.evaluators.qmod_node_evaluators.constant_evaluation import (
|
32
|
+
eval_constant,
|
33
|
+
eval_enum_member,
|
34
|
+
try_eval_sympy_constant,
|
35
|
+
)
|
36
|
+
from classiq.evaluators.qmod_node_evaluators.list_evaluation import eval_list
|
37
|
+
from classiq.evaluators.qmod_node_evaluators.measurement_evaluation import (
|
38
|
+
eval_measurement,
|
39
|
+
)
|
40
|
+
from classiq.evaluators.qmod_node_evaluators.name_evaluation import eval_name
|
41
|
+
from classiq.evaluators.qmod_node_evaluators.struct_instantiation_evaluation import (
|
42
|
+
eval_struct_instantiation,
|
43
|
+
)
|
44
|
+
from classiq.evaluators.qmod_node_evaluators.subscript_evaluation import (
|
45
|
+
eval_quantum_subscript,
|
46
|
+
eval_subscript,
|
47
|
+
)
|
48
|
+
from classiq.evaluators.qmod_node_evaluators.unary_op_evaluation import eval_unary_op
|
49
|
+
from classiq.evaluators.qmod_node_evaluators.utils import is_classical_type
|
50
|
+
|
51
|
+
_SUPPORTED_NODES = (
|
52
|
+
ast.BoolOp,
|
53
|
+
ast.BinOp,
|
54
|
+
ast.UnaryOp,
|
55
|
+
ast.Compare,
|
56
|
+
ast.Call,
|
57
|
+
ast.Constant,
|
58
|
+
ast.Attribute,
|
59
|
+
ast.Subscript,
|
60
|
+
ast.Name,
|
61
|
+
ast.List,
|
62
|
+
ast.cmpop,
|
63
|
+
ast.operator,
|
64
|
+
ast.expr_context,
|
65
|
+
ast.keyword,
|
66
|
+
ast.unaryop,
|
67
|
+
ast.boolop,
|
68
|
+
ast.Slice,
|
69
|
+
)
|
70
|
+
|
71
|
+
|
72
|
+
class QmodExpressionEvaluator(ast.NodeVisitor):
|
73
|
+
def __init__(
|
74
|
+
self,
|
75
|
+
expr_val: QmodAnnotatedExpression,
|
76
|
+
*,
|
77
|
+
machine_precision: int = DEFAULT_MACHINE_PRECISION,
|
78
|
+
classical_struct_declarations: Optional[Sequence[StructDeclaration]] = None,
|
79
|
+
enum_declarations: Optional[Sequence[EnumDeclaration]] = None,
|
80
|
+
classical_function_declarations: Optional[
|
81
|
+
Sequence[ClassicalFunctionDeclaration]
|
82
|
+
] = None,
|
83
|
+
classical_function_callables: Optional[Mapping[str, Callable]] = None,
|
84
|
+
scope: Optional[dict[str, Any]] = None,
|
85
|
+
) -> None:
|
86
|
+
self._expr_val = expr_val
|
87
|
+
self._machine_precision = machine_precision
|
88
|
+
self._classical_struct_decls = nameables_to_dict(
|
89
|
+
classical_struct_declarations or []
|
90
|
+
)
|
91
|
+
self._enum_declarations = {decl.name: decl for decl in enum_declarations or []}
|
92
|
+
self._enums: dict[str, type[IntEnum]] = {}
|
93
|
+
self._classical_function_declarations = nameables_to_dict(
|
94
|
+
classical_function_declarations or []
|
95
|
+
)
|
96
|
+
self._classical_function_callables = classical_function_callables or {}
|
97
|
+
self._scope = scope or {}
|
98
|
+
|
99
|
+
def visit(self, node: ast.AST) -> None:
|
100
|
+
if not isinstance(node, _SUPPORTED_NODES):
|
101
|
+
raise ClassiqInternalExpansionError(
|
102
|
+
f"Syntax error: {type(node).__name__!r} is not supported"
|
103
|
+
)
|
104
|
+
super().visit(node)
|
105
|
+
|
106
|
+
def visit_BoolOp(self, node: ast.BoolOp) -> None:
|
107
|
+
super().generic_visit(node)
|
108
|
+
eval_bool_op(self._expr_val, node)
|
109
|
+
|
110
|
+
def visit_BinOp(self, node: ast.BinOp) -> None:
|
111
|
+
super().generic_visit(node)
|
112
|
+
eval_binary_op(self._expr_val, node)
|
113
|
+
|
114
|
+
def visit_UnaryOp(self, node: ast.UnaryOp) -> None:
|
115
|
+
super().generic_visit(node)
|
116
|
+
eval_unary_op(self._expr_val, node)
|
117
|
+
|
118
|
+
def visit_Compare(self, node: ast.Compare) -> None:
|
119
|
+
super().generic_visit(node)
|
120
|
+
eval_compare(self._expr_val, node)
|
121
|
+
|
122
|
+
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
|
+
func = node.func
|
129
|
+
if not isinstance(func, ast.Name):
|
130
|
+
raise ClassiqExpansionError(
|
131
|
+
f"Function {ast.unparse(node.func)!r} is not supported"
|
132
|
+
)
|
133
|
+
func_name = func.id
|
134
|
+
|
135
|
+
if func_name == "measure":
|
136
|
+
eval_measurement(self._expr_val, node)
|
137
|
+
return
|
138
|
+
|
139
|
+
if func_name in self._classical_struct_decls:
|
140
|
+
eval_struct_instantiation(
|
141
|
+
self._expr_val, node, self._classical_struct_decls[func_name]
|
142
|
+
)
|
143
|
+
return
|
144
|
+
|
145
|
+
if func_name in self._classical_function_callables:
|
146
|
+
if func_name not in self._classical_function_declarations:
|
147
|
+
raise ClassiqInternalExpansionError
|
148
|
+
eval_function(
|
149
|
+
self._expr_val,
|
150
|
+
node,
|
151
|
+
self._classical_function_declarations[func_name],
|
152
|
+
self._classical_function_callables[func_name],
|
153
|
+
)
|
154
|
+
return
|
155
|
+
|
156
|
+
if func_name in self._classical_function_declarations:
|
157
|
+
eval_symbolic_function(
|
158
|
+
self._expr_val, node, self._classical_function_declarations[func_name]
|
159
|
+
)
|
160
|
+
return
|
161
|
+
|
162
|
+
if try_eval_builtin_function(self._expr_val, node, func_name):
|
163
|
+
return
|
164
|
+
|
165
|
+
if try_eval_sympy_function(self._expr_val, node, func_name):
|
166
|
+
return
|
167
|
+
|
168
|
+
raise ClassiqExpansionError(f"{func.id!r} is undefined")
|
169
|
+
|
170
|
+
def visit_Constant(self, node: ast.Constant) -> None:
|
171
|
+
eval_constant(self._expr_val, node)
|
172
|
+
|
173
|
+
def visit_Attribute(self, node: ast.Attribute) -> None:
|
174
|
+
if (
|
175
|
+
isinstance(node.value, ast.Name)
|
176
|
+
and (enum_name := node.value.id) in self._enum_declarations
|
177
|
+
):
|
178
|
+
if enum_name not in self._enums:
|
179
|
+
self._enums[enum_name] = self._enum_declarations[
|
180
|
+
enum_name
|
181
|
+
].create_enum()
|
182
|
+
eval_enum_member(self._expr_val, node, self._enums[enum_name])
|
183
|
+
return
|
184
|
+
super().generic_visit(node)
|
185
|
+
eval_attribute(self._expr_val, node)
|
186
|
+
|
187
|
+
def visit_Subscript(self, node: ast.Subscript) -> None:
|
188
|
+
super().generic_visit(node)
|
189
|
+
if not isinstance(node.slice, ast.Slice) and not is_classical_type(
|
190
|
+
self._expr_val.get_type(node.slice)
|
191
|
+
):
|
192
|
+
eval_quantum_subscript(self._expr_val, node, self._machine_precision)
|
193
|
+
return
|
194
|
+
eval_subscript(self._expr_val, node)
|
195
|
+
|
196
|
+
def visit_Name(self, node: ast.Name) -> None:
|
197
|
+
if try_eval_sympy_constant(self._expr_val, node):
|
198
|
+
return
|
199
|
+
|
200
|
+
if node.id not in self._scope:
|
201
|
+
raise ClassiqExpansionError(f"Variable {node.id!r} is undefined")
|
202
|
+
eval_name(self._expr_val, node, self._scope[node.id])
|
203
|
+
|
204
|
+
def visit_List(self, node: ast.List) -> None:
|
205
|
+
super().generic_visit(node)
|
206
|
+
eval_list(self._expr_val, node)
|
207
|
+
|
208
|
+
|
209
|
+
def evaluate_qmod_expression(
|
210
|
+
expr: str,
|
211
|
+
*,
|
212
|
+
machine_precision: int = DEFAULT_MACHINE_PRECISION,
|
213
|
+
classical_struct_declarations: Optional[Sequence[StructDeclaration]] = None,
|
214
|
+
enum_declarations: Optional[Sequence[EnumDeclaration]] = None,
|
215
|
+
classical_function_declarations: Optional[
|
216
|
+
Sequence[ClassicalFunctionDeclaration]
|
217
|
+
] = None,
|
218
|
+
classical_function_callables: Optional[Mapping[str, Callable]] = None,
|
219
|
+
scope: Optional[dict[str, Any]] = None,
|
220
|
+
) -> QmodAnnotatedExpression:
|
221
|
+
expr_ast = ast.parse(expr, mode="eval").body
|
222
|
+
expr_value = QmodAnnotatedExpression(expr_ast)
|
223
|
+
QmodExpressionEvaluator(
|
224
|
+
expr_value,
|
225
|
+
machine_precision=machine_precision,
|
226
|
+
classical_struct_declarations=classical_struct_declarations,
|
227
|
+
enum_declarations=enum_declarations,
|
228
|
+
classical_function_declarations=classical_function_declarations,
|
229
|
+
classical_function_callables=classical_function_callables,
|
230
|
+
scope=scope,
|
231
|
+
).visit(expr_value.root)
|
232
|
+
return expr_value
|
@@ -0,0 +1,44 @@
|
|
1
|
+
from typing import Optional
|
2
|
+
|
3
|
+
from classiq.interface.model.handle_binding import HandleBinding, NestedHandleBinding
|
4
|
+
|
5
|
+
from classiq.evaluators.qmod_annotated_expression import (
|
6
|
+
QmodAnnotatedExpression,
|
7
|
+
QmodExprNodeId,
|
8
|
+
)
|
9
|
+
|
10
|
+
|
11
|
+
def rename_handles_in_expression(
|
12
|
+
expr_val: QmodAnnotatedExpression,
|
13
|
+
renaming: dict[HandleBinding, HandleBinding],
|
14
|
+
) -> str:
|
15
|
+
if len(renaming) == 0:
|
16
|
+
return expr_val.to_qmod_expr()
|
17
|
+
all_vars = expr_val.get_classical_vars() | expr_val.get_quantum_vars()
|
18
|
+
for node_id, var in all_vars.items():
|
19
|
+
renamed_var = _rename_var(renaming, var)
|
20
|
+
if renamed_var is not None:
|
21
|
+
expr_val.set_var(node_id, renamed_var)
|
22
|
+
return expr_val.to_qmod_expr()
|
23
|
+
|
24
|
+
|
25
|
+
def _rename_var(
|
26
|
+
renaming: dict[HandleBinding, HandleBinding], var: HandleBinding
|
27
|
+
) -> Optional[HandleBinding]:
|
28
|
+
if (renamed_var := renaming.get(var)) is not None:
|
29
|
+
return renamed_var
|
30
|
+
if not isinstance(var, NestedHandleBinding):
|
31
|
+
return None
|
32
|
+
renamed_inner = _rename_var(renaming, var.base_handle)
|
33
|
+
if renamed_inner is None:
|
34
|
+
return None
|
35
|
+
return var.model_copy(update=dict(base_handle=renamed_inner))
|
36
|
+
|
37
|
+
|
38
|
+
def rename_nodes_in_expression(
|
39
|
+
expr_val: QmodAnnotatedExpression,
|
40
|
+
renaming: dict[QmodExprNodeId, HandleBinding],
|
41
|
+
) -> str:
|
42
|
+
for node_id, renamed_var in renaming.items():
|
43
|
+
expr_val.set_var(node_id, renamed_var)
|
44
|
+
return expr_val.to_qmod_expr()
|