classiq 0.56.1__py3-none-any.whl → 0.58.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/analyzer/show_interactive_hack.py +16 -4
- classiq/applications/combinatorial_helpers/encoding_utils.py +1 -0
- classiq/applications/combinatorial_helpers/transformations/encoding.py +3 -1
- classiq/execution/jobs.py +8 -1
- classiq/executor.py +1 -1
- classiq/interface/_version.py +1 -1
- classiq/interface/backend/backend_preferences.py +27 -5
- classiq/interface/backend/pydantic_backend.py +0 -1
- classiq/interface/execution/jobs.py +4 -1
- classiq/interface/executor/execution_request.py +19 -5
- classiq/interface/generator/arith/arithmetic_expression_validator.py +28 -9
- classiq/interface/generator/functions/type_name.py +7 -9
- classiq/interface/generator/types/builtin_enum_declarations.py +1 -0
- classiq/model_expansions/closure.py +24 -6
- classiq/model_expansions/evaluators/parameter_types.py +1 -2
- classiq/model_expansions/evaluators/quantum_type_utils.py +0 -7
- classiq/model_expansions/function_builder.py +13 -0
- classiq/model_expansions/interpreter.py +9 -14
- classiq/model_expansions/quantum_operations/call_emitter.py +207 -0
- classiq/model_expansions/quantum_operations/classicalif.py +2 -2
- classiq/model_expansions/quantum_operations/control.py +7 -5
- classiq/model_expansions/quantum_operations/emitter.py +1 -186
- classiq/model_expansions/quantum_operations/expression_operation.py +26 -189
- classiq/model_expansions/quantum_operations/inplace_binary_operation.py +2 -2
- classiq/model_expansions/quantum_operations/invert.py +2 -2
- classiq/model_expansions/quantum_operations/phase.py +3 -1
- classiq/model_expansions/quantum_operations/power.py +2 -2
- classiq/model_expansions/quantum_operations/quantum_assignment_operation.py +7 -9
- classiq/model_expansions/quantum_operations/quantum_function_call.py +2 -2
- classiq/model_expansions/quantum_operations/repeat.py +2 -2
- classiq/model_expansions/transformers/__init__.py +0 -0
- classiq/model_expansions/transformers/var_splitter.py +237 -0
- classiq/qmod/builtins/classical_functions.py +1 -0
- classiq/qmod/builtins/functions/state_preparation.py +1 -1
- classiq/qmod/create_model_function.py +25 -20
- classiq/qmod/native/pretty_printer.py +19 -4
- classiq/qmod/pretty_print/pretty_printer.py +53 -28
- classiq/qmod/qfunc.py +18 -16
- classiq/qmod/quantum_function.py +30 -24
- classiq/qmod/semantics/qstruct_annotator.py +23 -0
- classiq/qmod/semantics/static_semantics_visitor.py +4 -1
- classiq/qmod/write_qmod.py +3 -1
- classiq/synthesis.py +3 -1
- {classiq-0.56.1.dist-info → classiq-0.58.0.dist-info}/METADATA +1 -1
- {classiq-0.56.1.dist-info → classiq-0.58.0.dist-info}/RECORD +46 -42
- {classiq-0.56.1.dist-info → classiq-0.58.0.dist-info}/WHEEL +0 -0
@@ -1,79 +1,59 @@
|
|
1
1
|
import ast
|
2
|
-
from
|
3
|
-
from itertools import chain
|
4
|
-
from typing import TYPE_CHECKING, TypeVar, Union
|
2
|
+
from typing import TYPE_CHECKING, Generic, TypeVar, Union
|
5
3
|
|
6
|
-
from classiq.interface.exceptions import (
|
7
|
-
ClassiqExpansionError,
|
8
|
-
ClassiqInternalExpansionError,
|
9
|
-
)
|
10
4
|
from classiq.interface.generator.expressions.evaluated_expression import (
|
11
5
|
EvaluatedExpression,
|
12
6
|
)
|
13
7
|
from classiq.interface.generator.expressions.expression import Expression
|
14
|
-
from classiq.interface.generator.
|
15
|
-
from classiq.interface.generator.visitor import NodeType, Transformer
|
16
|
-
from classiq.interface.model.bind_operation import BindOperation
|
8
|
+
from classiq.interface.generator.visitor import NodeType
|
17
9
|
from classiq.interface.model.control import Control
|
18
|
-
from classiq.interface.model.handle_binding import (
|
19
|
-
FieldHandleBinding,
|
20
|
-
HandleBinding,
|
21
|
-
SlicedHandleBinding,
|
22
|
-
SubscriptHandleBinding,
|
23
|
-
)
|
24
10
|
from classiq.interface.model.quantum_expressions.quantum_expression import (
|
25
11
|
QuantumAssignmentOperation,
|
26
12
|
QuantumExpressionOperation,
|
27
13
|
)
|
28
14
|
from classiq.interface.model.quantum_type import (
|
29
|
-
QuantumBit,
|
30
|
-
QuantumBitvector,
|
31
15
|
QuantumNumeric,
|
32
|
-
QuantumType,
|
33
|
-
)
|
34
|
-
from classiq.interface.model.variable_declaration_statement import (
|
35
|
-
VariableDeclarationStatement,
|
36
16
|
)
|
37
17
|
from classiq.interface.model.within_apply_operation import WithinApply
|
38
18
|
|
39
|
-
from classiq.model_expansions.quantum_operations.
|
19
|
+
from classiq.model_expansions.quantum_operations.call_emitter import CallEmitter
|
40
20
|
from classiq.model_expansions.scope import QuantumSymbol
|
21
|
+
from classiq.model_expansions.transformers.var_splitter import (
|
22
|
+
SymbolParts,
|
23
|
+
VarSplitter,
|
24
|
+
)
|
41
25
|
from classiq.model_expansions.visitors.variable_references import VarRefCollector
|
42
26
|
|
27
|
+
if TYPE_CHECKING:
|
28
|
+
from classiq.model_expansions.interpreter import Interpreter
|
29
|
+
|
43
30
|
ExpressionOperationT = TypeVar("ExpressionOperationT", bound=QuantumExpressionOperation)
|
44
31
|
AST_NODE = TypeVar("AST_NODE", bound=NodeType)
|
45
32
|
|
46
33
|
|
47
|
-
class ExpressionOperationEmitter(
|
48
|
-
|
49
|
-
|
50
|
-
|
34
|
+
class ExpressionOperationEmitter(
|
35
|
+
Generic[ExpressionOperationT], CallEmitter[ExpressionOperationT], VarSplitter
|
36
|
+
):
|
37
|
+
def __init__(self, interpreter: "Interpreter") -> None:
|
38
|
+
CallEmitter.__init__(self, interpreter) # type:ignore[arg-type]
|
39
|
+
VarSplitter.__init__(self, interpreter._current_scope)
|
51
40
|
|
52
41
|
def _emit_with_split(
|
53
42
|
self,
|
54
43
|
op: ExpressionOperationT,
|
55
44
|
expression: Expression,
|
56
|
-
|
45
|
+
symbol_parts: SymbolParts,
|
57
46
|
) -> None:
|
58
|
-
|
47
|
+
for var_decl in self.get_var_decls(symbol_parts):
|
48
|
+
self._interpreter.emit_statement(var_decl)
|
49
|
+
bind_ops = self.get_bind_ops(symbol_parts)
|
59
50
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
VariableDeclarationStatement(
|
65
|
-
name=symbol_part_var_name,
|
66
|
-
quantum_type=symbol.quantum_type,
|
67
|
-
)
|
68
|
-
)
|
69
|
-
|
70
|
-
symbol_mapping = {
|
71
|
-
symbol.handle: (symbol_part_var_name, symbol.quantum_type)
|
72
|
-
for symbol, symbol_part_var_name in chain.from_iterable(symbols_parts)
|
73
|
-
}
|
74
|
-
new_expression = self._update_op_expression(symbol_mapping, expression)
|
51
|
+
new_expression = self.rewrite(expression, symbol_parts)
|
52
|
+
new_expression._evaluated_expr = EvaluatedExpression(
|
53
|
+
value=self._interpreter.evaluate(new_expression).value
|
54
|
+
)
|
75
55
|
new_op = op.model_copy(update=dict(expression=new_expression))
|
76
|
-
new_op = self._get_updated_op_split_symbols(new_op,
|
56
|
+
new_op = self._get_updated_op_split_symbols(new_op, symbol_parts)
|
77
57
|
|
78
58
|
self._interpreter.emit_statement(
|
79
59
|
WithinApply(
|
@@ -86,125 +66,10 @@ class ExpressionOperationEmitter(Emitter[ExpressionOperationT]):
|
|
86
66
|
def _get_updated_op_split_symbols(
|
87
67
|
self,
|
88
68
|
op: ExpressionOperationT,
|
89
|
-
symbol_mapping:
|
69
|
+
symbol_mapping: SymbolParts,
|
90
70
|
) -> ExpressionOperationT:
|
91
71
|
return op
|
92
72
|
|
93
|
-
def _update_op_expression(
|
94
|
-
self,
|
95
|
-
symbol_mapping: dict[HandleBinding, tuple[str, QuantumType]],
|
96
|
-
expression: Expression,
|
97
|
-
) -> Expression:
|
98
|
-
vrc = VarRefCollector(ignore_duplicated_handles=True)
|
99
|
-
vrc.visit(ast.parse(expression.expr))
|
100
|
-
|
101
|
-
new_expr_str = expression.expr
|
102
|
-
for handle in vrc.var_handles:
|
103
|
-
collapsed_handle = handle.collapse()
|
104
|
-
if collapsed_handle in symbol_mapping:
|
105
|
-
new_expr_str = new_expr_str.replace(
|
106
|
-
str(handle), symbol_mapping[collapsed_handle][0]
|
107
|
-
)
|
108
|
-
self._check_all_handles_were_replaced(new_expr_str)
|
109
|
-
|
110
|
-
new_expr = Expression(expr=new_expr_str)
|
111
|
-
new_expr._evaluated_expr = EvaluatedExpression(
|
112
|
-
value=self._interpreter.evaluate(new_expr).value
|
113
|
-
)
|
114
|
-
return new_expr
|
115
|
-
|
116
|
-
def _check_all_handles_were_replaced(self, new_expr_str: str) -> None:
|
117
|
-
vrc = VarRefCollector(ignore_duplicated_handles=True)
|
118
|
-
vrc.visit(ast.parse(new_expr_str))
|
119
|
-
for handle in self._get_handles(vrc):
|
120
|
-
if isinstance(
|
121
|
-
handle,
|
122
|
-
(SubscriptHandleBinding, SlicedHandleBinding, FieldHandleBinding),
|
123
|
-
):
|
124
|
-
raise ClassiqInternalExpansionError(f"Did not replace handle {handle}")
|
125
|
-
|
126
|
-
def _get_bind_ops(
|
127
|
-
self,
|
128
|
-
symbols_to_split: dict[QuantumSymbol, set[HandleBinding]],
|
129
|
-
) -> tuple[list[list[tuple[QuantumSymbol, str]]], list[BindOperation]]:
|
130
|
-
bind_ops = []
|
131
|
-
symbols_parts = []
|
132
|
-
for symbol, target_parts in symbols_to_split.items():
|
133
|
-
symbol_parts = self._get_symbol_parts(symbol, target_parts)
|
134
|
-
symbols_parts.append(symbol_parts)
|
135
|
-
bind_ops.append(
|
136
|
-
BindOperation(
|
137
|
-
in_handles=[symbol.handle],
|
138
|
-
out_handles=[
|
139
|
-
HandleBinding(name=symbol_part_var_name)
|
140
|
-
for _, symbol_part_var_name in symbol_parts
|
141
|
-
],
|
142
|
-
)
|
143
|
-
)
|
144
|
-
return symbols_parts, bind_ops
|
145
|
-
|
146
|
-
def _get_symbol_parts(
|
147
|
-
self, symbol: QuantumSymbol, target_parts: set[HandleBinding]
|
148
|
-
) -> list[tuple[QuantumSymbol, str]]:
|
149
|
-
quantum_type = symbol.quantum_type
|
150
|
-
|
151
|
-
if all(
|
152
|
-
symbol.handle == target_part or symbol.handle not in target_part.prefixes()
|
153
|
-
for target_part in target_parts
|
154
|
-
) or isinstance(quantum_type, (QuantumBit, QuantumNumeric)):
|
155
|
-
return [
|
156
|
-
(
|
157
|
-
symbol,
|
158
|
-
self._counted_name_allocator.allocate(symbol.handle.identifier),
|
159
|
-
)
|
160
|
-
]
|
161
|
-
|
162
|
-
if isinstance(quantum_type, QuantumBitvector):
|
163
|
-
if not quantum_type.has_length:
|
164
|
-
raise ClassiqExpansionError(
|
165
|
-
f"Could not determine the length of quantum array "
|
166
|
-
f"{symbol.handle}."
|
167
|
-
)
|
168
|
-
return list(
|
169
|
-
chain.from_iterable(
|
170
|
-
self._get_symbol_parts(symbol[idx], target_parts)
|
171
|
-
for idx in range(quantum_type.length_value)
|
172
|
-
)
|
173
|
-
)
|
174
|
-
|
175
|
-
if TYPE_CHECKING:
|
176
|
-
assert isinstance(quantum_type, TypeName)
|
177
|
-
|
178
|
-
return list(
|
179
|
-
chain.from_iterable(
|
180
|
-
self._get_symbol_parts(field_symbol, target_parts)
|
181
|
-
for field_symbol in symbol.fields.values()
|
182
|
-
)
|
183
|
-
)
|
184
|
-
|
185
|
-
def _get_symbols_to_split(
|
186
|
-
self, expression: Expression
|
187
|
-
) -> dict[QuantumSymbol, set[HandleBinding]]:
|
188
|
-
vrc = VarRefCollector(ignore_duplicated_handles=True)
|
189
|
-
vrc.visit(ast.parse(expression.expr))
|
190
|
-
symbol_names_to_split = dict.fromkeys(
|
191
|
-
handle.name
|
192
|
-
for handle in self._get_handles(vrc)
|
193
|
-
if isinstance(handle, (SubscriptHandleBinding, FieldHandleBinding))
|
194
|
-
)
|
195
|
-
return {
|
196
|
-
symbol: {
|
197
|
-
handle.collapse()
|
198
|
-
for handle in vrc.var_handles
|
199
|
-
if handle.name == symbol.handle.name
|
200
|
-
}
|
201
|
-
for symbol_name in symbol_names_to_split
|
202
|
-
if isinstance(
|
203
|
-
symbol := self._current_scope[symbol_name].value,
|
204
|
-
QuantumSymbol,
|
205
|
-
)
|
206
|
-
}
|
207
|
-
|
208
73
|
def _evaluate_op_expression(self, op: ExpressionOperationT) -> Expression:
|
209
74
|
return self._evaluate_expression(op.expression)
|
210
75
|
|
@@ -248,31 +113,3 @@ class ExpressionOperationEmitter(Emitter[ExpressionOperationT]):
|
|
248
113
|
isinstance(op.result_type, QuantumNumeric)
|
249
114
|
and (op.result_type.sign_value or op.result_type.fraction_digits_value > 0)
|
250
115
|
)
|
251
|
-
|
252
|
-
def _get_handles(self, collector: VarRefCollector) -> list[HandleBinding]:
|
253
|
-
return [
|
254
|
-
handle
|
255
|
-
for handle in collector.var_handles
|
256
|
-
if isinstance(self._interpreter.evaluate(handle.name).value, QuantumSymbol)
|
257
|
-
]
|
258
|
-
|
259
|
-
def _rewrite(
|
260
|
-
self,
|
261
|
-
subject: AST_NODE,
|
262
|
-
symbol_mapping: dict[HandleBinding, tuple[str, QuantumType]],
|
263
|
-
) -> AST_NODE:
|
264
|
-
class ReplaceSplitVars(Transformer):
|
265
|
-
@staticmethod
|
266
|
-
def visit_HandleBinding(handle: HandleBinding) -> HandleBinding:
|
267
|
-
handle = handle.collapse()
|
268
|
-
for handle_to_replace, replacement in symbol_mapping.items():
|
269
|
-
handle = handle.replace_prefix(
|
270
|
-
handle_to_replace, HandleBinding(name=replacement[0])
|
271
|
-
)
|
272
|
-
return handle
|
273
|
-
|
274
|
-
@staticmethod
|
275
|
-
def visit_Expression(expr: Expression) -> Expression:
|
276
|
-
return self._update_op_expression(symbol_mapping, expr)
|
277
|
-
|
278
|
-
return ReplaceSplitVars().visit(subject)
|
@@ -31,7 +31,7 @@ from classiq.model_expansions.evaluators.parameter_types import (
|
|
31
31
|
from classiq.model_expansions.evaluators.quantum_type_utils import (
|
32
32
|
validate_inplace_binary_op_vars,
|
33
33
|
)
|
34
|
-
from classiq.model_expansions.quantum_operations.
|
34
|
+
from classiq.model_expansions.quantum_operations.call_emitter import CallEmitter
|
35
35
|
from classiq.model_expansions.scope import QuantumSymbol, Scope
|
36
36
|
from classiq.qmod.builtins.functions import (
|
37
37
|
CX,
|
@@ -59,7 +59,7 @@ def _binary_function_declaration(
|
|
59
59
|
}[constant][op]
|
60
60
|
|
61
61
|
|
62
|
-
class InplaceBinaryOperationEmitter(
|
62
|
+
class InplaceBinaryOperationEmitter(CallEmitter[InplaceBinaryOperation]):
|
63
63
|
def emit(self, op: InplaceBinaryOperation, /) -> None:
|
64
64
|
if isinstance(op.value, Expression):
|
65
65
|
self._emit_constant_operation(op)
|
@@ -4,11 +4,11 @@ from classiq.interface.generator.functions.builtins.internal_operators import (
|
|
4
4
|
from classiq.interface.model.invert import Invert
|
5
5
|
|
6
6
|
from classiq.model_expansions.closure import Closure
|
7
|
-
from classiq.model_expansions.quantum_operations.
|
7
|
+
from classiq.model_expansions.quantum_operations.call_emitter import CallEmitter
|
8
8
|
from classiq.model_expansions.scope import Scope
|
9
9
|
|
10
10
|
|
11
|
-
class InvertEmitter(
|
11
|
+
class InvertEmitter(CallEmitter[Invert]):
|
12
12
|
def emit(self, invert: Invert, /) -> None:
|
13
13
|
with self._propagated_var_stack.capture_variables(invert):
|
14
14
|
self._emit_propagated(invert)
|
@@ -41,7 +41,9 @@ class PhaseEmitter(ExpressionOperationEmitter[PhaseOperation]):
|
|
41
41
|
def emit(self, phase_op: PhaseOperation, /) -> None:
|
42
42
|
phase_expression = self._evaluate_op_expression(phase_op)
|
43
43
|
phase_op = phase_op.model_copy(update=dict(expression=phase_expression))
|
44
|
-
arrays_with_subscript = self.
|
44
|
+
arrays_with_subscript = self.split_symbols(
|
45
|
+
phase_op.expression, self._counted_name_allocator.allocate
|
46
|
+
)
|
45
47
|
if len(arrays_with_subscript) > 0:
|
46
48
|
self._emit_with_split(phase_op, phase_op.expression, arrays_with_subscript)
|
47
49
|
return
|
@@ -13,11 +13,11 @@ from classiq.interface.generator.functions.builtins.internal_operators import (
|
|
13
13
|
from classiq.interface.model.power import Power
|
14
14
|
|
15
15
|
from classiq.model_expansions.closure import Closure
|
16
|
-
from classiq.model_expansions.quantum_operations.
|
16
|
+
from classiq.model_expansions.quantum_operations.call_emitter import CallEmitter
|
17
17
|
from classiq.model_expansions.scope import Scope
|
18
18
|
|
19
19
|
|
20
|
-
class PowerEmitter(
|
20
|
+
class PowerEmitter(CallEmitter[Power]):
|
21
21
|
_power_value: Union[int, sympy.Basic]
|
22
22
|
_power_expr: Expression
|
23
23
|
|
@@ -20,10 +20,7 @@ from classiq.interface.model.quantum_expressions.quantum_expression import (
|
|
20
20
|
QuantumAssignmentOperation,
|
21
21
|
)
|
22
22
|
from classiq.interface.model.quantum_function_call import QuantumFunctionCall
|
23
|
-
from classiq.interface.model.quantum_type import
|
24
|
-
QuantumNumeric,
|
25
|
-
QuantumType,
|
26
|
-
)
|
23
|
+
from classiq.interface.model.quantum_type import QuantumNumeric
|
27
24
|
from classiq.interface.model.variable_declaration_statement import (
|
28
25
|
VariableDeclarationStatement,
|
29
26
|
)
|
@@ -35,6 +32,7 @@ from classiq.model_expansions.quantum_operations.expression_operation import (
|
|
35
32
|
ExpressionOperationEmitter,
|
36
33
|
)
|
37
34
|
from classiq.model_expansions.scope import QuantumSymbol
|
35
|
+
from classiq.model_expansions.transformers.var_splitter import SymbolParts
|
38
36
|
from classiq.model_expansions.visitors.boolean_expression_transformers import (
|
39
37
|
BooleanExpressionFuncLibAdapter,
|
40
38
|
BooleanExpressionOptimizer,
|
@@ -70,7 +68,9 @@ class QuantumAssignmentOperationEmitter(
|
|
70
68
|
new_expression = self._evaluate_op_expression(op)
|
71
69
|
if self._skip_assignment(op, new_expression.expr):
|
72
70
|
return
|
73
|
-
arrays_with_subscript = self.
|
71
|
+
arrays_with_subscript = self.split_symbols(
|
72
|
+
new_expression, self._counted_name_allocator.allocate
|
73
|
+
)
|
74
74
|
if len(arrays_with_subscript) > 0:
|
75
75
|
self._emit_with_split(op, new_expression, arrays_with_subscript)
|
76
76
|
return
|
@@ -230,12 +230,10 @@ class QuantumAssignmentOperationEmitter(
|
|
230
230
|
return True
|
231
231
|
|
232
232
|
def _get_updated_op_split_symbols(
|
233
|
-
self,
|
234
|
-
op: QuantumAssignmentOperation,
|
235
|
-
symbol_mapping: dict[HandleBinding, tuple[str, QuantumType]],
|
233
|
+
self, op: QuantumAssignmentOperation, symbol_parts: SymbolParts
|
236
234
|
) -> QuantumAssignmentOperation:
|
237
235
|
return op.model_copy(
|
238
|
-
update=dict(result_var=self.
|
236
|
+
update=dict(result_var=self.rewrite(op.result_var, symbol_parts))
|
239
237
|
)
|
240
238
|
|
241
239
|
|
@@ -3,14 +3,14 @@ from typing import TYPE_CHECKING
|
|
3
3
|
from classiq.interface.model.quantum_function_call import QuantumFunctionCall
|
4
4
|
|
5
5
|
from classiq.model_expansions.closure import FunctionClosure
|
6
|
-
from classiq.model_expansions.quantum_operations.
|
6
|
+
from classiq.model_expansions.quantum_operations.call_emitter import CallEmitter
|
7
7
|
from classiq.qmod.semantics.error_manager import ErrorManager
|
8
8
|
|
9
9
|
if TYPE_CHECKING:
|
10
10
|
from classiq.model_expansions.interpreter import Interpreter
|
11
11
|
|
12
12
|
|
13
|
-
class QuantumFunctionCallEmitter(
|
13
|
+
class QuantumFunctionCallEmitter(CallEmitter[QuantumFunctionCall]):
|
14
14
|
def __init__(self, interpreter: "Interpreter") -> None:
|
15
15
|
super().__init__(interpreter)
|
16
16
|
self._model = self._interpreter._model
|
@@ -10,12 +10,12 @@ from classiq.interface.model.classical_parameter_declaration import (
|
|
10
10
|
from classiq.interface.model.repeat import Repeat
|
11
11
|
|
12
12
|
from classiq.model_expansions.closure import FunctionClosure, GenerativeFunctionClosure
|
13
|
-
from classiq.model_expansions.quantum_operations.
|
13
|
+
from classiq.model_expansions.quantum_operations.call_emitter import CallEmitter
|
14
14
|
from classiq.model_expansions.scope import Scope
|
15
15
|
from classiq.qmod.quantum_function import GenerativeQFunc
|
16
16
|
|
17
17
|
|
18
|
-
class RepeatEmitter(
|
18
|
+
class RepeatEmitter(CallEmitter[Repeat]):
|
19
19
|
def emit(self, repeat: Repeat, /) -> None:
|
20
20
|
count = self._interpreter.evaluate(repeat.count).as_type(int)
|
21
21
|
if count < 0:
|
File without changes
|
@@ -0,0 +1,237 @@
|
|
1
|
+
import ast
|
2
|
+
from dataclasses import dataclass
|
3
|
+
from itertools import chain
|
4
|
+
from typing import TYPE_CHECKING, Callable, TypeVar
|
5
|
+
|
6
|
+
from classiq.interface.exceptions import (
|
7
|
+
ClassiqExpansionError,
|
8
|
+
ClassiqInternalExpansionError,
|
9
|
+
)
|
10
|
+
from classiq.interface.generator.expressions.expression import Expression
|
11
|
+
from classiq.interface.generator.visitor import NodeType, Transformer
|
12
|
+
from classiq.interface.model.bind_operation import BindOperation
|
13
|
+
from classiq.interface.model.handle_binding import (
|
14
|
+
HandleBinding,
|
15
|
+
NestedHandleBinding,
|
16
|
+
SlicedHandleBinding,
|
17
|
+
)
|
18
|
+
from classiq.interface.model.quantum_type import (
|
19
|
+
QuantumBitvector,
|
20
|
+
QuantumScalar,
|
21
|
+
QuantumType,
|
22
|
+
)
|
23
|
+
from classiq.interface.model.variable_declaration_statement import (
|
24
|
+
VariableDeclarationStatement,
|
25
|
+
)
|
26
|
+
|
27
|
+
from classiq.model_expansions.scope import QuantumSymbol, Scope
|
28
|
+
from classiq.model_expansions.visitors.variable_references import VarRefCollector
|
29
|
+
|
30
|
+
AST_NODE = TypeVar("AST_NODE", bound=NodeType)
|
31
|
+
|
32
|
+
|
33
|
+
@dataclass(frozen=True)
|
34
|
+
class SymbolPart:
|
35
|
+
source_handle: HandleBinding
|
36
|
+
target_var_name: str
|
37
|
+
target_var_type: QuantumType
|
38
|
+
|
39
|
+
@property
|
40
|
+
def target_var_handle(self) -> HandleBinding:
|
41
|
+
return HandleBinding(name=self.target_var_name)
|
42
|
+
|
43
|
+
|
44
|
+
SymbolParts = dict[QuantumSymbol, list[SymbolPart]]
|
45
|
+
PartNamer = Callable[[str], str]
|
46
|
+
|
47
|
+
|
48
|
+
class VarSplitter:
|
49
|
+
def __init__(self, scope: Scope):
|
50
|
+
self._scope = scope
|
51
|
+
|
52
|
+
def split_symbols(self, expression: Expression, namer: PartNamer) -> SymbolParts:
|
53
|
+
vrc = VarRefCollector(ignore_duplicated_handles=True)
|
54
|
+
vrc.visit(ast.parse(expression.expr))
|
55
|
+
symbol_names_to_split = dict.fromkeys(
|
56
|
+
handle.name
|
57
|
+
for handle in self._get_handles(vrc)
|
58
|
+
if isinstance(handle, NestedHandleBinding)
|
59
|
+
)
|
60
|
+
|
61
|
+
symbol_handles = {
|
62
|
+
symbol: list(
|
63
|
+
dict.fromkeys(
|
64
|
+
handle.collapse()
|
65
|
+
for handle in vrc.var_handles
|
66
|
+
if handle.name == symbol.handle.name
|
67
|
+
)
|
68
|
+
)
|
69
|
+
for symbol_name in symbol_names_to_split
|
70
|
+
if isinstance(
|
71
|
+
symbol := self._scope[symbol_name].value,
|
72
|
+
QuantumSymbol,
|
73
|
+
)
|
74
|
+
}
|
75
|
+
|
76
|
+
return {
|
77
|
+
symbol: [
|
78
|
+
SymbolPart(
|
79
|
+
source_handle=part.handle,
|
80
|
+
target_var_name=namer(part.handle.identifier),
|
81
|
+
target_var_type=part.quantum_type,
|
82
|
+
)
|
83
|
+
for part in self._get_symbol_parts(symbol, handles)
|
84
|
+
]
|
85
|
+
for symbol, handles in symbol_handles.items()
|
86
|
+
}
|
87
|
+
|
88
|
+
def _get_handles(self, collector: VarRefCollector) -> list[HandleBinding]:
|
89
|
+
return [
|
90
|
+
handle
|
91
|
+
for handle in collector.var_handles
|
92
|
+
if isinstance(self._scope[handle.name].value, QuantumSymbol)
|
93
|
+
]
|
94
|
+
|
95
|
+
def _get_symbol_parts(
|
96
|
+
self, symbol: QuantumSymbol, target_parts: list[HandleBinding]
|
97
|
+
) -> list[QuantumSymbol]:
|
98
|
+
for i in range(len(target_parts)):
|
99
|
+
for j in range(i + 1, len(target_parts)):
|
100
|
+
if target_parts[i].overlaps(target_parts[j]):
|
101
|
+
raise ClassiqInternalExpansionError(
|
102
|
+
f"Handles {str(target_parts[i])!r} and "
|
103
|
+
f"{str(target_parts[j])!r} overlapping in expression"
|
104
|
+
)
|
105
|
+
return self._get_symbol_parts_unsafe(symbol, target_parts)
|
106
|
+
|
107
|
+
def _get_symbol_parts_unsafe(
|
108
|
+
self, symbol: QuantumSymbol, target_parts: list[HandleBinding]
|
109
|
+
) -> list[QuantumSymbol]:
|
110
|
+
if all(
|
111
|
+
symbol.handle == target_part or symbol.handle not in target_part.prefixes()
|
112
|
+
for target_part in target_parts
|
113
|
+
) or isinstance(symbol.quantum_type, QuantumScalar):
|
114
|
+
return [symbol]
|
115
|
+
|
116
|
+
if isinstance(symbol.quantum_type, QuantumBitvector):
|
117
|
+
return self._get_array_parts(symbol, target_parts)
|
118
|
+
|
119
|
+
return self._get_struct_parts(symbol, target_parts)
|
120
|
+
|
121
|
+
def _get_array_parts(
|
122
|
+
self, symbol: QuantumSymbol, target_parts: list[HandleBinding]
|
123
|
+
) -> list[QuantumSymbol]:
|
124
|
+
if TYPE_CHECKING:
|
125
|
+
assert isinstance(symbol.quantum_type, QuantumBitvector)
|
126
|
+
|
127
|
+
if not symbol.quantum_type.has_length:
|
128
|
+
raise ClassiqExpansionError(
|
129
|
+
f"Could not determine the length of quantum array " f"{symbol.handle}."
|
130
|
+
)
|
131
|
+
target_slices = {
|
132
|
+
target_part.start.to_int_value(): target_part.end.to_int_value()
|
133
|
+
for target_part in target_parts
|
134
|
+
if isinstance(target_part, SlicedHandleBinding)
|
135
|
+
and symbol.handle == target_part.base_handle
|
136
|
+
}
|
137
|
+
|
138
|
+
symbol_parts: list[QuantumSymbol] = []
|
139
|
+
idx = 0
|
140
|
+
while idx < symbol.quantum_type.length_value:
|
141
|
+
if idx in target_slices:
|
142
|
+
stop = target_slices[idx]
|
143
|
+
if stop <= idx:
|
144
|
+
raise ClassiqInternalExpansionError(
|
145
|
+
f"Illegal sliced handle {str(symbol[idx: stop].handle)!r}"
|
146
|
+
)
|
147
|
+
symbol_parts.append(symbol[idx:stop])
|
148
|
+
idx = stop
|
149
|
+
else:
|
150
|
+
symbol_parts.extend(
|
151
|
+
self._get_symbol_parts_unsafe(symbol[idx], target_parts)
|
152
|
+
)
|
153
|
+
idx += 1
|
154
|
+
|
155
|
+
return symbol_parts
|
156
|
+
|
157
|
+
def _get_struct_parts(
|
158
|
+
self, symbol: QuantumSymbol, target_parts: list[HandleBinding]
|
159
|
+
) -> list[QuantumSymbol]:
|
160
|
+
return list(
|
161
|
+
chain.from_iterable(
|
162
|
+
self._get_symbol_parts_unsafe(field_symbol, target_parts)
|
163
|
+
for field_symbol in symbol.fields.values()
|
164
|
+
)
|
165
|
+
)
|
166
|
+
|
167
|
+
@staticmethod
|
168
|
+
def get_bind_ops(symbol_parts: SymbolParts) -> list[BindOperation]:
|
169
|
+
return [
|
170
|
+
BindOperation(
|
171
|
+
in_handles=[symbol.handle],
|
172
|
+
out_handles=[part.target_var_handle for part in parts],
|
173
|
+
)
|
174
|
+
for symbol, parts in symbol_parts.items()
|
175
|
+
]
|
176
|
+
|
177
|
+
@staticmethod
|
178
|
+
def get_var_decls(symbol_parts: SymbolParts) -> list[VariableDeclarationStatement]:
|
179
|
+
return [
|
180
|
+
VariableDeclarationStatement(
|
181
|
+
name=part.target_var_name,
|
182
|
+
quantum_type=part.target_var_type,
|
183
|
+
)
|
184
|
+
for part in chain.from_iterable(symbol_parts.values())
|
185
|
+
]
|
186
|
+
|
187
|
+
def _check_all_handles_were_replaced(self, new_expr_str: str) -> None:
|
188
|
+
vrc = VarRefCollector(ignore_duplicated_handles=True)
|
189
|
+
vrc.visit(ast.parse(new_expr_str))
|
190
|
+
for handle in self._get_handles(vrc):
|
191
|
+
if isinstance(handle, NestedHandleBinding):
|
192
|
+
raise ClassiqInternalExpansionError(f"Did not replace handle {handle}")
|
193
|
+
|
194
|
+
def rewrite(self, subject: AST_NODE, symbol_mapping: SymbolParts) -> AST_NODE:
|
195
|
+
handle_replacements = {
|
196
|
+
part.source_handle: part.target_var_handle
|
197
|
+
for parts in symbol_mapping.values()
|
198
|
+
for part in parts
|
199
|
+
}
|
200
|
+
|
201
|
+
class ReplaceSplitVars(Transformer):
|
202
|
+
@staticmethod
|
203
|
+
def visit_HandleBinding(handle: HandleBinding) -> HandleBinding:
|
204
|
+
handle = handle.collapse()
|
205
|
+
for handle_to_replace, replacement in handle_replacements.items():
|
206
|
+
handle = handle.replace_prefix(handle_to_replace, replacement)
|
207
|
+
return handle
|
208
|
+
|
209
|
+
@staticmethod
|
210
|
+
def visit_Expression(expr: Expression) -> Expression:
|
211
|
+
return self._rewrite_expression(symbol_mapping, expr)
|
212
|
+
|
213
|
+
return ReplaceSplitVars().visit(subject)
|
214
|
+
|
215
|
+
def _rewrite_expression(
|
216
|
+
self,
|
217
|
+
symbol_mapping: SymbolParts,
|
218
|
+
expression: Expression,
|
219
|
+
) -> Expression:
|
220
|
+
vrc = VarRefCollector(ignore_duplicated_handles=True)
|
221
|
+
vrc.visit(ast.parse(expression.expr))
|
222
|
+
|
223
|
+
handle_names = {
|
224
|
+
part.source_handle: part.target_var_name
|
225
|
+
for parts in symbol_mapping.values()
|
226
|
+
for part in parts
|
227
|
+
}
|
228
|
+
new_expr_str = expression.expr
|
229
|
+
for handle in vrc.var_handles:
|
230
|
+
collapsed_handle = handle.collapse()
|
231
|
+
if collapsed_handle in handle_names:
|
232
|
+
new_expr_str = new_expr_str.replace(
|
233
|
+
str(handle), handle_names[collapsed_handle]
|
234
|
+
)
|
235
|
+
self._check_all_handles_were_replaced(new_expr_str)
|
236
|
+
|
237
|
+
return Expression(expr=new_expr_str)
|
@@ -415,7 +415,7 @@ def inplace_prepare_int(value: CInt, target: QArray[QBit]) -> None:
|
|
415
415
|
@qfunc(external=True)
|
416
416
|
def prepare_int(
|
417
417
|
value: CInt,
|
418
|
-
out: Output[QNum[Literal["floor(log(value, 2)) + 1"]
|
418
|
+
out: Output[QNum[Literal["floor(log(value, 2)) + 1"]]],
|
419
419
|
) -> None:
|
420
420
|
"""
|
421
421
|
[Qmod Classiq-library function]
|