classiq 0.61.0__py3-none-any.whl → 0.63.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/__init__.py +3 -0
- classiq/_internals/api_wrapper.py +6 -26
- classiq/_internals/client.py +1 -9
- classiq/applications/chemistry/chemistry_model_constructor.py +1 -1
- classiq/applications/combinatorial_helpers/combinatorial_problem_utils.py +26 -8
- classiq/applications/combinatorial_helpers/optimization_model.py +13 -2
- classiq/applications/combinatorial_helpers/pyomo_utils.py +143 -13
- classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +1 -1
- classiq/applications/combinatorial_optimization/combinatorial_problem.py +58 -23
- classiq/applications/grover/grover_model_constructor.py +1 -1
- classiq/applications/libraries/qmci_library.py +2 -1
- classiq/execution/execution_session.py +66 -96
- classiq/execution/jobs.py +12 -10
- classiq/interface/_version.py +1 -1
- classiq/interface/backend/backend_preferences.py +26 -5
- classiq/interface/backend/pydantic_backend.py +1 -1
- classiq/interface/backend/quantum_backend_providers.py +3 -1
- classiq/interface/chemistry/operator.py +0 -204
- classiq/interface/execution/primitives.py +1 -0
- classiq/interface/generator/compiler_keywords.py +4 -0
- classiq/interface/generator/copy.py +47 -0
- classiq/interface/generator/function_param_list_without_self_reference.py +2 -0
- classiq/interface/generator/functions/type_name.py +6 -0
- classiq/interface/generator/generated_circuit_data.py +22 -7
- classiq/interface/generator/model/model.py +3 -0
- classiq/interface/generator/model/preferences/preferences.py +14 -1
- classiq/interface/generator/quantum_function_call.py +4 -2
- classiq/interface/generator/types/compilation_metadata.py +2 -1
- classiq/interface/model/handle_binding.py +50 -5
- classiq/interface/model/quantum_type.py +16 -0
- classiq/interface/server/routes.py +1 -3
- classiq/model_expansions/capturing/captured_vars.py +114 -28
- classiq/model_expansions/closure.py +25 -65
- classiq/model_expansions/function_builder.py +19 -9
- classiq/model_expansions/generative_functions.py +16 -2
- classiq/model_expansions/interpreter.py +110 -66
- classiq/model_expansions/model_tables.py +4 -0
- classiq/model_expansions/quantum_operations/call_emitter.py +83 -20
- classiq/model_expansions/quantum_operations/classicalif.py +1 -1
- classiq/model_expansions/quantum_operations/control.py +3 -10
- classiq/model_expansions/quantum_operations/emitter.py +3 -4
- classiq/model_expansions/quantum_operations/quantum_assignment_operation.py +1 -2
- classiq/model_expansions/quantum_operations/quantum_function_call.py +1 -1
- classiq/model_expansions/quantum_operations/repeat.py +4 -3
- classiq/model_expansions/quantum_operations/shallow_emitter.py +9 -3
- classiq/model_expansions/scope.py +9 -13
- classiq/model_expansions/scope_initialization.py +34 -25
- classiq/model_expansions/transformers/var_splitter.py +57 -7
- classiq/open_library/__init__.py +4 -0
- classiq/open_library/functions/__init__.py +130 -0
- classiq/{qmod/builtins → open_library}/functions/amplitude_estimation.py +2 -2
- classiq/{qmod/builtins → open_library}/functions/discrete_sine_cosine_transform.py +6 -4
- classiq/{qmod/builtins → open_library}/functions/grover.py +2 -2
- classiq/{qmod/builtins → open_library}/functions/linear_pauli_rotation.py +1 -1
- classiq/{qmod/builtins → open_library}/functions/modular_exponentiation.py +2 -2
- classiq/{qmod/builtins → open_library}/functions/qpe.py +2 -2
- classiq/{qmod/builtins → open_library}/functions/state_preparation.py +6 -149
- classiq/{qmod/builtins → open_library}/functions/swap_test.py +1 -1
- classiq/open_library/functions/utility_functions.py +81 -0
- classiq/{qmod/builtins → open_library}/functions/variational.py +1 -1
- classiq/qmod/builtins/functions/__init__.py +4 -130
- classiq/qmod/builtins/functions/allocation.py +150 -0
- classiq/qmod/builtins/functions/arithmetic.py +0 -34
- classiq/qmod/builtins/functions/operators.py +0 -6
- classiq/qmod/builtins/operations.py +19 -80
- classiq/qmod/create_model_function.py +8 -162
- classiq/qmod/generative.py +0 -16
- classiq/qmod/model_state_container.py +7 -0
- classiq/qmod/native/pretty_printer.py +10 -11
- classiq/qmod/pretty_print/pretty_printer.py +1 -1
- classiq/qmod/python_classical_type.py +1 -5
- classiq/qmod/qfunc.py +11 -12
- classiq/qmod/qmod_variable.py +1 -3
- classiq/qmod/quantum_expandable.py +23 -1
- classiq/qmod/quantum_function.py +69 -7
- {classiq-0.61.0.dist-info → classiq-0.63.0.dist-info}/METADATA +2 -1
- {classiq-0.61.0.dist-info → classiq-0.63.0.dist-info}/RECORD +82 -78
- classiq/qmod/builtins/functions/utility_functions.py +0 -43
- /classiq/{qmod/builtins → open_library}/functions/hea.py +0 -0
- /classiq/{qmod/builtins → open_library}/functions/qaoa_penalty.py +0 -0
- /classiq/{qmod/builtins → open_library}/functions/qft_functions.py +0 -0
- /classiq/{qmod/builtins → open_library}/functions/qsvt.py +0 -0
- {classiq-0.61.0.dist-info → classiq-0.63.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,47 @@
|
|
1
|
+
from typing import Optional
|
2
|
+
|
3
|
+
import pydantic
|
4
|
+
|
5
|
+
from classiq.interface.generator.arith import argument_utils
|
6
|
+
from classiq.interface.generator.arith.register_user_input import RegisterArithmeticInfo
|
7
|
+
from classiq.interface.generator.function_params import FunctionParams
|
8
|
+
|
9
|
+
|
10
|
+
class Copy(FunctionParams):
|
11
|
+
source: argument_utils.RegisterOrConst
|
12
|
+
target: RegisterArithmeticInfo
|
13
|
+
output_size: Optional[pydantic.PositiveInt] = pydantic.Field(default=None)
|
14
|
+
|
15
|
+
@property
|
16
|
+
def source_size(self) -> int:
|
17
|
+
return argument_utils.size(self.source)
|
18
|
+
|
19
|
+
@property
|
20
|
+
def source_reg_size(self) -> int:
|
21
|
+
return (
|
22
|
+
self.source.size if isinstance(self.source, RegisterArithmeticInfo) else 0
|
23
|
+
)
|
24
|
+
|
25
|
+
@property
|
26
|
+
def source_fraction_places(self) -> int:
|
27
|
+
return argument_utils.fraction_places(self.source)
|
28
|
+
|
29
|
+
@property
|
30
|
+
def offset(self) -> int:
|
31
|
+
return self.target.fraction_places - self.source_fraction_places
|
32
|
+
|
33
|
+
@property
|
34
|
+
def source_name(self) -> str:
|
35
|
+
return "source"
|
36
|
+
|
37
|
+
@property
|
38
|
+
def target_name(self) -> str:
|
39
|
+
return "target"
|
40
|
+
|
41
|
+
def _create_ios(self) -> None:
|
42
|
+
self._inputs = {
|
43
|
+
self.target_name: self.target,
|
44
|
+
}
|
45
|
+
if isinstance(self.source, RegisterArithmeticInfo):
|
46
|
+
self._inputs[self.source_name] = self.source
|
47
|
+
self._outputs = {**self._inputs}
|
@@ -28,6 +28,7 @@ from classiq.interface.generator.arith.unary_ops import BitwiseInvert, Negation,
|
|
28
28
|
from classiq.interface.generator.commuting_pauli_exponentiation import (
|
29
29
|
CommutingPauliExponentiation,
|
30
30
|
)
|
31
|
+
from classiq.interface.generator.copy import Copy
|
31
32
|
from classiq.interface.generator.entangler_params import (
|
32
33
|
GridEntangler,
|
33
34
|
HypercubeEntangler,
|
@@ -148,6 +149,7 @@ function_param_library_without_self_reference: FunctionParamLibrary = (
|
|
148
149
|
PiecewiseLinearAmplitudeLoading,
|
149
150
|
PiecewiseLinearRotationAmplitudeLoading,
|
150
151
|
HadamardTransform,
|
152
|
+
Copy,
|
151
153
|
},
|
152
154
|
standard_gate_function_param_library.param_list,
|
153
155
|
oracle_function_param_library.param_list,
|
@@ -69,6 +69,12 @@ class TypeName(ClassicalType, QuantumType):
|
|
69
69
|
def set_fields(self, fields: Mapping[str, "ConcreteQuantumType"]) -> None:
|
70
70
|
self._assigned_fields = fields
|
71
71
|
|
72
|
+
@property
|
73
|
+
def is_instantiated(self) -> bool:
|
74
|
+
return self.has_fields and all(
|
75
|
+
field_type.is_instantiated for field_type in self.fields.values()
|
76
|
+
)
|
77
|
+
|
72
78
|
|
73
79
|
class Enum(TypeName):
|
74
80
|
pass
|
@@ -197,14 +197,16 @@ class FunctionDebugInfoInterface(pydantic.BaseModel):
|
|
197
197
|
|
198
198
|
updated_children: list[FunctionDebugInfoInterface] = []
|
199
199
|
for child in self.children:
|
200
|
-
updated_child = child.
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
200
|
+
updated_child = child.white_new_absolute_qubits(self.absolute_qubits)
|
201
|
+
if updated_child.override_debug_info is None:
|
202
|
+
updated_child = updated_child.propagate_absolute_qubits()
|
203
|
+
else:
|
204
|
+
updated_child.override_debug_info = (
|
205
|
+
updated_child.override_debug_info.white_new_absolute_qubits(
|
206
|
+
absolute_qubits=self.absolute_qubits
|
207
|
+
).propagate_absolute_qubits()
|
205
208
|
)
|
206
|
-
)
|
207
|
-
updated_children.append(updated_child.propagate_absolute_qubits())
|
209
|
+
updated_children.append(updated_child)
|
208
210
|
|
209
211
|
return self.model_copy(
|
210
212
|
update=dict(
|
@@ -213,6 +215,17 @@ class FunctionDebugInfoInterface(pydantic.BaseModel):
|
|
213
215
|
)
|
214
216
|
)
|
215
217
|
|
218
|
+
def white_new_absolute_qubits(
|
219
|
+
self, absolute_qubits: tuple[int, ...]
|
220
|
+
) -> "FunctionDebugInfoInterface":
|
221
|
+
return self.model_copy(
|
222
|
+
update=dict(
|
223
|
+
absolute_qubits=_get_absolute_from_relative(
|
224
|
+
absolute_qubits, self.relative_qubits
|
225
|
+
)
|
226
|
+
)
|
227
|
+
)
|
228
|
+
|
216
229
|
def inverse(self) -> "FunctionDebugInfoInterface":
|
217
230
|
inverted_children = [child.inverse() for child in self.children[::-1]]
|
218
231
|
return self.model_copy(
|
@@ -226,6 +239,8 @@ class FunctionDebugInfoInterface(pydantic.BaseModel):
|
|
226
239
|
def _get_absolute_from_relative(
|
227
240
|
absolute_qubits: tuple[int, ...], relative_qubits: tuple[int, ...]
|
228
241
|
) -> tuple[int, ...]:
|
242
|
+
if len(relative_qubits) == 0:
|
243
|
+
return tuple()
|
229
244
|
if max(relative_qubits) >= len(absolute_qubits):
|
230
245
|
_logger.warning(
|
231
246
|
"Invalid qubit computation (relative qubits: %s, absolute qubits: %s)",
|
@@ -1,4 +1,5 @@
|
|
1
1
|
from collections.abc import Sequence
|
2
|
+
from enum import IntEnum
|
2
3
|
from typing import TYPE_CHECKING, Annotated, Any, Optional, Union
|
3
4
|
|
4
5
|
import pydantic
|
@@ -64,6 +65,13 @@ else:
|
|
64
65
|
]
|
65
66
|
|
66
67
|
|
68
|
+
class OptimizationLevel(IntEnum):
|
69
|
+
NONE = 0
|
70
|
+
LIGHT = 1
|
71
|
+
MEDIUM = 2
|
72
|
+
HIGH = 3
|
73
|
+
|
74
|
+
|
67
75
|
class TranspilationOption(StrEnum):
|
68
76
|
NONE = "none"
|
69
77
|
DECOMPOSE = "decompose"
|
@@ -156,7 +164,12 @@ class Preferences(pydantic.BaseModel, extra="forbid"):
|
|
156
164
|
)
|
157
165
|
synthesize_all_separately: bool = pydantic.Field(
|
158
166
|
default=False,
|
159
|
-
description="If true,
|
167
|
+
description="If true, a heuristic is used to determine if a function should be synthesized separately",
|
168
|
+
deprecated=True,
|
169
|
+
)
|
170
|
+
optimization_level: OptimizationLevel = pydantic.Field(
|
171
|
+
default=OptimizationLevel.HIGH,
|
172
|
+
description="The optimization level used during synthesis; determines the trade-off between synthesis speed and the quality of the results",
|
160
173
|
)
|
161
174
|
output_format: PydanticConstrainedQuantumFormatList = pydantic.Field(
|
162
175
|
default=[QuantumFormat.QASM],
|
@@ -22,7 +22,9 @@ from pydantic_core.core_schema import ValidationInfo
|
|
22
22
|
from classiq.interface.exceptions import ClassiqControlError, ClassiqValueError
|
23
23
|
from classiq.interface.generator import function_param_list, function_params as f_params
|
24
24
|
from classiq.interface.generator.arith.arithmetic import Arithmetic
|
25
|
-
from classiq.interface.generator.compiler_keywords import
|
25
|
+
from classiq.interface.generator.compiler_keywords import (
|
26
|
+
generate_original_function_name,
|
27
|
+
)
|
26
28
|
from classiq.interface.generator.control_state import ControlState
|
27
29
|
from classiq.interface.generator.function_params import (
|
28
30
|
NAME_REGEX,
|
@@ -237,7 +239,7 @@ class SynthesisQuantumFunctionCall(BaseModel):
|
|
237
239
|
suffix = f"{SUFFIX_MARKER}_{randomize_suffix()}"
|
238
240
|
if not function or params is None:
|
239
241
|
return name if name else suffix
|
240
|
-
return f"{function
|
242
|
+
return f"{generate_original_function_name(function)}_{suffix}"
|
241
243
|
|
242
244
|
@pydantic.model_validator(mode="before")
|
243
245
|
@classmethod
|
@@ -149,8 +149,7 @@ class SubscriptHandleBinding(NestedHandleBinding):
|
|
149
149
|
if (
|
150
150
|
isinstance(other_handle, SlicedHandleBinding)
|
151
151
|
and self.index.is_evaluated()
|
152
|
-
and other_handle.
|
153
|
-
and other_handle.end.is_evaluated()
|
152
|
+
and other_handle._is_evaluated()
|
154
153
|
):
|
155
154
|
return (
|
156
155
|
other_handle.start.to_int_value()
|
@@ -159,6 +158,26 @@ class SubscriptHandleBinding(NestedHandleBinding):
|
|
159
158
|
)
|
160
159
|
return False
|
161
160
|
|
161
|
+
def replace_prefix(
|
162
|
+
self, prefix: HandleBinding, replacement: HandleBinding
|
163
|
+
) -> HandleBinding:
|
164
|
+
if (
|
165
|
+
isinstance(prefix, SlicedHandleBinding)
|
166
|
+
and self.base_handle == prefix.base_handle
|
167
|
+
and self.index.is_evaluated()
|
168
|
+
and prefix._is_evaluated()
|
169
|
+
and prefix.start.to_int_value()
|
170
|
+
<= self.index.to_int_value()
|
171
|
+
< prefix.end.to_int_value()
|
172
|
+
):
|
173
|
+
return SubscriptHandleBinding(
|
174
|
+
base_handle=replacement,
|
175
|
+
index=Expression(
|
176
|
+
expr=str(self.index.to_int_value() - prefix.start.to_int_value())
|
177
|
+
),
|
178
|
+
)
|
179
|
+
return super().replace_prefix(prefix, replacement)
|
180
|
+
|
162
181
|
|
163
182
|
class SlicedHandleBinding(NestedHandleBinding):
|
164
183
|
start: Expression
|
@@ -192,7 +211,7 @@ class SlicedHandleBinding(NestedHandleBinding):
|
|
192
211
|
)
|
193
212
|
|
194
213
|
def _tail_overlaps(self, other_handle: "HandleBinding") -> bool:
|
195
|
-
if not self.
|
214
|
+
if not self._is_evaluated():
|
196
215
|
return False
|
197
216
|
start = self.start.to_int_value()
|
198
217
|
end = self.end.to_int_value()
|
@@ -203,8 +222,7 @@ class SlicedHandleBinding(NestedHandleBinding):
|
|
203
222
|
return start <= other_handle.index.to_int_value() < end
|
204
223
|
if (
|
205
224
|
isinstance(other_handle, SlicedHandleBinding)
|
206
|
-
and other_handle.
|
207
|
-
and other_handle.end.is_evaluated()
|
225
|
+
and other_handle._is_evaluated()
|
208
226
|
):
|
209
227
|
other_start = other_handle.start.to_int_value()
|
210
228
|
other_end = other_handle.end.to_int_value()
|
@@ -231,6 +249,33 @@ class SlicedHandleBinding(NestedHandleBinding):
|
|
231
249
|
)
|
232
250
|
return Expression(expr=f"({self.base_handle.end})-({self.end})")
|
233
251
|
|
252
|
+
def replace_prefix(
|
253
|
+
self, prefix: HandleBinding, replacement: HandleBinding
|
254
|
+
) -> HandleBinding:
|
255
|
+
if (
|
256
|
+
isinstance(prefix, SlicedHandleBinding)
|
257
|
+
and self.base_handle == prefix.base_handle
|
258
|
+
and self._is_evaluated()
|
259
|
+
and prefix._is_evaluated()
|
260
|
+
):
|
261
|
+
prefix_start = prefix.start.to_int_value()
|
262
|
+
prefix_end = prefix.end.to_int_value()
|
263
|
+
self_start = self.start.to_int_value()
|
264
|
+
self_end = self.end.to_int_value()
|
265
|
+
if (
|
266
|
+
prefix_start <= self_start < prefix_end
|
267
|
+
and prefix_start < self_end <= prefix_end
|
268
|
+
):
|
269
|
+
return SlicedHandleBinding(
|
270
|
+
base_handle=replacement,
|
271
|
+
start=Expression(expr=str(self_start - prefix_start)),
|
272
|
+
end=Expression(expr=str(self_end - prefix_start)),
|
273
|
+
)
|
274
|
+
return super().replace_prefix(prefix, replacement)
|
275
|
+
|
276
|
+
def _is_evaluated(self) -> bool:
|
277
|
+
return self.start.is_evaluated() and self.end.is_evaluated()
|
278
|
+
|
234
279
|
|
235
280
|
class FieldHandleBinding(NestedHandleBinding):
|
236
281
|
field: str
|
@@ -59,6 +59,10 @@ class QuantumType(HashableASTNode):
|
|
59
59
|
def type_name(self) -> str:
|
60
60
|
raise NotImplementedError
|
61
61
|
|
62
|
+
@property
|
63
|
+
def is_instantiated(self) -> bool:
|
64
|
+
raise NotImplementedError
|
65
|
+
|
62
66
|
|
63
67
|
class QuantumScalar(QuantumType):
|
64
68
|
def get_proxy(self, handle: "HandleBinding") -> QmodQScalarProxy:
|
@@ -88,6 +92,10 @@ class QuantumBit(QuantumScalar):
|
|
88
92
|
def type_name(self) -> str:
|
89
93
|
return "Quantum bit"
|
90
94
|
|
95
|
+
@property
|
96
|
+
def is_instantiated(self) -> bool:
|
97
|
+
return True
|
98
|
+
|
91
99
|
|
92
100
|
class QuantumBitvector(QuantumType):
|
93
101
|
element_type: "ConcreteQuantumType" = Field(
|
@@ -142,6 +150,10 @@ class QuantumBitvector(QuantumType):
|
|
142
150
|
def type_name(self) -> str:
|
143
151
|
return "Quantum array"
|
144
152
|
|
153
|
+
@property
|
154
|
+
def is_instantiated(self) -> bool:
|
155
|
+
return self.length is not None and self.element_type.is_instantiated
|
156
|
+
|
145
157
|
|
146
158
|
class QuantumNumeric(QuantumScalar):
|
147
159
|
kind: Literal["qnum"]
|
@@ -211,6 +223,10 @@ class QuantumNumeric(QuantumScalar):
|
|
211
223
|
def type_name(self) -> str:
|
212
224
|
return "Quantum numeric"
|
213
225
|
|
226
|
+
@property
|
227
|
+
def is_instantiated(self) -> bool:
|
228
|
+
return self.size is not None
|
229
|
+
|
214
230
|
|
215
231
|
class RegisterQuantumType(BaseModel):
|
216
232
|
quantum_types: "ConcreteQuantumType"
|
@@ -64,9 +64,7 @@ GENERATE_RESOURCE_ESTIMATOR_REPORT = "/resource_estimator_report"
|
|
64
64
|
TASKS_SOLVE_EXACT_SUFFIX = "/tasks/solve_exact"
|
65
65
|
|
66
66
|
GENERATE_HAMILTONIAN_SUFFIX = "/generate_hamiltonian"
|
67
|
-
GENERATE_HAMILTONIAN_FULL_PATH =
|
68
|
-
SYNTHESIS_NON_VERSIONED_PREFIX + GENERATE_HAMILTONIAN_SUFFIX
|
69
|
-
)
|
67
|
+
GENERATE_HAMILTONIAN_FULL_PATH = CHEMISTRY_PREFIX + GENERATE_HAMILTONIAN_SUFFIX
|
70
68
|
|
71
69
|
CONVERSION_GENERATED_CIRCUIT_TO_EXECUTION_INPUT_SUFFIX = "/execution_input"
|
72
70
|
CONVERSION_GENERATED_CIRCUIT_TO_EXECUTION_INPUT_FULL = (
|
@@ -1,6 +1,5 @@
|
|
1
1
|
import dataclasses
|
2
|
-
from collections.abc import
|
3
|
-
from contextlib import contextmanager
|
2
|
+
from collections.abc import Sequence
|
4
3
|
from dataclasses import dataclass, field
|
5
4
|
from typing import TYPE_CHECKING, Optional
|
6
5
|
|
@@ -9,13 +8,18 @@ from classiq.interface.exceptions import (
|
|
9
8
|
ClassiqExpansionError,
|
10
9
|
ClassiqInternalExpansionError,
|
11
10
|
)
|
11
|
+
from classiq.interface.generator.expressions.expression import Expression
|
12
12
|
from classiq.interface.generator.functions.port_declaration import (
|
13
13
|
PortDeclarationDirection,
|
14
14
|
)
|
15
|
-
from classiq.interface.model.handle_binding import
|
15
|
+
from classiq.interface.model.handle_binding import (
|
16
|
+
HandleBinding,
|
17
|
+
NestedHandleBinding,
|
18
|
+
SlicedHandleBinding,
|
19
|
+
)
|
16
20
|
from classiq.interface.model.port_declaration import PortDeclaration
|
17
21
|
from classiq.interface.model.quantum_function_call import ArgValue
|
18
|
-
from classiq.interface.model.quantum_type import QuantumType
|
22
|
+
from classiq.interface.model.quantum_type import QuantumBitvector, QuantumType
|
19
23
|
from classiq.interface.model.variable_declaration_statement import (
|
20
24
|
VariableDeclarationStatement,
|
21
25
|
)
|
@@ -105,6 +109,11 @@ class _CapturedHandle:
|
|
105
109
|
return dataclasses.replace(self, is_propagated=False)
|
106
110
|
return self
|
107
111
|
|
112
|
+
def set_symbol(
|
113
|
+
self, handle: HandleBinding, quantum_type: QuantumType
|
114
|
+
) -> "_CapturedHandle":
|
115
|
+
return dataclasses.replace(self, handle=handle, quantum_type=quantum_type)
|
116
|
+
|
108
117
|
|
109
118
|
@dataclass
|
110
119
|
class CapturedVars:
|
@@ -146,23 +155,10 @@ class CapturedVars:
|
|
146
155
|
captured_handle = self._conjugate_direction(
|
147
156
|
existing_captured_handle, captured_handle
|
148
157
|
)
|
149
|
-
elif captured_handle.handle
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
):
|
154
|
-
raise ClassiqInternalExpansionError(
|
155
|
-
"Captured handle is already freed"
|
156
|
-
)
|
157
|
-
captured_handle = existing_captured_handle
|
158
|
-
elif existing_captured_handle.handle in captured_handle.handle:
|
159
|
-
if captured_handle.direction in (
|
160
|
-
PortDirection.Output,
|
161
|
-
PortDirection.Outin,
|
162
|
-
):
|
163
|
-
raise ClassiqInternalExpansionError(
|
164
|
-
"Captured handle is already allocated"
|
165
|
-
)
|
158
|
+
elif captured_handle.handle.overlaps(existing_captured_handle.handle):
|
159
|
+
captured_handle = self._intersect_handles(
|
160
|
+
existing_captured_handle, captured_handle
|
161
|
+
)
|
166
162
|
else:
|
167
163
|
new_captured_handles.append(existing_captured_handle)
|
168
164
|
new_captured_handles.append(captured_handle)
|
@@ -205,6 +201,92 @@ class CapturedVars:
|
|
205
201
|
raise ClassiqInternalExpansionError("Captured handle is already freed")
|
206
202
|
return captured_handle
|
207
203
|
|
204
|
+
def _intersect_handles(
|
205
|
+
self,
|
206
|
+
existing_captured_handle: _CapturedHandle,
|
207
|
+
captured_handle: _CapturedHandle,
|
208
|
+
) -> _CapturedHandle:
|
209
|
+
if captured_handle.handle in existing_captured_handle.handle:
|
210
|
+
if existing_captured_handle.direction in (
|
211
|
+
PortDirection.Input,
|
212
|
+
PortDirection.Outin,
|
213
|
+
):
|
214
|
+
raise ClassiqInternalExpansionError("Captured handle is already freed")
|
215
|
+
return existing_captured_handle
|
216
|
+
|
217
|
+
if existing_captured_handle.handle in captured_handle.handle:
|
218
|
+
if captured_handle.direction in (
|
219
|
+
PortDirection.Output,
|
220
|
+
PortDirection.Outin,
|
221
|
+
):
|
222
|
+
raise ClassiqInternalExpansionError(
|
223
|
+
"Captured handle is already allocated"
|
224
|
+
)
|
225
|
+
return captured_handle
|
226
|
+
|
227
|
+
sliced_handle, quantum_type, other_handle = self._get_sliced_handle(
|
228
|
+
existing_captured_handle, captured_handle
|
229
|
+
)
|
230
|
+
if not isinstance(other_handle, SlicedHandleBinding):
|
231
|
+
return captured_handle.set_symbol(sliced_handle, quantum_type)
|
232
|
+
|
233
|
+
merged_handle, merged_quantum_type = self._merge_sliced_handles(
|
234
|
+
sliced_handle, other_handle, quantum_type
|
235
|
+
)
|
236
|
+
return captured_handle.set_symbol(merged_handle, merged_quantum_type)
|
237
|
+
|
238
|
+
@staticmethod
|
239
|
+
def _get_sliced_handle(
|
240
|
+
existing_captured_handle: _CapturedHandle,
|
241
|
+
captured_handle: _CapturedHandle,
|
242
|
+
) -> tuple[SlicedHandleBinding, QuantumBitvector, HandleBinding]:
|
243
|
+
handle_1 = existing_captured_handle.handle
|
244
|
+
quantum_type_1 = existing_captured_handle.quantum_type
|
245
|
+
handle_2 = captured_handle.handle
|
246
|
+
quantum_type_2 = captured_handle.quantum_type
|
247
|
+
if isinstance(handle_1, SlicedHandleBinding):
|
248
|
+
sliced_handle = handle_1
|
249
|
+
other_handle = handle_2
|
250
|
+
quantum_type = quantum_type_1
|
251
|
+
elif isinstance(handle_2, SlicedHandleBinding):
|
252
|
+
sliced_handle = handle_2
|
253
|
+
other_handle = handle_1
|
254
|
+
quantum_type = quantum_type_2
|
255
|
+
else:
|
256
|
+
raise ClassiqInternalExpansionError(
|
257
|
+
f"Unexpected overlapping handles {handle_1} and {handle_2}"
|
258
|
+
)
|
259
|
+
if not isinstance(quantum_type, QuantumBitvector):
|
260
|
+
raise ClassiqInternalExpansionError
|
261
|
+
return sliced_handle, quantum_type, other_handle
|
262
|
+
|
263
|
+
@staticmethod
|
264
|
+
def _merge_sliced_handles(
|
265
|
+
handle_1: SlicedHandleBinding,
|
266
|
+
handle_2: SlicedHandleBinding,
|
267
|
+
quantum_type: QuantumBitvector,
|
268
|
+
) -> tuple[HandleBinding, QuantumBitvector]:
|
269
|
+
if (
|
270
|
+
not handle_1.start.is_evaluated()
|
271
|
+
or not handle_1.end.is_evaluated()
|
272
|
+
or not handle_2.start.is_evaluated()
|
273
|
+
or not handle_2.end.is_evaluated()
|
274
|
+
):
|
275
|
+
raise ClassiqInternalExpansionError
|
276
|
+
|
277
|
+
new_start = min(handle_1.start.to_int_value(), handle_2.start.to_int_value())
|
278
|
+
new_end = max(handle_1.end.to_int_value(), handle_2.end.to_int_value())
|
279
|
+
merged_handle = SlicedHandleBinding(
|
280
|
+
base_handle=handle_1.base_handle,
|
281
|
+
start=Expression(expr=str(new_start)),
|
282
|
+
end=Expression(expr=str(new_end)),
|
283
|
+
)
|
284
|
+
merged_quantum_type = QuantumBitvector(
|
285
|
+
element_type=quantum_type.element_type,
|
286
|
+
length=Expression(expr=str(new_end - new_start)),
|
287
|
+
)
|
288
|
+
return merged_handle, merged_quantum_type
|
289
|
+
|
208
290
|
def update(self, other_captured_vars: "CapturedVars") -> None:
|
209
291
|
for captured_handle in other_captured_vars._captured_handles:
|
210
292
|
self._capture_handle(captured_handle)
|
@@ -277,11 +359,8 @@ class CapturedVars:
|
|
277
359
|
if not captured_handle.is_propagated
|
278
360
|
}
|
279
361
|
|
280
|
-
|
281
|
-
|
282
|
-
previous = self._captured_handles
|
283
|
-
yield
|
284
|
-
self._captured_handles = previous
|
362
|
+
def clone(self) -> "CapturedVars":
|
363
|
+
return CapturedVars(_captured_handles=list(self._captured_handles))
|
285
364
|
|
286
365
|
|
287
366
|
def _same_closure(closure_1: "FunctionClosure", closure_2: "FunctionClosure") -> bool:
|
@@ -311,7 +390,9 @@ def validate_args_are_not_propagated(
|
|
311
390
|
)
|
312
391
|
|
313
392
|
|
314
|
-
def validate_captured_directions(
|
393
|
+
def validate_captured_directions(
|
394
|
+
captured_vars: CapturedVars, report_outin: bool = True
|
395
|
+
) -> None:
|
315
396
|
captured_inputs = [
|
316
397
|
captured_handle.handle.name
|
317
398
|
for captured_handle in captured_vars._captured_handles
|
@@ -320,7 +401,12 @@ def validate_captured_directions(captured_vars: CapturedVars) -> None:
|
|
320
401
|
captured_outputs = [
|
321
402
|
captured_handle.handle.name
|
322
403
|
for captured_handle in captured_vars._captured_handles
|
323
|
-
if captured_handle.direction
|
404
|
+
if captured_handle.direction
|
405
|
+
in (
|
406
|
+
(PortDirection.Output, PortDirection.Outin)
|
407
|
+
if report_outin
|
408
|
+
else (PortDirection.Output,)
|
409
|
+
)
|
324
410
|
]
|
325
411
|
if len(captured_inputs) > 0:
|
326
412
|
raise ClassiqExpansionError(
|