classiq 0.58.1__py3-none-any.whl → 0.60.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/_internals/api_wrapper.py +91 -20
- classiq/_internals/client.py +48 -11
- classiq/_internals/jobs.py +47 -40
- classiq/execution/execution_session.py +62 -22
- classiq/execution/jobs.py +64 -23
- classiq/execution/qaoa.py +17 -15
- classiq/execution/qnn.py +17 -18
- classiq/executor.py +2 -1
- classiq/interface/_version.py +1 -1
- classiq/interface/generator/arith/arithmetic_operations.py +1 -0
- classiq/interface/generator/register_role.py +8 -0
- classiq/interface/model/handle_binding.py +22 -3
- classiq/model_expansions/capturing/captured_vars.py +316 -0
- classiq/model_expansions/capturing/mangling_utils.py +18 -9
- classiq/model_expansions/closure.py +29 -74
- classiq/model_expansions/function_builder.py +51 -66
- classiq/model_expansions/interpreter.py +4 -7
- classiq/model_expansions/quantum_operations/bind.py +1 -3
- classiq/model_expansions/quantum_operations/call_emitter.py +46 -11
- classiq/model_expansions/quantum_operations/classicalif.py +2 -5
- classiq/model_expansions/quantum_operations/control.py +13 -16
- classiq/model_expansions/quantum_operations/emitter.py +36 -8
- classiq/model_expansions/quantum_operations/expression_operation.py +9 -19
- classiq/model_expansions/quantum_operations/inplace_binary_operation.py +4 -6
- classiq/model_expansions/quantum_operations/invert.py +5 -8
- classiq/model_expansions/quantum_operations/power.py +5 -10
- classiq/model_expansions/quantum_operations/quantum_assignment_operation.py +1 -3
- classiq/model_expansions/quantum_operations/quantum_function_call.py +1 -3
- classiq/model_expansions/quantum_operations/repeat.py +3 -3
- classiq/model_expansions/quantum_operations/variable_decleration.py +1 -1
- classiq/model_expansions/quantum_operations/within_apply.py +1 -5
- classiq/model_expansions/scope.py +2 -2
- classiq/model_expansions/transformers/var_splitter.py +32 -19
- classiq/model_expansions/utils/handles_collector.py +33 -0
- classiq/model_expansions/visitors/variable_references.py +18 -2
- classiq/qmod/qfunc.py +9 -13
- classiq/qmod/quantum_expandable.py +1 -21
- classiq/qmod/quantum_function.py +16 -0
- {classiq-0.58.1.dist-info → classiq-0.60.0.dist-info}/METADATA +1 -1
- {classiq-0.58.1.dist-info → classiq-0.60.0.dist-info}/RECORD +41 -42
- classiq/interface/executor/aws_execution_cost.py +0 -90
- classiq/model_expansions/capturing/captured_var_manager.py +0 -48
- classiq/model_expansions/capturing/propagated_var_stack.py +0 -194
- {classiq-0.58.1.dist-info → classiq-0.60.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,316 @@
|
|
1
|
+
import dataclasses
|
2
|
+
from collections.abc import Iterator, Sequence
|
3
|
+
from contextlib import contextmanager
|
4
|
+
from dataclasses import dataclass, field
|
5
|
+
from typing import TYPE_CHECKING
|
6
|
+
|
7
|
+
from classiq.interface.enum_utils import StrEnum
|
8
|
+
from classiq.interface.exceptions import (
|
9
|
+
ClassiqExpansionError,
|
10
|
+
ClassiqInternalExpansionError,
|
11
|
+
)
|
12
|
+
from classiq.interface.generator.functions.port_declaration import (
|
13
|
+
PortDeclarationDirection,
|
14
|
+
)
|
15
|
+
from classiq.interface.model.handle_binding import HandleBinding, NestedHandleBinding
|
16
|
+
from classiq.interface.model.port_declaration import PortDeclaration
|
17
|
+
from classiq.interface.model.quantum_function_call import ArgValue
|
18
|
+
from classiq.interface.model.quantum_type import QuantumType
|
19
|
+
|
20
|
+
from classiq.model_expansions.capturing.mangling_utils import (
|
21
|
+
demangle_handle,
|
22
|
+
mangle_captured_var_name,
|
23
|
+
)
|
24
|
+
from classiq.model_expansions.scope import QuantumSymbol
|
25
|
+
from classiq.model_expansions.transformers.var_splitter import SymbolPart, SymbolParts
|
26
|
+
|
27
|
+
if TYPE_CHECKING:
|
28
|
+
from classiq.model_expansions.closure import FunctionClosure
|
29
|
+
|
30
|
+
|
31
|
+
class PortDirection(StrEnum):
|
32
|
+
Input = "input"
|
33
|
+
Inout = "inout"
|
34
|
+
Output = "output"
|
35
|
+
Outin = "outin"
|
36
|
+
|
37
|
+
def negate(self) -> "PortDirection":
|
38
|
+
if self == PortDirection.Input:
|
39
|
+
return PortDirection.Output
|
40
|
+
if self == PortDirection.Output:
|
41
|
+
return PortDirection.Input
|
42
|
+
return self
|
43
|
+
|
44
|
+
@staticmethod
|
45
|
+
def load(direction: PortDeclarationDirection) -> "PortDirection":
|
46
|
+
if direction == PortDeclarationDirection.Input:
|
47
|
+
return PortDirection.Input
|
48
|
+
if direction == PortDeclarationDirection.Output:
|
49
|
+
return PortDirection.Output
|
50
|
+
if direction == PortDeclarationDirection.Inout:
|
51
|
+
return PortDirection.Inout
|
52
|
+
raise ClassiqInternalExpansionError
|
53
|
+
|
54
|
+
def dump(self) -> PortDeclarationDirection:
|
55
|
+
if self == PortDirection.Input:
|
56
|
+
return PortDeclarationDirection.Input
|
57
|
+
if self == PortDirection.Output:
|
58
|
+
return PortDeclarationDirection.Output
|
59
|
+
if self == PortDirection.Inout:
|
60
|
+
return PortDeclarationDirection.Inout
|
61
|
+
raise ClassiqInternalExpansionError
|
62
|
+
|
63
|
+
|
64
|
+
@dataclass(frozen=True)
|
65
|
+
class _CapturedHandle:
|
66
|
+
handle: HandleBinding
|
67
|
+
quantum_type: QuantumType
|
68
|
+
defining_function: "FunctionClosure"
|
69
|
+
direction: PortDirection
|
70
|
+
is_propagated: bool
|
71
|
+
|
72
|
+
@property
|
73
|
+
def mangled_name(self) -> str:
|
74
|
+
return mangle_captured_var_name(self.handle.identifier, self.defining_function)
|
75
|
+
|
76
|
+
@property
|
77
|
+
def port(self) -> PortDeclaration:
|
78
|
+
return PortDeclaration(
|
79
|
+
name=self.mangled_name,
|
80
|
+
quantum_type=self.quantum_type,
|
81
|
+
direction=self.direction.dump(),
|
82
|
+
)
|
83
|
+
|
84
|
+
def is_same_var(self, other: "_CapturedHandle") -> bool:
|
85
|
+
return self.handle.name == other.handle.name and _same_closure(
|
86
|
+
self.defining_function, other.defining_function
|
87
|
+
)
|
88
|
+
|
89
|
+
def change_direction(self, new_direction: PortDirection) -> "_CapturedHandle":
|
90
|
+
return dataclasses.replace(self, direction=new_direction)
|
91
|
+
|
92
|
+
def set_propagated(self) -> "_CapturedHandle":
|
93
|
+
return dataclasses.replace(self, is_propagated=True)
|
94
|
+
|
95
|
+
def update_propagation(
|
96
|
+
self, other_captured_handle: "_CapturedHandle"
|
97
|
+
) -> "_CapturedHandle":
|
98
|
+
if self.is_propagated and not other_captured_handle.is_propagated:
|
99
|
+
return dataclasses.replace(self, is_propagated=False)
|
100
|
+
return self
|
101
|
+
|
102
|
+
|
103
|
+
@dataclass
|
104
|
+
class CapturedVars:
|
105
|
+
_captured_handles: list[_CapturedHandle] = field(default_factory=list)
|
106
|
+
|
107
|
+
def capture_handle(
|
108
|
+
self,
|
109
|
+
handle: HandleBinding,
|
110
|
+
quantum_type: QuantumType,
|
111
|
+
defining_function: "FunctionClosure",
|
112
|
+
direction: PortDeclarationDirection,
|
113
|
+
) -> None:
|
114
|
+
self._capture_handle(
|
115
|
+
_CapturedHandle(
|
116
|
+
handle=handle,
|
117
|
+
quantum_type=quantum_type,
|
118
|
+
defining_function=defining_function,
|
119
|
+
direction=PortDirection.load(direction),
|
120
|
+
is_propagated=False,
|
121
|
+
)
|
122
|
+
)
|
123
|
+
|
124
|
+
def _capture_handle(self, captured_handle: _CapturedHandle) -> None:
|
125
|
+
if (
|
126
|
+
isinstance(captured_handle.handle, NestedHandleBinding)
|
127
|
+
and captured_handle.direction != PortDirection.Inout
|
128
|
+
):
|
129
|
+
raise ClassiqInternalExpansionError("Captured nested handles must be inout")
|
130
|
+
|
131
|
+
new_captured_handles = []
|
132
|
+
for existing_captured_handle in self._captured_handles:
|
133
|
+
if not existing_captured_handle.is_same_var(captured_handle):
|
134
|
+
new_captured_handles.append(existing_captured_handle)
|
135
|
+
continue
|
136
|
+
captured_handle = captured_handle.update_propagation(
|
137
|
+
existing_captured_handle
|
138
|
+
)
|
139
|
+
if existing_captured_handle.handle == captured_handle.handle:
|
140
|
+
captured_handle = self._conjugate_direction(
|
141
|
+
existing_captured_handle, captured_handle
|
142
|
+
)
|
143
|
+
elif captured_handle.handle in existing_captured_handle.handle:
|
144
|
+
if existing_captured_handle.direction in (
|
145
|
+
PortDirection.Input,
|
146
|
+
PortDirection.Outin,
|
147
|
+
):
|
148
|
+
raise ClassiqInternalExpansionError(
|
149
|
+
"Captured handle is already freed"
|
150
|
+
)
|
151
|
+
captured_handle = existing_captured_handle
|
152
|
+
elif existing_captured_handle.handle in captured_handle.handle:
|
153
|
+
if captured_handle.direction in (
|
154
|
+
PortDirection.Output,
|
155
|
+
PortDirection.Outin,
|
156
|
+
):
|
157
|
+
raise ClassiqInternalExpansionError(
|
158
|
+
"Captured handle is already allocated"
|
159
|
+
)
|
160
|
+
else:
|
161
|
+
new_captured_handles.append(existing_captured_handle)
|
162
|
+
new_captured_handles.append(captured_handle)
|
163
|
+
self._captured_handles = new_captured_handles
|
164
|
+
|
165
|
+
def _conjugate_direction(
|
166
|
+
self,
|
167
|
+
existing_captured_handle: _CapturedHandle,
|
168
|
+
captured_handle: _CapturedHandle,
|
169
|
+
) -> _CapturedHandle:
|
170
|
+
if existing_captured_handle.direction == PortDirection.Input:
|
171
|
+
if captured_handle.direction == PortDirection.Output:
|
172
|
+
return captured_handle.change_direction(PortDirection.Inout)
|
173
|
+
if captured_handle.direction == PortDirection.Outin:
|
174
|
+
return captured_handle.change_direction(PortDirection.Input)
|
175
|
+
raise ClassiqInternalExpansionError("Captured handle is already freed")
|
176
|
+
if existing_captured_handle.direction == PortDirection.Output:
|
177
|
+
if captured_handle.direction == PortDirection.Input:
|
178
|
+
return captured_handle.change_direction(PortDirection.Outin)
|
179
|
+
if captured_handle.direction in (
|
180
|
+
PortDirection.Output,
|
181
|
+
PortDirection.Outin,
|
182
|
+
):
|
183
|
+
raise ClassiqInternalExpansionError(
|
184
|
+
"Captured handle is already allocated"
|
185
|
+
)
|
186
|
+
return captured_handle.change_direction(PortDirection.Output)
|
187
|
+
if existing_captured_handle.direction == PortDirection.Inout:
|
188
|
+
if captured_handle.direction in (
|
189
|
+
PortDirection.Output,
|
190
|
+
PortDirection.Outin,
|
191
|
+
):
|
192
|
+
raise ClassiqInternalExpansionError(
|
193
|
+
"Captured handle is already allocated"
|
194
|
+
)
|
195
|
+
elif captured_handle.direction in (
|
196
|
+
PortDirection.Input,
|
197
|
+
PortDirection.Inout,
|
198
|
+
):
|
199
|
+
raise ClassiqInternalExpansionError("Captured handle is already freed")
|
200
|
+
return captured_handle
|
201
|
+
|
202
|
+
def update(self, other_captured_vars: "CapturedVars") -> None:
|
203
|
+
for captured_handle in other_captured_vars._captured_handles:
|
204
|
+
self._capture_handle(captured_handle)
|
205
|
+
|
206
|
+
def negate(self) -> "CapturedVars":
|
207
|
+
return CapturedVars(
|
208
|
+
_captured_handles=[
|
209
|
+
captured_handle.change_direction(captured_handle.direction.negate())
|
210
|
+
for captured_handle in self._captured_handles
|
211
|
+
]
|
212
|
+
)
|
213
|
+
|
214
|
+
def filter(self, current_function: "FunctionClosure") -> "CapturedVars":
|
215
|
+
return CapturedVars(
|
216
|
+
_captured_handles=[
|
217
|
+
captured_handle
|
218
|
+
for captured_handle in self._captured_handles
|
219
|
+
if not _same_closure(
|
220
|
+
captured_handle.defining_function, current_function
|
221
|
+
)
|
222
|
+
]
|
223
|
+
)
|
224
|
+
|
225
|
+
def set_propagated(self) -> "CapturedVars":
|
226
|
+
return CapturedVars(
|
227
|
+
_captured_handles=[
|
228
|
+
captured_handle.set_propagated()
|
229
|
+
for captured_handle in self._captured_handles
|
230
|
+
]
|
231
|
+
)
|
232
|
+
|
233
|
+
def get_captured_ports(self) -> list[PortDeclaration]:
|
234
|
+
return [captured_handle.port for captured_handle in self._captured_handles]
|
235
|
+
|
236
|
+
def get_captured_args(
|
237
|
+
self, current_function: "FunctionClosure"
|
238
|
+
) -> list[HandleBinding]:
|
239
|
+
return [
|
240
|
+
(
|
241
|
+
captured_handle.handle
|
242
|
+
if _same_closure(current_function, captured_handle.defining_function)
|
243
|
+
else HandleBinding(name=captured_handle.mangled_name)
|
244
|
+
)
|
245
|
+
for captured_handle in self._captured_handles
|
246
|
+
]
|
247
|
+
|
248
|
+
def get_captured_mapping(self) -> SymbolParts:
|
249
|
+
return {
|
250
|
+
QuantumSymbol(
|
251
|
+
handle=captured_handle.handle,
|
252
|
+
quantum_type=captured_handle.quantum_type,
|
253
|
+
): [
|
254
|
+
SymbolPart(
|
255
|
+
source_handle=captured_handle.handle,
|
256
|
+
target_var_name=captured_handle.mangled_name,
|
257
|
+
target_var_type=captured_handle.quantum_type,
|
258
|
+
)
|
259
|
+
]
|
260
|
+
for captured_handle in self._captured_handles
|
261
|
+
if not captured_handle.is_propagated
|
262
|
+
}
|
263
|
+
|
264
|
+
@contextmanager
|
265
|
+
def freeze(self) -> Iterator[None]:
|
266
|
+
previous = self._captured_handles
|
267
|
+
yield
|
268
|
+
self._captured_handles = previous
|
269
|
+
|
270
|
+
|
271
|
+
def _same_closure(closure_1: "FunctionClosure", closure_2: "FunctionClosure") -> bool:
|
272
|
+
return closure_1.depth == closure_2.depth
|
273
|
+
|
274
|
+
|
275
|
+
def validate_args_are_not_propagated(
|
276
|
+
args: Sequence[ArgValue], captured_vars: Sequence[HandleBinding]
|
277
|
+
) -> None:
|
278
|
+
if not captured_vars:
|
279
|
+
return
|
280
|
+
captured_handles = {demangle_handle(handle) for handle in captured_vars}
|
281
|
+
arg_handles = {
|
282
|
+
demangle_handle(arg) for arg in args if isinstance(arg, HandleBinding)
|
283
|
+
}
|
284
|
+
if any(
|
285
|
+
arg_handle.overlaps(captured_handle)
|
286
|
+
for arg_handle in arg_handles
|
287
|
+
for captured_handle in captured_handles
|
288
|
+
):
|
289
|
+
captured_handles_str = {str(handle) for handle in captured_handles}
|
290
|
+
arg_handles_str = {str(handle) for handle in arg_handles}
|
291
|
+
vars_msg = f"Explicitly passed variables: {arg_handles_str}, captured variables: {captured_handles_str}"
|
292
|
+
raise ClassiqExpansionError(
|
293
|
+
f"Cannot capture variables that are explicitly passed as arguments. "
|
294
|
+
f"{vars_msg}"
|
295
|
+
)
|
296
|
+
|
297
|
+
|
298
|
+
def validate_captured_directions(captured_vars: CapturedVars) -> None:
|
299
|
+
captured_inputs = [
|
300
|
+
captured_handle.handle.name
|
301
|
+
for captured_handle in captured_vars._captured_handles
|
302
|
+
if captured_handle.direction == PortDirection.Input
|
303
|
+
]
|
304
|
+
captured_outputs = [
|
305
|
+
captured_handle.handle.name
|
306
|
+
for captured_handle in captured_vars._captured_handles
|
307
|
+
if captured_handle.direction == PortDirection.Output
|
308
|
+
]
|
309
|
+
if len(captured_inputs) > 0:
|
310
|
+
raise ClassiqExpansionError(
|
311
|
+
f"Captured quantum variables {captured_inputs!r} cannot be used as inputs"
|
312
|
+
)
|
313
|
+
if len(captured_outputs) > 0:
|
314
|
+
raise ClassiqExpansionError(
|
315
|
+
f"Captured quantum variables {captured_outputs!r} cannot be used as outputs"
|
316
|
+
)
|
@@ -1,17 +1,25 @@
|
|
1
1
|
import re
|
2
|
+
from typing import TYPE_CHECKING
|
2
3
|
|
3
4
|
from classiq.interface.generator.compiler_keywords import CAPTURE_SUFFIX
|
4
5
|
from classiq.interface.model.handle_binding import HANDLE_ID_SEPARATOR, HandleBinding
|
5
6
|
|
7
|
+
if TYPE_CHECKING:
|
8
|
+
from classiq.model_expansions.closure import FunctionClosure
|
9
|
+
|
6
10
|
IDENTIFIER_PATTERN = r"[a-zA-Z_][a-zA-Z0-9_]*"
|
7
11
|
CAPTURE_PATTERN = re.compile(
|
8
|
-
rf"({IDENTIFIER_PATTERN}){CAPTURE_SUFFIX}{IDENTIFIER_PATTERN}__"
|
12
|
+
rf"({IDENTIFIER_PATTERN}){CAPTURE_SUFFIX}{IDENTIFIER_PATTERN}__\d*"
|
9
13
|
)
|
10
14
|
ARRAY_CAST_SUFFIX = HANDLE_ID_SEPARATOR + "array_cast"
|
11
15
|
|
12
16
|
|
13
|
-
def mangle_captured_var_name(
|
14
|
-
|
17
|
+
def mangle_captured_var_name(
|
18
|
+
var_name: str, defining_function: "FunctionClosure"
|
19
|
+
) -> str:
|
20
|
+
return (
|
21
|
+
f"{var_name}{CAPTURE_SUFFIX}{defining_function.name}__{defining_function.depth}"
|
22
|
+
)
|
15
23
|
|
16
24
|
|
17
25
|
def demangle_name(name: str) -> str:
|
@@ -25,15 +33,16 @@ def demangle_handle(handle: HandleBinding) -> HandleBinding:
|
|
25
33
|
return handle
|
26
34
|
if ARRAY_CAST_SUFFIX in name:
|
27
35
|
return HandleBinding(name=name.split(ARRAY_CAST_SUFFIX)[0])
|
28
|
-
name = re.sub(r"_\d+$", "", name)
|
36
|
+
name = re.sub(r"([^_])_\d+$", r"\1", name)
|
29
37
|
name_parts = name.split(HANDLE_ID_SEPARATOR)
|
30
|
-
|
38
|
+
new_name_parts = [name_parts[0]]
|
31
39
|
for part in name_parts[1:]:
|
32
40
|
if re.fullmatch(r"\d+", part):
|
33
|
-
|
41
|
+
new_name_parts.append(f"[{part}]")
|
34
42
|
elif re.fullmatch(r"\d+_\d+", part):
|
35
43
|
part_left, part_right = part.split("_")
|
36
|
-
|
44
|
+
new_name_parts.append(f"[{part_left}:{part_right}]")
|
37
45
|
else:
|
38
|
-
|
39
|
-
|
46
|
+
new_name_parts.append(f".{part}")
|
47
|
+
new_name_parts = list(map(demangle_name, new_name_parts))
|
48
|
+
return handle.rename("".join(new_name_parts))
|
@@ -1,34 +1,29 @@
|
|
1
|
+
import dataclasses
|
1
2
|
import json
|
2
3
|
import uuid
|
3
|
-
from collections import
|
4
|
-
from
|
4
|
+
from collections.abc import Collection, Iterator, Sequence
|
5
|
+
from contextlib import contextmanager
|
5
6
|
from dataclasses import dataclass, field
|
6
|
-
from functools import
|
7
|
+
from functools import singledispatch
|
7
8
|
from symtable import Symbol
|
8
|
-
from typing import Any, Optional
|
9
|
+
from typing import Any, Optional
|
9
10
|
|
10
11
|
from typing_extensions import Self
|
11
12
|
|
12
|
-
from classiq.interface.exceptions import
|
13
|
-
ClassiqInternalExpansionError,
|
14
|
-
)
|
13
|
+
from classiq.interface.exceptions import ClassiqInternalExpansionError
|
15
14
|
from classiq.interface.generator.functions.builtins.internal_operators import (
|
16
15
|
All_BUILTINS_OPERATORS,
|
17
16
|
)
|
18
|
-
from classiq.interface.generator.visitor import Visitor
|
19
17
|
from classiq.interface.model.port_declaration import PortDeclaration
|
20
|
-
from classiq.interface.model.quantum_function_call import QuantumFunctionCall
|
21
18
|
from classiq.interface.model.quantum_function_declaration import (
|
22
19
|
NamedParamsQuantumFunctionDeclaration,
|
23
20
|
PositionalArg,
|
24
21
|
QuantumOperandDeclaration,
|
25
22
|
)
|
26
23
|
from classiq.interface.model.quantum_statement import QuantumStatement
|
27
|
-
from classiq.interface.model.variable_declaration_statement import (
|
28
|
-
VariableDeclarationStatement,
|
29
|
-
)
|
30
24
|
|
31
25
|
from classiq import ClassicalParameterDeclaration
|
26
|
+
from classiq.model_expansions.capturing.captured_vars import CapturedVars
|
32
27
|
from classiq.model_expansions.expression_renamer import ExpressionRenamer
|
33
28
|
from classiq.model_expansions.scope import (
|
34
29
|
Evaluated,
|
@@ -46,6 +41,7 @@ class Closure:
|
|
46
41
|
blocks: dict[str, Sequence[QuantumStatement]]
|
47
42
|
scope: Scope
|
48
43
|
positional_arg_declarations: Sequence[PositionalArg] = tuple()
|
44
|
+
captured_vars: CapturedVars = field(default_factory=CapturedVars)
|
49
45
|
|
50
46
|
@property
|
51
47
|
def port_declarations(self) -> dict[str, PortDeclaration]:
|
@@ -55,6 +51,11 @@ class Closure:
|
|
55
51
|
if isinstance(param, PortDeclaration)
|
56
52
|
}
|
57
53
|
|
54
|
+
@contextmanager
|
55
|
+
def freeze(self) -> Iterator[None]:
|
56
|
+
with self.scope.freeze(), self.captured_vars.freeze():
|
57
|
+
yield
|
58
|
+
|
58
59
|
|
59
60
|
@dataclass(frozen=True)
|
60
61
|
class GenerativeClosure(Closure):
|
@@ -66,6 +67,13 @@ class FunctionClosure(Closure):
|
|
66
67
|
is_lambda: bool = False
|
67
68
|
is_atomic: bool = False
|
68
69
|
signature_scope: Scope = field(default_factory=Scope)
|
70
|
+
_depth: Optional[int] = None
|
71
|
+
|
72
|
+
@property
|
73
|
+
def depth(self) -> int:
|
74
|
+
if self._depth is None:
|
75
|
+
raise ClassiqInternalExpansionError
|
76
|
+
return self._depth
|
69
77
|
|
70
78
|
# creates a unique id for the function closure based on the arguments values.
|
71
79
|
# The closure is changing across the interpreter flow so it's closure_id may change
|
@@ -89,12 +97,6 @@ class FunctionClosure(Closure):
|
|
89
97
|
return []
|
90
98
|
return self.blocks["body"]
|
91
99
|
|
92
|
-
@cached_property
|
93
|
-
def colliding_variables(self) -> set[str]:
|
94
|
-
# Note that this has to be accessed after adding the parameters from the signature and not during
|
95
|
-
# initialization
|
96
|
-
return VariableCollector(self.scope).get_colliding_variables(self.body)
|
97
|
-
|
98
100
|
@classmethod
|
99
101
|
def create(
|
100
102
|
cls,
|
@@ -122,6 +124,7 @@ class FunctionClosure(Closure):
|
|
122
124
|
blocks,
|
123
125
|
scope,
|
124
126
|
positional_arg_declarations,
|
127
|
+
CapturedVars(),
|
125
128
|
is_lambda,
|
126
129
|
is_atomic,
|
127
130
|
**kwargs,
|
@@ -134,68 +137,20 @@ class FunctionClosure(Closure):
|
|
134
137
|
"name": declaration.name,
|
135
138
|
"positional_arg_declarations": declaration.positional_arg_declarations,
|
136
139
|
}
|
137
|
-
fields.pop("colliding_variables", 0)
|
138
140
|
return type(self)(**fields)
|
139
141
|
|
142
|
+
def set_depth(self, depth: int) -> Self:
|
143
|
+
return dataclasses.replace(self, _depth=depth)
|
140
144
|
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
NestedFunctionClosureT = Union[FunctionClosure, list["NestedFunctionClosureT"]]
|
147
|
-
|
148
|
-
|
149
|
-
class VariableCollector(Visitor):
|
150
|
-
def __init__(self, function_scope: Scope) -> None:
|
151
|
-
self._function_scope = function_scope
|
152
|
-
self._variables: defaultdict[str, set[Optional[str]]] = defaultdict(set)
|
153
|
-
for var in self._function_scope.data:
|
154
|
-
defining_function = self._function_scope[var].defining_function
|
155
|
-
if defining_function is not None:
|
156
|
-
self._variables[var].add(defining_function.name)
|
157
|
-
|
158
|
-
def get_colliding_variables(self, body: Sequence[QuantumStatement]) -> set[str]:
|
159
|
-
self.visit(body)
|
160
|
-
return {
|
161
|
-
var
|
162
|
-
for var, defining_functions in self._variables.items()
|
163
|
-
if len(defining_functions) > 1
|
164
|
-
}
|
165
|
-
|
166
|
-
def visit_VariableDeclarationStatement(
|
167
|
-
self, node: VariableDeclarationStatement
|
168
|
-
) -> None:
|
169
|
-
self._variables[node.name].add(None)
|
170
|
-
|
171
|
-
def visit_QuantumFunctionCall(self, node: QuantumFunctionCall) -> None:
|
172
|
-
# The else case corresponds to operand identifiers. In case of operand identifiers, we scan
|
173
|
-
# the whole list of operands because we can't evaluate the index yet.
|
174
|
-
identifier = (
|
175
|
-
node.function if isinstance(node.function, str) else node.function.name
|
145
|
+
def copy_scope(self) -> Self: # Remove when scoping is normal (CAD-24980)
|
146
|
+
return dataclasses.replace(
|
147
|
+
self, scope=Scope(self.scope.data, parent=self.scope.parent)
|
176
148
|
)
|
177
|
-
self._add_variables(self._function_scope[identifier].value)
|
178
|
-
|
179
|
-
def _add_variables(self, evaluated: NestedFunctionClosureT) -> None:
|
180
|
-
if isinstance(evaluated, list):
|
181
|
-
for elem in evaluated:
|
182
|
-
self._add_variables(elem)
|
183
|
-
return
|
184
|
-
if not isinstance(evaluated, FunctionClosure):
|
185
|
-
raise ClassiqInternalExpansionError
|
186
|
-
self._add_variables_from_closure(evaluated)
|
187
149
|
|
188
|
-
def _add_variables_from_closure(self, closure: FunctionClosure) -> None:
|
189
|
-
if not closure.is_lambda:
|
190
|
-
return
|
191
|
-
lambda_environment = closure.scope.parent
|
192
|
-
if lambda_environment is None:
|
193
|
-
raise ClassiqInternalExpansionError
|
194
150
|
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
self._variables[var].add(defining_function.name)
|
151
|
+
@dataclass(frozen=True)
|
152
|
+
class GenerativeFunctionClosure(GenerativeClosure, FunctionClosure):
|
153
|
+
pass
|
199
154
|
|
200
155
|
|
201
156
|
def _generate_closure_id(
|