classiq 0.62.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 +5 -1
- classiq/applications/combinatorial_helpers/pyomo_utils.py +106 -27
- classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +1 -1
- classiq/applications/combinatorial_optimization/combinatorial_problem.py +4 -2
- 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 +3 -9
- classiq/interface/_version.py +1 -1
- classiq/interface/backend/backend_preferences.py +8 -5
- classiq/interface/backend/pydantic_backend.py +1 -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/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 +13 -0
- classiq/interface/generator/quantum_function_call.py +4 -2
- 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 +102 -19
- classiq/model_expansions/closure.py +19 -56
- classiq/model_expansions/function_builder.py +13 -8
- classiq/model_expansions/generative_functions.py +15 -1
- classiq/model_expansions/interpreter.py +94 -32
- classiq/model_expansions/model_tables.py +4 -0
- classiq/model_expansions/quantum_operations/call_emitter.py +61 -2
- 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 +1 -1
- classiq/model_expansions/quantum_operations/quantum_assignment_operation.py +1 -2
- classiq/model_expansions/quantum_operations/repeat.py +4 -3
- classiq/model_expansions/scope.py +7 -1
- 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/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/qfunc.py +11 -12
- classiq/qmod/qmod_variable.py +1 -3
- classiq/qmod/quantum_expandable.py +21 -0
- classiq/qmod/quantum_function.py +65 -3
- {classiq-0.62.0.dist-info → classiq-0.63.0.dist-info}/METADATA +1 -1
- {classiq-0.62.0.dist-info → classiq-0.63.0.dist-info}/RECORD +74 -71
- 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.62.0.dist-info → classiq-0.63.0.dist-info}/WHEEL +0 -0
@@ -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"
|
@@ -157,6 +165,11 @@ class Preferences(pydantic.BaseModel, extra="forbid"):
|
|
157
165
|
synthesize_all_separately: bool = pydantic.Field(
|
158
166
|
default=False,
|
159
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 = (
|
@@ -8,13 +8,18 @@ from classiq.interface.exceptions import (
|
|
8
8
|
ClassiqExpansionError,
|
9
9
|
ClassiqInternalExpansionError,
|
10
10
|
)
|
11
|
+
from classiq.interface.generator.expressions.expression import Expression
|
11
12
|
from classiq.interface.generator.functions.port_declaration import (
|
12
13
|
PortDeclarationDirection,
|
13
14
|
)
|
14
|
-
from classiq.interface.model.handle_binding import
|
15
|
+
from classiq.interface.model.handle_binding import (
|
16
|
+
HandleBinding,
|
17
|
+
NestedHandleBinding,
|
18
|
+
SlicedHandleBinding,
|
19
|
+
)
|
15
20
|
from classiq.interface.model.port_declaration import PortDeclaration
|
16
21
|
from classiq.interface.model.quantum_function_call import ArgValue
|
17
|
-
from classiq.interface.model.quantum_type import QuantumType
|
22
|
+
from classiq.interface.model.quantum_type import QuantumBitvector, QuantumType
|
18
23
|
from classiq.interface.model.variable_declaration_statement import (
|
19
24
|
VariableDeclarationStatement,
|
20
25
|
)
|
@@ -104,6 +109,11 @@ class _CapturedHandle:
|
|
104
109
|
return dataclasses.replace(self, is_propagated=False)
|
105
110
|
return self
|
106
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
|
+
|
107
117
|
|
108
118
|
@dataclass
|
109
119
|
class CapturedVars:
|
@@ -145,23 +155,10 @@ class CapturedVars:
|
|
145
155
|
captured_handle = self._conjugate_direction(
|
146
156
|
existing_captured_handle, captured_handle
|
147
157
|
)
|
148
|
-
elif captured_handle.handle
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
):
|
153
|
-
raise ClassiqInternalExpansionError(
|
154
|
-
"Captured handle is already freed"
|
155
|
-
)
|
156
|
-
captured_handle = existing_captured_handle
|
157
|
-
elif existing_captured_handle.handle in captured_handle.handle:
|
158
|
-
if captured_handle.direction in (
|
159
|
-
PortDirection.Output,
|
160
|
-
PortDirection.Outin,
|
161
|
-
):
|
162
|
-
raise ClassiqInternalExpansionError(
|
163
|
-
"Captured handle is already allocated"
|
164
|
-
)
|
158
|
+
elif captured_handle.handle.overlaps(existing_captured_handle.handle):
|
159
|
+
captured_handle = self._intersect_handles(
|
160
|
+
existing_captured_handle, captured_handle
|
161
|
+
)
|
165
162
|
else:
|
166
163
|
new_captured_handles.append(existing_captured_handle)
|
167
164
|
new_captured_handles.append(captured_handle)
|
@@ -204,6 +201,92 @@ class CapturedVars:
|
|
204
201
|
raise ClassiqInternalExpansionError("Captured handle is already freed")
|
205
202
|
return captured_handle
|
206
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
|
+
|
207
290
|
def update(self, other_captured_vars: "CapturedVars") -> None:
|
208
291
|
for captured_handle in other_captured_vars._captured_handles:
|
209
292
|
self._capture_handle(captured_handle)
|
@@ -1,27 +1,21 @@
|
|
1
1
|
import dataclasses
|
2
2
|
import json
|
3
|
-
import uuid
|
4
3
|
from collections.abc import Collection, Sequence
|
5
4
|
from dataclasses import dataclass, field
|
6
5
|
from functools import singledispatch
|
7
|
-
from symtable import Symbol
|
8
6
|
from typing import Any, Optional
|
9
7
|
|
10
8
|
from typing_extensions import Self
|
11
9
|
|
12
10
|
from classiq.interface.exceptions import ClassiqInternalExpansionError
|
13
|
-
from classiq.interface.generator.functions.builtins.internal_operators import (
|
14
|
-
All_BUILTINS_OPERATORS,
|
15
|
-
)
|
16
11
|
from classiq.interface.model.port_declaration import PortDeclaration
|
17
12
|
from classiq.interface.model.quantum_function_declaration import (
|
18
13
|
NamedParamsQuantumFunctionDeclaration,
|
19
14
|
PositionalArg,
|
20
|
-
QuantumOperandDeclaration,
|
21
15
|
)
|
16
|
+
from classiq.interface.model.quantum_lambda_function import QuantumCallable
|
22
17
|
from classiq.interface.model.quantum_statement import QuantumStatement
|
23
18
|
|
24
|
-
from classiq import ClassicalParameterDeclaration
|
25
19
|
from classiq.model_expansions.capturing.captured_vars import CapturedVars
|
26
20
|
from classiq.model_expansions.expression_renamer import ExpressionRenamer
|
27
21
|
from classiq.model_expansions.scope import (
|
@@ -73,15 +67,7 @@ class FunctionClosure(Closure):
|
|
73
67
|
# The closure is changing across the interpreter flow so it's closure_id may change
|
74
68
|
@property
|
75
69
|
def closure_id(self) -> str:
|
76
|
-
|
77
|
-
# may create a bugs. Therefore, with each call to closure_id a new id is
|
78
|
-
# created
|
79
|
-
if self.is_lambda or self.name in All_BUILTINS_OPERATORS:
|
80
|
-
signature = str(uuid.uuid4())
|
81
|
-
else:
|
82
|
-
signature = _generate_closure_id(
|
83
|
-
self.positional_arg_declarations, self.scope.data.values()
|
84
|
-
)
|
70
|
+
signature = _generate_closure_id(self.scope.data.values())
|
85
71
|
return f"{self.name}__{signature}"
|
86
72
|
|
87
73
|
@property
|
@@ -144,65 +130,42 @@ class FunctionClosure(Closure):
|
|
144
130
|
captured_vars=self.captured_vars.clone(),
|
145
131
|
)
|
146
132
|
|
133
|
+
def emit(self) -> QuantumCallable:
|
134
|
+
return self.name
|
135
|
+
|
147
136
|
|
148
137
|
@dataclass(frozen=True)
|
149
138
|
class GenerativeFunctionClosure(GenerativeClosure, FunctionClosure):
|
150
139
|
pass
|
151
140
|
|
152
141
|
|
153
|
-
def _generate_closure_id(
|
154
|
-
|
155
|
-
)
|
156
|
-
|
157
|
-
for arg_declara, eval_arg in zip(args_declaration, evaluated_args):
|
158
|
-
args_signature |= _generate_arg_id(arg_declara, eval_arg)
|
142
|
+
def _generate_closure_id(evaluated_args: Collection[Evaluated]) -> str:
|
143
|
+
args_signature = [
|
144
|
+
_evaluated_arg_to_str(eval_arg.value) for eval_arg in evaluated_args
|
145
|
+
]
|
159
146
|
return json.dumps(args_signature)
|
160
147
|
|
161
148
|
|
162
|
-
|
163
|
-
arg_declaration: PositionalArg, evaluated_arg: Evaluated
|
164
|
-
) -> dict[str, str]:
|
165
|
-
arg_value = evaluated_arg.value
|
166
|
-
arg_name = arg_declaration.name
|
167
|
-
if isinstance(arg_declaration, ClassicalParameterDeclaration):
|
168
|
-
return {arg_name: evaluated_classical_param_to_str(arg_value)}
|
169
|
-
if isinstance(arg_declaration, PortDeclaration):
|
170
|
-
return {arg_name: _evaluated_port_to_str(arg_value)}
|
171
|
-
if isinstance(arg_declaration, QuantumOperandDeclaration):
|
172
|
-
return {arg_name: _evaluated_operand_to_str(arg_value)}
|
173
|
-
|
174
|
-
|
149
|
+
@singledispatch
|
175
150
|
def _evaluated_arg_to_str(arg: Any) -> str:
|
176
151
|
if isinstance(arg, str):
|
177
152
|
return arg
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
153
|
+
if isinstance(arg, QuantumSymbol):
|
154
|
+
return _evaluated_quantum_symbol_to_str(arg)
|
155
|
+
if isinstance(arg, FunctionClosure):
|
156
|
+
return _evaluated_one_operand_to_str(arg)
|
157
|
+
if isinstance(arg, list) and arg and isinstance(arg[0], FunctionClosure):
|
158
|
+
return _evaluated_operands_list_to_str(arg)
|
159
|
+
return evaluated_classical_param_to_str(arg)
|
184
160
|
|
185
161
|
|
186
|
-
@_evaluated_port_to_str.register
|
187
162
|
def _evaluated_quantum_symbol_to_str(port: QuantumSymbol) -> str:
|
188
163
|
return port.quantum_type.model_dump_json(exclude_none=True, exclude={"name"})
|
189
164
|
|
190
165
|
|
191
|
-
@_evaluated_port_to_str.register
|
192
|
-
def _evaluated_symbol_to_str(port: Symbol) -> str:
|
193
|
-
return repr(port)
|
194
|
-
|
195
|
-
|
196
|
-
@singledispatch
|
197
|
-
def _evaluated_operand_to_str(arg: Any) -> str:
|
198
|
-
return _evaluated_arg_to_str(arg)
|
199
|
-
|
200
|
-
|
201
|
-
@_evaluated_operand_to_str.register
|
202
166
|
def _evaluated_one_operand_to_str(operand: FunctionClosure) -> str:
|
203
167
|
return operand.closure_id
|
204
168
|
|
205
169
|
|
206
|
-
|
207
|
-
|
208
|
-
return json.dumps([_evaluated_operand_to_str(ope) for ope in operands])
|
170
|
+
def _evaluated_operands_list_to_str(arg: list[FunctionClosure]) -> str:
|
171
|
+
return json.dumps([_evaluated_one_operand_to_str(ope) for ope in arg])
|
@@ -31,6 +31,7 @@ from classiq.model_expansions.capturing.captured_vars import (
|
|
31
31
|
)
|
32
32
|
from classiq.model_expansions.closure import Closure, FunctionClosure
|
33
33
|
from classiq.model_expansions.scope import Scope
|
34
|
+
from classiq.model_expansions.utils.counted_name_allocator import CountedNameAllocator
|
34
35
|
|
35
36
|
ClosureType = TypeVar("ClosureType", bound=Closure)
|
36
37
|
|
@@ -82,11 +83,14 @@ class FunctionContext(OperationContext[FunctionClosure]):
|
|
82
83
|
|
83
84
|
|
84
85
|
class OperationBuilder:
|
85
|
-
def __init__(
|
86
|
+
def __init__(
|
87
|
+
self, functions_scope: Scope, counted_name_allocator: CountedNameAllocator
|
88
|
+
) -> None:
|
86
89
|
self._operations: list[OperationContext] = []
|
87
90
|
self._blocks: list[str] = []
|
88
91
|
self._functions_scope = functions_scope
|
89
92
|
self._current_source_ref: Optional[SourceReference] = None
|
93
|
+
self._counted_name_allocator = counted_name_allocator
|
90
94
|
|
91
95
|
@property
|
92
96
|
def current_operation(self) -> Closure:
|
@@ -198,13 +202,14 @@ class OperationBuilder:
|
|
198
202
|
) -> NativeFunctionDefinition:
|
199
203
|
name = function_context.name
|
200
204
|
if name != MAIN_FUNCTION_NAME:
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
205
|
+
for _ in self.current_scope:
|
206
|
+
name = self._counted_name_allocator.allocate(
|
207
|
+
f"{name}_{LAMBDA_KEYWORD + '_0_0_' if function_context.is_lambda else ''}{EXPANDED_KEYWORD}"
|
208
|
+
)
|
209
|
+
if name not in self.current_scope:
|
210
|
+
break
|
211
|
+
else:
|
212
|
+
raise ClassiqInternalExpansionError("Could not allocate function name")
|
208
213
|
new_parameters: list[PortDeclaration] = [
|
209
214
|
param
|
210
215
|
for param in function_context.positional_arg_declarations
|
@@ -105,7 +105,21 @@ class _InterpreterExpandable(QFunc):
|
|
105
105
|
positional_arg_declarations=current_operation.positional_arg_declarations,
|
106
106
|
body=self._interpreter._builder._current_statements + [stmt],
|
107
107
|
)
|
108
|
-
|
108
|
+
declarative_functions = {
|
109
|
+
name: func
|
110
|
+
for name, func in self._qmodule.native_defs.items()
|
111
|
+
if name not in self._interpreter._top_level_scope
|
112
|
+
}
|
113
|
+
self._interpreter.update_declarative_functions(
|
114
|
+
declarative_functions, self._qmodule
|
115
|
+
)
|
116
|
+
self._interpreter.update_generative_functions(
|
117
|
+
self._qmodule.generative_functions
|
118
|
+
)
|
119
|
+
func_decls = self._get_function_declarations()
|
120
|
+
for dec_func in declarative_functions.values():
|
121
|
+
resolve_function_calls(dec_func, func_decls)
|
122
|
+
resolve_function_calls(dummy_function, func_decls)
|
109
123
|
stmt = dummy_function.body[-1]
|
110
124
|
with generative_mode_context(False):
|
111
125
|
self._interpreter.emit_statement(stmt)
|