classiq 0.66.1__py3-none-any.whl → 0.68.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 +5 -1
- classiq/_internals/async_utils.py +1 -1
- classiq/_internals/authentication/password_manager.py +1 -1
- classiq/_internals/client.py +1 -1
- classiq/applications/finance/finance_model_constructor.py +9 -0
- classiq/applications/grover/grover_model_constructor.py +10 -0
- classiq/applications/qnn/qlayer.py +8 -2
- classiq/applications/qsvm/qsvm_model_constructor.py +9 -0
- classiq/execution/execution_session.py +7 -3
- classiq/executor.py +7 -2
- classiq/interface/_version.py +1 -1
- classiq/interface/ast_node.py +1 -1
- classiq/interface/chemistry/operator.py +1 -1
- classiq/interface/debug_info/debug_info.py +27 -0
- classiq/interface/exceptions.py +2 -5
- classiq/interface/generator/arith/argument_utils.py +1 -1
- classiq/interface/generator/arith/arithmetic.py +99 -2
- classiq/interface/generator/arith/arithmetic_expression_parser.py +1 -1
- classiq/interface/generator/arith/binary_ops.py +3 -0
- classiq/interface/generator/function_param_list_without_self_reference.py +2 -0
- classiq/interface/generator/functions/type_name.py +2 -2
- classiq/interface/generator/generated_circuit_data.py +38 -3
- classiq/interface/generator/hardware_efficient_ansatz.py +1 -1
- classiq/interface/generator/hva.py +1 -1
- classiq/interface/generator/model/preferences/preferences.py +8 -1
- classiq/interface/generator/quantum_program.py +18 -1
- classiq/interface/generator/reset.py +14 -0
- classiq/interface/generator/types/enum_declaration.py +33 -2
- classiq/interface/generator/ucc.py +1 -1
- classiq/interface/interface_version.py +1 -1
- classiq/interface/model/classical_if.py +2 -2
- classiq/interface/model/control.py +2 -2
- classiq/interface/model/invert.py +2 -2
- classiq/interface/model/power.py +2 -2
- classiq/interface/model/quantum_function_call.py +4 -0
- classiq/interface/model/quantum_statement.py +13 -0
- classiq/interface/model/repeat.py +2 -2
- classiq/interface/model/statement_block.py +1 -1
- classiq/interface/model/within_apply_operation.py +2 -2
- classiq/model_expansions/atomic_expression_functions_defs.py +2 -1
- classiq/model_expansions/capturing/captured_vars.py +184 -54
- classiq/model_expansions/closure.py +6 -3
- classiq/model_expansions/evaluators/control.py +14 -38
- classiq/model_expansions/function_builder.py +19 -14
- classiq/model_expansions/generative_functions.py +7 -11
- classiq/model_expansions/interpreters/base_interpreter.py +14 -5
- classiq/model_expansions/interpreters/generative_interpreter.py +87 -26
- classiq/model_expansions/quantum_operations/allocate.py +9 -3
- classiq/model_expansions/quantum_operations/assignment_result_processor.py +52 -0
- classiq/model_expansions/quantum_operations/bind.py +67 -14
- classiq/model_expansions/quantum_operations/block_evaluator.py +76 -0
- classiq/model_expansions/quantum_operations/call_emitter.py +79 -10
- classiq/model_expansions/quantum_operations/classicalif.py +10 -6
- classiq/model_expansions/quantum_operations/composite_emitter.py +27 -0
- classiq/model_expansions/quantum_operations/emitter.py +24 -3
- classiq/model_expansions/quantum_operations/expression_evaluator.py +33 -0
- classiq/model_expansions/quantum_operations/handle_evaluator.py +28 -0
- classiq/model_expansions/quantum_operations/quantum_function_call.py +3 -2
- classiq/model_expansions/quantum_operations/repeat.py +9 -3
- classiq/model_expansions/quantum_operations/variable_decleration.py +13 -2
- classiq/model_expansions/sympy_conversion/expression_to_sympy.py +1 -1
- classiq/open_library/functions/__init__.py +1 -2
- classiq/open_library/functions/amplitude_amplification.py +12 -9
- classiq/open_library/functions/discrete_sine_cosine_transform.py +16 -13
- classiq/open_library/functions/grover.py +11 -15
- classiq/open_library/functions/modular_exponentiation.py +7 -13
- classiq/open_library/functions/state_preparation.py +16 -17
- classiq/open_library/functions/swap_test.py +1 -1
- classiq/open_library/functions/utility_functions.py +10 -2
- classiq/qmod/builtins/functions/__init__.py +3 -0
- classiq/qmod/builtins/functions/chemistry.py +6 -38
- classiq/qmod/builtins/functions/mid_circuit_measurement.py +15 -0
- classiq/qmod/quantum_expandable.py +30 -6
- classiq/qmod/quantum_function.py +4 -0
- classiq/qmod/semantics/annotation/call_annotation.py +8 -2
- classiq/qmod/semantics/annotation/model_annotation.py +9 -0
- classiq/qmod/semantics/error_manager.py +0 -6
- classiq/qmod/semantics/static_semantics_visitor.py +0 -347
- classiq/qmod/semantics/validation/types_validation.py +1 -1
- classiq/qmod/symbolic.py +2 -1
- classiq/qmod/utilities.py +2 -1
- classiq/qmod/write_qmod.py +10 -7
- classiq/synthesis.py +20 -7
- {classiq-0.66.1.dist-info → classiq-0.68.0.dist-info}/METADATA +1 -1
- {classiq-0.66.1.dist-info → classiq-0.68.0.dist-info}/RECORD +86 -81
- classiq/model_expansions/quantum_operations/shallow_emitter.py +0 -166
- classiq/qmod/semantics/validation/func_call_validation.py +0 -99
- classiq/qmod/semantics/validation/handle_validation.py +0 -85
- {classiq-0.66.1.dist-info → classiq-0.68.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,14 @@
|
|
1
|
+
from classiq.interface.generator.arith.register_user_input import (
|
2
|
+
RegisterArithmeticInfo,
|
3
|
+
RegisterUserInput,
|
4
|
+
)
|
5
|
+
from classiq.interface.generator.function_params import FunctionParams
|
6
|
+
|
7
|
+
|
8
|
+
class Reset(FunctionParams):
|
9
|
+
target: RegisterUserInput
|
10
|
+
|
11
|
+
def _create_ios(self) -> None:
|
12
|
+
mapping: dict[str, RegisterArithmeticInfo] = {self.target.name: self.target}
|
13
|
+
self._inputs = mapping
|
14
|
+
self._outputs = mapping
|
@@ -1,5 +1,6 @@
|
|
1
1
|
from collections import Counter
|
2
2
|
from enum import Enum, EnumMeta, IntEnum
|
3
|
+
from typing import Any, Callable
|
3
4
|
|
4
5
|
import pydantic
|
5
6
|
|
@@ -7,6 +8,33 @@ from classiq.interface.ast_node import HashableASTNode
|
|
7
8
|
from classiq.interface.exceptions import ClassiqValueError
|
8
9
|
|
9
10
|
|
11
|
+
def rebuild_dynamic_enum(name: str, members: dict[str, int]) -> type[IntEnum]:
|
12
|
+
"""
|
13
|
+
Rebuilds the dynamic enum from its name and members.
|
14
|
+
Returns a new enum type.
|
15
|
+
"""
|
16
|
+
new_enum = IntEnum(name, members) # type: ignore[misc]
|
17
|
+
setattr(new_enum, "__members_data__", members) # noqa: B010
|
18
|
+
setattr(new_enum, "__reduce_ex__", dynamic_enum_reduce_ex) # noqa: B010
|
19
|
+
return new_enum
|
20
|
+
|
21
|
+
|
22
|
+
def dynamic_enum_reduce_ex(
|
23
|
+
obj: Any, protocol: int
|
24
|
+
) -> tuple[Callable[..., type[IntEnum]], tuple[str, dict[str, int]]]:
|
25
|
+
"""
|
26
|
+
Custom __reduce_ex__ for dynamic enums.
|
27
|
+
This function will be used when pickling an enum type or one of its members.
|
28
|
+
"""
|
29
|
+
# Get the enum type and its member data.
|
30
|
+
enum_type = type(obj)
|
31
|
+
members = getattr(enum_type, "__members_data__", None)
|
32
|
+
if members is None:
|
33
|
+
raise ValueError("Dynamic enum is missing __members_data__ attribute")
|
34
|
+
# Return the callable and arguments needed to rebuild the enum type.
|
35
|
+
return rebuild_dynamic_enum, (enum_type.__name__, members)
|
36
|
+
|
37
|
+
|
10
38
|
class EnumDeclaration(HashableASTNode):
|
11
39
|
name: str
|
12
40
|
|
@@ -39,8 +67,11 @@ class EnumDeclaration(HashableASTNode):
|
|
39
67
|
|
40
68
|
return members
|
41
69
|
|
42
|
-
def create_enum(self) -> IntEnum:
|
43
|
-
|
70
|
+
def create_enum(self) -> type[IntEnum]:
|
71
|
+
dynamic_enum = IntEnum(self.name, self.members) # type: ignore[misc]
|
72
|
+
setattr(dynamic_enum, "__members_data__", self.members) # noqa: B010
|
73
|
+
setattr(dynamic_enum, "__reduce_ex__", dynamic_enum_reduce_ex) # noqa: B010
|
74
|
+
return dynamic_enum
|
44
75
|
|
45
76
|
|
46
77
|
def declaration_from_enum(enum_type: EnumMeta) -> EnumDeclaration:
|
@@ -38,7 +38,7 @@ class UCC(ChemistryFunctionParams):
|
|
38
38
|
description="Maximum depth of the generated quantum circuit ansatz",
|
39
39
|
)
|
40
40
|
parameter_prefix: str = pydantic.Field(
|
41
|
-
default="
|
41
|
+
default="ucc_param_",
|
42
42
|
description="Prefix for the generated parameters",
|
43
43
|
)
|
44
44
|
|
@@ -1 +1 @@
|
|
1
|
-
INTERFACE_VERSION = "
|
1
|
+
INTERFACE_VERSION = "8"
|
@@ -1,6 +1,6 @@
|
|
1
1
|
from typing import TYPE_CHECKING, Literal
|
2
2
|
|
3
|
-
from classiq.interface.ast_node import ASTNodeType,
|
3
|
+
from classiq.interface.ast_node import ASTNodeType, reset_lists
|
4
4
|
from classiq.interface.generator.expressions.expression import Expression
|
5
5
|
from classiq.interface.model.quantum_statement import QuantumOperation
|
6
6
|
|
@@ -16,4 +16,4 @@ class ClassicalIf(QuantumOperation):
|
|
16
16
|
else_: "StatementBlock"
|
17
17
|
|
18
18
|
def _as_back_ref(self: ASTNodeType) -> ASTNodeType:
|
19
|
-
return
|
19
|
+
return reset_lists(self, ["then", "else_"])
|
@@ -2,7 +2,7 @@ from typing import TYPE_CHECKING, Literal, Optional
|
|
2
2
|
|
3
3
|
import pydantic
|
4
4
|
|
5
|
-
from classiq.interface.ast_node import ASTNodeType,
|
5
|
+
from classiq.interface.ast_node import ASTNodeType, reset_lists
|
6
6
|
from classiq.interface.generator.arith.arithmetic import compute_arithmetic_result_type
|
7
7
|
from classiq.interface.model.quantum_expressions.quantum_expression import (
|
8
8
|
QuantumExpressionOperation,
|
@@ -46,4 +46,4 @@ class Control(QuantumExpressionOperation):
|
|
46
46
|
)
|
47
47
|
|
48
48
|
def _as_back_ref(self: ASTNodeType) -> ASTNodeType:
|
49
|
-
return
|
49
|
+
return reset_lists(self, ["body", "else_block"])
|
@@ -1,6 +1,6 @@
|
|
1
1
|
from typing import TYPE_CHECKING, Literal
|
2
2
|
|
3
|
-
from classiq.interface.ast_node import ASTNodeType,
|
3
|
+
from classiq.interface.ast_node import ASTNodeType, reset_lists
|
4
4
|
from classiq.interface.model.quantum_statement import QuantumOperation
|
5
5
|
|
6
6
|
if TYPE_CHECKING:
|
@@ -13,4 +13,4 @@ class Invert(QuantumOperation):
|
|
13
13
|
body: "StatementBlock"
|
14
14
|
|
15
15
|
def _as_back_ref(self: ASTNodeType) -> ASTNodeType:
|
16
|
-
return
|
16
|
+
return reset_lists(self, ["body"])
|
classiq/interface/model/power.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
from typing import TYPE_CHECKING, Literal
|
2
2
|
|
3
|
-
from classiq.interface.ast_node import ASTNodeType,
|
3
|
+
from classiq.interface.ast_node import ASTNodeType, reset_lists
|
4
4
|
from classiq.interface.generator.expressions.expression import Expression
|
5
5
|
from classiq.interface.model.quantum_statement import QuantumOperation
|
6
6
|
|
@@ -15,4 +15,4 @@ class Power(QuantumOperation):
|
|
15
15
|
body: "StatementBlock"
|
16
16
|
|
17
17
|
def _as_back_ref(self: ASTNodeType) -> ASTNodeType:
|
18
|
-
return
|
18
|
+
return reset_lists(self, ["body"])
|
@@ -7,6 +7,7 @@ from typing import (
|
|
7
7
|
|
8
8
|
import pydantic
|
9
9
|
|
10
|
+
from classiq.interface.ast_node import ASTNodeType, reset_lists
|
10
11
|
from classiq.interface.exceptions import ClassiqError, ClassiqValueError
|
11
12
|
from classiq.interface.generator.expressions.expression import Expression
|
12
13
|
from classiq.interface.generator.functions.port_declaration import (
|
@@ -45,6 +46,9 @@ class QuantumFunctionCall(QuantumOperation):
|
|
45
46
|
default=None
|
46
47
|
)
|
47
48
|
|
49
|
+
def _as_back_ref(self: ASTNodeType) -> ASTNodeType:
|
50
|
+
return reset_lists(self, ["positional_args"])
|
51
|
+
|
48
52
|
@property
|
49
53
|
def func_decl(self) -> QuantumFunctionDeclaration:
|
50
54
|
if self._func_decl is None:
|
@@ -5,6 +5,7 @@ from uuid import UUID, uuid4
|
|
5
5
|
|
6
6
|
import pydantic
|
7
7
|
from pydantic import ConfigDict
|
8
|
+
from typing_extensions import Self
|
8
9
|
|
9
10
|
from classiq.interface.ast_node import ASTNode
|
10
11
|
from classiq.interface.helpers.pydantic_model_helpers import values_with_discriminator
|
@@ -21,6 +22,18 @@ class QuantumStatement(ASTNode):
|
|
21
22
|
description="A unique identifier for this operation", default_factory=uuid4
|
22
23
|
)
|
23
24
|
|
25
|
+
def model_copy(
|
26
|
+
self,
|
27
|
+
*,
|
28
|
+
update: Optional[dict[str, Any]] = None,
|
29
|
+
deep: bool = False,
|
30
|
+
keep_uuid: bool = False
|
31
|
+
) -> Self:
|
32
|
+
if not keep_uuid:
|
33
|
+
update = update or dict()
|
34
|
+
update.setdefault("uuid", uuid4())
|
35
|
+
return super().model_copy(update=update, deep=deep)
|
36
|
+
|
24
37
|
@pydantic.model_validator(mode="before")
|
25
38
|
@classmethod
|
26
39
|
def _set_kind(cls, values: Any) -> dict[str, Any]:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
from typing import TYPE_CHECKING, Literal
|
2
2
|
|
3
|
-
from classiq.interface.ast_node import ASTNodeType,
|
3
|
+
from classiq.interface.ast_node import ASTNodeType, reset_lists
|
4
4
|
from classiq.interface.generator.expressions.expression import Expression
|
5
5
|
from classiq.interface.model.quantum_statement import QuantumOperation
|
6
6
|
|
@@ -16,4 +16,4 @@ class Repeat(QuantumOperation):
|
|
16
16
|
body: "StatementBlock"
|
17
17
|
|
18
18
|
def _as_back_ref(self: ASTNodeType) -> ASTNodeType:
|
19
|
-
return
|
19
|
+
return reset_lists(self, ["body"])
|
@@ -27,8 +27,8 @@ from classiq.interface.model.within_apply_operation import WithinApply
|
|
27
27
|
|
28
28
|
ConcreteQuantumStatement = Annotated[
|
29
29
|
Union[
|
30
|
-
Allocate,
|
31
30
|
QuantumFunctionCall,
|
31
|
+
Allocate,
|
32
32
|
ArithmeticOperation,
|
33
33
|
AmplitudeLoadingOperation,
|
34
34
|
VariableDeclarationStatement,
|
@@ -1,6 +1,6 @@
|
|
1
1
|
from typing import TYPE_CHECKING, Literal
|
2
2
|
|
3
|
-
from classiq.interface.ast_node import ASTNodeType,
|
3
|
+
from classiq.interface.ast_node import ASTNodeType, reset_lists
|
4
4
|
from classiq.interface.model.quantum_statement import QuantumOperation
|
5
5
|
|
6
6
|
if TYPE_CHECKING:
|
@@ -14,4 +14,4 @@ class WithinApply(QuantumOperation):
|
|
14
14
|
action: "StatementBlock"
|
15
15
|
|
16
16
|
def _as_back_ref(self: ASTNodeType) -> ASTNodeType:
|
17
|
-
return
|
17
|
+
return reset_lists(self, ["compute", "action"])
|
@@ -21,6 +21,7 @@ from classiq.interface.generator.functions.classical_function_declaration import
|
|
21
21
|
)
|
22
22
|
from classiq.interface.generator.functions.classical_type import (
|
23
23
|
Bool,
|
24
|
+
ClassicalArray,
|
24
25
|
ClassicalList,
|
25
26
|
ClassicalType,
|
26
27
|
OpaqueHandle,
|
@@ -64,7 +65,7 @@ def qmod_val_to_python(val: ExpressionValue, qmod_type: ClassicalType) -> Any:
|
|
64
65
|
if isinstance(val, (Enum, int)):
|
65
66
|
return val
|
66
67
|
|
67
|
-
elif isinstance(qmod_type, ClassicalList):
|
68
|
+
elif isinstance(qmod_type, (ClassicalArray, ClassicalList)):
|
68
69
|
if isinstance(val, list):
|
69
70
|
return [qmod_val_to_python(elem, qmod_type.element_type) for elem in val]
|
70
71
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import dataclasses
|
2
2
|
from collections.abc import Sequence
|
3
3
|
from dataclasses import dataclass, field
|
4
|
-
from typing import TYPE_CHECKING
|
4
|
+
from typing import TYPE_CHECKING
|
5
5
|
|
6
6
|
from classiq.interface.enum_utils import StrEnum
|
7
7
|
from classiq.interface.exceptions import (
|
@@ -34,8 +34,8 @@ if TYPE_CHECKING:
|
|
34
34
|
from classiq.model_expansions.closure import FunctionClosure
|
35
35
|
|
36
36
|
|
37
|
-
|
38
|
-
|
37
|
+
INITIALIZED_VAR_MESSAGE = "Variable '{}' should be uninitialized here"
|
38
|
+
UNINITIALIZED_VAR_MESSAGE = "Variable '{}' should be initialized here"
|
39
39
|
|
40
40
|
|
41
41
|
class PortDirection(StrEnum):
|
@@ -103,6 +103,11 @@ class _CapturedHandle:
|
|
103
103
|
def change_direction(self, new_direction: PortDirection) -> "_CapturedHandle":
|
104
104
|
return dataclasses.replace(self, direction=new_direction)
|
105
105
|
|
106
|
+
def change_defining_function(
|
107
|
+
self, new_defining_function: "FunctionClosure"
|
108
|
+
) -> "_CapturedHandle":
|
109
|
+
return dataclasses.replace(self, defining_function=new_defining_function)
|
110
|
+
|
106
111
|
def set_propagated(self) -> "_CapturedHandle":
|
107
112
|
return dataclasses.replace(self, is_propagated=True)
|
108
113
|
|
@@ -119,9 +124,13 @@ class _CapturedHandle:
|
|
119
124
|
return dataclasses.replace(self, handle=handle, quantum_type=quantum_type)
|
120
125
|
|
121
126
|
|
127
|
+
HandleState = tuple[str, "FunctionClosure", bool]
|
128
|
+
|
129
|
+
|
122
130
|
@dataclass
|
123
131
|
class CapturedVars:
|
124
132
|
_captured_handles: list[_CapturedHandle] = field(default_factory=list)
|
133
|
+
_handle_states: list[HandleState] = field(default_factory=list)
|
125
134
|
|
126
135
|
def capture_handle(
|
127
136
|
self,
|
@@ -151,9 +160,25 @@ class CapturedVars:
|
|
151
160
|
else "allocate"
|
152
161
|
)
|
153
162
|
raise ClassiqExpansionError(
|
154
|
-
f"Cannot
|
163
|
+
f"Cannot {verb} partial variable {str(captured_handle.handle)!r}"
|
155
164
|
)
|
156
165
|
|
166
|
+
# A handle should be in either _captured_handles or _handle_states, but never
|
167
|
+
# both
|
168
|
+
|
169
|
+
new_handle_states = []
|
170
|
+
for var_name, defining_function, handle_state in self._handle_states:
|
171
|
+
if captured_handle.handle.name == var_name and _same_closure(
|
172
|
+
captured_handle.defining_function, defining_function
|
173
|
+
):
|
174
|
+
# verify variable state
|
175
|
+
self._conjugate_direction(
|
176
|
+
handle_state, captured_handle.direction, var_name
|
177
|
+
)
|
178
|
+
else:
|
179
|
+
new_handle_states.append((var_name, defining_function, handle_state))
|
180
|
+
self._handle_states = new_handle_states
|
181
|
+
|
157
182
|
new_captured_handles = []
|
158
183
|
for existing_captured_handle in self._captured_handles:
|
159
184
|
if not existing_captured_handle.is_same_var(captured_handle):
|
@@ -163,8 +188,12 @@ class CapturedVars:
|
|
163
188
|
existing_captured_handle
|
164
189
|
)
|
165
190
|
if existing_captured_handle.handle == captured_handle.handle:
|
166
|
-
captured_handle =
|
167
|
-
|
191
|
+
captured_handle = captured_handle.change_direction(
|
192
|
+
self._conjugate_direction(
|
193
|
+
existing_captured_handle.direction,
|
194
|
+
captured_handle.direction,
|
195
|
+
str(captured_handle.handle),
|
196
|
+
)
|
168
197
|
)
|
169
198
|
elif captured_handle.handle.overlaps(existing_captured_handle.handle):
|
170
199
|
captured_handle = self._intersect_handles(
|
@@ -177,44 +206,43 @@ class CapturedVars:
|
|
177
206
|
|
178
207
|
def _conjugate_direction(
|
179
208
|
self,
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
return captured_handle.change_direction(PortDirection.Input)
|
188
|
-
raise ClassiqExpansionError(
|
189
|
-
ALREADY_FREED_MESSAGE.format(captured_handle.handle)
|
209
|
+
source_direction: PortDirection | bool,
|
210
|
+
target_direction: PortDirection,
|
211
|
+
var_name: str,
|
212
|
+
) -> PortDirection:
|
213
|
+
if isinstance(source_direction, bool):
|
214
|
+
source_direction = (
|
215
|
+
PortDirection.Inout if source_direction else PortDirection.Outin
|
190
216
|
)
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
PortDirection.
|
197
|
-
)
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
if captured_handle.direction in (
|
217
|
+
|
218
|
+
if source_direction == PortDirection.Input:
|
219
|
+
if target_direction == PortDirection.Output:
|
220
|
+
return PortDirection.Inout
|
221
|
+
if target_direction == PortDirection.Outin:
|
222
|
+
return PortDirection.Input
|
223
|
+
raise ClassiqExpansionError(UNINITIALIZED_VAR_MESSAGE.format(var_name))
|
224
|
+
|
225
|
+
if source_direction == PortDirection.Output:
|
226
|
+
if target_direction == PortDirection.Input:
|
227
|
+
return PortDirection.Outin
|
228
|
+
if target_direction in (
|
204
229
|
PortDirection.Output,
|
205
230
|
PortDirection.Outin,
|
206
231
|
):
|
207
|
-
raise ClassiqExpansionError(
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
PortDirection.Input,
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
)
|
217
|
-
|
232
|
+
raise ClassiqExpansionError(INITIALIZED_VAR_MESSAGE.format(var_name))
|
233
|
+
return PortDirection.Output
|
234
|
+
|
235
|
+
if source_direction == PortDirection.Inout:
|
236
|
+
if target_direction in (PortDirection.Input, PortDirection.Inout):
|
237
|
+
return target_direction
|
238
|
+
raise ClassiqExpansionError(INITIALIZED_VAR_MESSAGE.format(var_name))
|
239
|
+
|
240
|
+
if source_direction == PortDirection.Outin:
|
241
|
+
if target_direction in (PortDirection.Output, PortDirection.Outin):
|
242
|
+
return target_direction
|
243
|
+
raise ClassiqExpansionError(UNINITIALIZED_VAR_MESSAGE.format(var_name))
|
244
|
+
|
245
|
+
raise ClassiqInternalExpansionError(f"Unexpected direction {source_direction}")
|
218
246
|
|
219
247
|
def _intersect_handles(
|
220
248
|
self,
|
@@ -227,7 +255,7 @@ class CapturedVars:
|
|
227
255
|
PortDirection.Outin,
|
228
256
|
):
|
229
257
|
raise ClassiqExpansionError(
|
230
|
-
|
258
|
+
UNINITIALIZED_VAR_MESSAGE.format(captured_handle.handle)
|
231
259
|
)
|
232
260
|
return existing_captured_handle
|
233
261
|
|
@@ -237,7 +265,7 @@ class CapturedVars:
|
|
237
265
|
PortDirection.Outin,
|
238
266
|
):
|
239
267
|
raise ClassiqExpansionError(
|
240
|
-
|
268
|
+
INITIALIZED_VAR_MESSAGE.format(captured_handle.handle)
|
241
269
|
)
|
242
270
|
return captured_handle
|
243
271
|
|
@@ -316,16 +344,7 @@ class CapturedVars:
|
|
316
344
|
]
|
317
345
|
)
|
318
346
|
|
319
|
-
def filter_vars(
|
320
|
-
self,
|
321
|
-
current_function: "FunctionClosure",
|
322
|
-
current_declarations: Optional[list[VariableDeclarationStatement]] = None,
|
323
|
-
) -> "CapturedVars":
|
324
|
-
current_declared_vars = (
|
325
|
-
None
|
326
|
-
if current_declarations is None
|
327
|
-
else {decl.name for decl in current_declarations}
|
328
|
-
)
|
347
|
+
def filter_vars(self, current_function: "FunctionClosure") -> "CapturedVars":
|
329
348
|
return CapturedVars(
|
330
349
|
_captured_handles=[
|
331
350
|
captured_handle
|
@@ -333,7 +352,18 @@ class CapturedVars:
|
|
333
352
|
if not _same_closure(
|
334
353
|
captured_handle.defining_function, current_function
|
335
354
|
)
|
336
|
-
|
355
|
+
]
|
356
|
+
)
|
357
|
+
|
358
|
+
def filter_var_decls(
|
359
|
+
self, current_declarations: list[VariableDeclarationStatement]
|
360
|
+
) -> "CapturedVars":
|
361
|
+
current_declared_vars = {decl.name for decl in current_declarations}
|
362
|
+
return CapturedVars(
|
363
|
+
_captured_handles=[
|
364
|
+
captured_handle
|
365
|
+
for captured_handle in self._captured_handles
|
366
|
+
if (
|
337
367
|
current_declared_vars is not None
|
338
368
|
and captured_handle.handle.name not in current_declared_vars
|
339
369
|
)
|
@@ -376,8 +406,92 @@ class CapturedVars:
|
|
376
406
|
if not captured_handle.is_propagated
|
377
407
|
}
|
378
408
|
|
409
|
+
def init_var(self, var_name: str, defining_function: "FunctionClosure") -> None:
|
410
|
+
self._handle_states.append((var_name, defining_function, False))
|
411
|
+
|
412
|
+
def init_params(self, func: "FunctionClosure") -> None:
|
413
|
+
ports = {
|
414
|
+
param.name: param.direction
|
415
|
+
for param in func.positional_arg_declarations
|
416
|
+
if isinstance(param, PortDeclaration)
|
417
|
+
}
|
418
|
+
new_handle_states = [
|
419
|
+
handle_state
|
420
|
+
for handle_state in self._handle_states
|
421
|
+
if handle_state[0] not in ports
|
422
|
+
]
|
423
|
+
for var_name, direction in ports.items():
|
424
|
+
new_handle_states.append(
|
425
|
+
(
|
426
|
+
var_name,
|
427
|
+
func,
|
428
|
+
PortDirection.load(direction)
|
429
|
+
in (PortDirection.Input, PortDirection.Inout),
|
430
|
+
)
|
431
|
+
)
|
432
|
+
self._handle_states = new_handle_states
|
433
|
+
|
434
|
+
def _get_handle_states(self) -> list[HandleState]:
|
435
|
+
return self._handle_states + list(
|
436
|
+
{
|
437
|
+
(
|
438
|
+
captured_handle.handle.name,
|
439
|
+
captured_handle.defining_function.depth,
|
440
|
+
): (
|
441
|
+
captured_handle.handle.name,
|
442
|
+
captured_handle.defining_function,
|
443
|
+
captured_handle.direction
|
444
|
+
in (PortDirection.Output, PortDirection.Inout),
|
445
|
+
)
|
446
|
+
for captured_handle in self._captured_handles
|
447
|
+
}.values()
|
448
|
+
)
|
449
|
+
|
450
|
+
def set_parent(self, parent: "CapturedVars") -> None:
|
451
|
+
self._handle_states += parent._get_handle_states()
|
452
|
+
|
453
|
+
def get_state(self, var_name: str, defining_function: "FunctionClosure") -> bool:
|
454
|
+
for name, func, state in self._handle_states:
|
455
|
+
if name == var_name and _same_closure(func, defining_function):
|
456
|
+
return state
|
457
|
+
for captured_handle in self._captured_handles:
|
458
|
+
if captured_handle.handle.name == var_name and _same_closure(
|
459
|
+
captured_handle.defining_function, defining_function
|
460
|
+
):
|
461
|
+
return captured_handle.direction in (
|
462
|
+
PortDirection.Output,
|
463
|
+
PortDirection.Inout,
|
464
|
+
)
|
465
|
+
raise ClassiqInternalExpansionError(
|
466
|
+
f"Cannot find {var_name!r} from {defining_function.name!r}"
|
467
|
+
)
|
468
|
+
|
379
469
|
def clone(self) -> "CapturedVars":
|
380
|
-
return CapturedVars(
|
470
|
+
return CapturedVars(
|
471
|
+
_captured_handles=list(self._captured_handles),
|
472
|
+
_handle_states=list(self._handle_states),
|
473
|
+
)
|
474
|
+
|
475
|
+
def set(
|
476
|
+
self,
|
477
|
+
other: "CapturedVars",
|
478
|
+
source_func: "FunctionClosure",
|
479
|
+
target_func: "FunctionClosure",
|
480
|
+
) -> None:
|
481
|
+
self._captured_handles = []
|
482
|
+
for captured_handle in other._captured_handles:
|
483
|
+
if _same_closure(captured_handle.defining_function, source_func):
|
484
|
+
self._captured_handles.append(
|
485
|
+
captured_handle.change_defining_function(target_func)
|
486
|
+
)
|
487
|
+
else:
|
488
|
+
self._captured_handles.append(captured_handle)
|
489
|
+
self._handle_states = []
|
490
|
+
for var, defining_function, state in other._handle_states:
|
491
|
+
if _same_closure(defining_function, source_func):
|
492
|
+
self._handle_states.append((var, target_func, state))
|
493
|
+
else:
|
494
|
+
self._handle_states.append((var, defining_function, state))
|
381
495
|
|
382
496
|
|
383
497
|
def _same_closure(closure_1: "FunctionClosure", closure_2: "FunctionClosure") -> bool:
|
@@ -433,3 +547,19 @@ def validate_captured_directions(
|
|
433
547
|
raise ClassiqExpansionError(
|
434
548
|
f"Captured quantum variables {captured_outputs!r} cannot be used as outputs"
|
435
549
|
)
|
550
|
+
|
551
|
+
|
552
|
+
def validate_end_state(func: "FunctionClosure", captured_vars: CapturedVars) -> None:
|
553
|
+
for param in func.positional_arg_declarations:
|
554
|
+
if isinstance(param, PortDeclaration):
|
555
|
+
state = captured_vars.get_state(param.name, func)
|
556
|
+
expected_state = param.direction in (
|
557
|
+
PortDeclarationDirection.Output,
|
558
|
+
PortDeclarationDirection.Inout,
|
559
|
+
)
|
560
|
+
if state != expected_state:
|
561
|
+
status = "initialized" if expected_state else "uninitialized"
|
562
|
+
raise ClassiqExpansionError(
|
563
|
+
f"At the end of function {func.name}, variable {param.name!r} "
|
564
|
+
f"should be {status}"
|
565
|
+
)
|
@@ -83,18 +83,21 @@ class FunctionClosure(Closure):
|
|
83
83
|
scope: Scope,
|
84
84
|
body: Optional[Sequence[QuantumStatement]] = None,
|
85
85
|
positional_arg_declarations: Sequence[PositionalArg] = tuple(),
|
86
|
-
|
86
|
+
lambda_external_vars: Optional[CapturedVars] = None,
|
87
87
|
is_atomic: bool = False,
|
88
88
|
**kwargs: Any,
|
89
89
|
) -> Self:
|
90
90
|
blocks = {"body": body} if body is not None else {}
|
91
|
+
captured_vars = CapturedVars()
|
92
|
+
if lambda_external_vars is not None:
|
93
|
+
captured_vars.set_parent(lambda_external_vars)
|
91
94
|
return cls(
|
92
95
|
name,
|
93
96
|
blocks,
|
94
97
|
scope,
|
95
98
|
positional_arg_declarations,
|
96
|
-
|
97
|
-
|
99
|
+
captured_vars,
|
100
|
+
lambda_external_vars is not None,
|
98
101
|
is_atomic,
|
99
102
|
**kwargs,
|
100
103
|
)
|