classiq 0.73.0__py3-none-any.whl → 0.75.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/client.py +9 -10
- classiq/analyzer/show_interactive_hack.py +1 -1
- classiq/applications/qnn/qlayer.py +9 -0
- classiq/interface/_version.py +1 -1
- classiq/interface/ast_node.py +5 -2
- classiq/interface/compression_utils.py +31 -0
- classiq/interface/debug_info/debug_info.py +2 -11
- classiq/interface/generator/expressions/proxies/classical/classical_array_proxy.py +5 -5
- classiq/interface/generator/expressions/proxies/classical/utils.py +2 -2
- classiq/interface/generator/functions/classical_type.py +30 -0
- classiq/interface/generator/functions/type_name.py +25 -3
- classiq/interface/generator/generated_circuit_data.py +11 -25
- classiq/interface/generator/quantum_program.py +14 -0
- classiq/interface/generator/synthesis_metadata/synthesis_execution_data.py +10 -13
- classiq/interface/helpers/versioned_model.py +12 -0
- classiq/interface/ide/visual_model.py +4 -2
- classiq/interface/interface_version.py +1 -1
- classiq/interface/model/handle_binding.py +12 -0
- classiq/interface/model/quantum_lambda_function.py +14 -0
- classiq/interface/model/statement_block.py +9 -1
- classiq/interface/model/within_apply_operation.py +12 -0
- classiq/model_expansions/atomic_expression_functions_defs.py +24 -8
- classiq/model_expansions/capturing/captured_vars.py +28 -6
- classiq/model_expansions/closure.py +13 -0
- classiq/model_expansions/evaluators/argument_types.py +6 -5
- classiq/model_expansions/evaluators/type_type_match.py +2 -1
- classiq/model_expansions/generative_functions.py +14 -8
- classiq/model_expansions/interpreters/base_interpreter.py +10 -13
- classiq/model_expansions/interpreters/frontend_generative_interpreter.py +21 -0
- classiq/model_expansions/interpreters/generative_interpreter.py +13 -5
- classiq/model_expansions/quantum_operations/allocate.py +22 -11
- classiq/model_expansions/quantum_operations/assignment_result_processor.py +2 -0
- classiq/model_expansions/quantum_operations/call_emitter.py +5 -10
- classiq/model_expansions/quantum_operations/emitter.py +1 -5
- classiq/model_expansions/quantum_operations/expression_evaluator.py +1 -0
- classiq/model_expansions/quantum_operations/handle_evaluator.py +1 -0
- classiq/model_expansions/transformers/model_renamer.py +3 -1
- classiq/model_expansions/visitors/symbolic_param_inference.py +197 -0
- classiq/open_library/functions/__init__.py +2 -0
- classiq/open_library/functions/amplitude_amplification.py +5 -3
- classiq/open_library/functions/state_preparation.py +95 -2
- classiq/qmod/model_state_container.py +11 -8
- classiq/qmod/qmod_variable.py +23 -1
- classiq/qmod/quantum_function.py +1 -9
- classiq/qmod/symbolic_expr.py +8 -2
- classiq/qmod/write_qmod.py +5 -1
- {classiq-0.73.0.dist-info → classiq-0.75.0.dist-info}/METADATA +2 -1
- {classiq-0.73.0.dist-info → classiq-0.75.0.dist-info}/RECORD +49 -47
- {classiq-0.73.0.dist-info → classiq-0.75.0.dist-info}/WHEEL +1 -1
classiq/_internals/client.py
CHANGED
@@ -75,25 +75,23 @@ def _get_user_agent_header() -> Headers:
|
|
75
75
|
python_version = (
|
76
76
|
f"python({_get_python_execution_environment()})/{platform.python_version()}"
|
77
77
|
)
|
78
|
-
|
79
|
-
sdk_env_value = os.getenv("SDK_ENV", "Default")
|
80
78
|
os_platform = f"{os.name}/{platform.platform()}"
|
81
79
|
frontend_version = f"{_FRONTEND_VARIANT}/{_VERSION}"
|
82
80
|
interface_version = f"{_INTERFACE_VARIANT}/{_VERSION}"
|
83
|
-
|
81
|
+
|
84
82
|
return {
|
85
83
|
"User-Agent": _USERAGENT_SEPARATOR.join(
|
86
|
-
(
|
87
|
-
python_version,
|
88
|
-
os_platform,
|
89
|
-
frontend_version,
|
90
|
-
interface_version,
|
91
|
-
sdk_env,
|
92
|
-
)
|
84
|
+
(python_version, os_platform, frontend_version, interface_version)
|
93
85
|
)
|
94
86
|
}
|
95
87
|
|
96
88
|
|
89
|
+
@functools.lru_cache
|
90
|
+
def _get_sdk_env_header() -> Headers:
|
91
|
+
sdk_env_value = os.getenv("SDK_ENV", "Default")
|
92
|
+
return {_SDK_ENV: sdk_env_value}
|
93
|
+
|
94
|
+
|
97
95
|
Ret = TypeVar("Ret")
|
98
96
|
P = ParamSpec("P")
|
99
97
|
|
@@ -327,6 +325,7 @@ class Client:
|
|
327
325
|
if self._session_id is not None:
|
328
326
|
headers[self._SESSION_HEADER] = self._session_id
|
329
327
|
headers.update(_get_user_agent_header())
|
328
|
+
headers.update(_get_sdk_env_header())
|
330
329
|
return headers
|
331
330
|
|
332
331
|
async def update_expired_access_token(self) -> None:
|
@@ -1,5 +1,6 @@
|
|
1
1
|
import functools
|
2
2
|
import inspect
|
3
|
+
import os
|
3
4
|
import typing
|
4
5
|
from typing import Any, Callable, Optional, Union, overload
|
5
6
|
|
@@ -247,6 +248,14 @@ class QLayer(nn.Module):
|
|
247
248
|
def _make_execute(
|
248
249
|
self, quantum_program: SerializedQuantumProgram
|
249
250
|
) -> ExecuteFunction:
|
251
|
+
if os.environ.get("SDK_ENV") == "Studio":
|
252
|
+
try:
|
253
|
+
import classiq_studio_simulation
|
254
|
+
|
255
|
+
return classiq_studio_simulation.make_execute_qnn(quantum_program)
|
256
|
+
except ImportError:
|
257
|
+
pass
|
258
|
+
|
250
259
|
session = ExecutionSession(quantum_program)
|
251
260
|
self._session = session
|
252
261
|
|
classiq/interface/_version.py
CHANGED
classiq/interface/ast_node.py
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
from typing import Optional, TypeVar
|
1
|
+
from typing import Any, Optional, TypeVar
|
2
2
|
from uuid import UUID
|
3
3
|
|
4
4
|
import pydantic
|
@@ -23,7 +23,10 @@ class ASTNode(HashablePydanticBaseModel):
|
|
23
23
|
def reset_lists(
|
24
24
|
ast_node: ASTNodeType, statement_block_fields: list[str]
|
25
25
|
) -> ASTNodeType:
|
26
|
-
|
26
|
+
update: dict[str, Any] = {field: [] for field in statement_block_fields}
|
27
|
+
if hasattr(ast_node, "uuid"):
|
28
|
+
update["uuid"] = ast_node.uuid
|
29
|
+
return ast_node.model_copy(update=update)
|
27
30
|
|
28
31
|
|
29
32
|
class HashableASTNode(ASTNode, HashablePydanticBaseModel):
|
@@ -0,0 +1,31 @@
|
|
1
|
+
import base64
|
2
|
+
from collections.abc import Sequence
|
3
|
+
from typing import Union
|
4
|
+
|
5
|
+
import zstandard as zstd
|
6
|
+
from pydantic import BaseModel
|
7
|
+
from pydantic_core import from_json, to_json
|
8
|
+
|
9
|
+
|
10
|
+
def compress_pydantic(obj: Union[BaseModel, Sequence[BaseModel]]) -> bytes:
|
11
|
+
json_data = to_json(obj)
|
12
|
+
compressed_data = _compress(json_data, level=22) # max compression level
|
13
|
+
return compressed_data
|
14
|
+
|
15
|
+
|
16
|
+
def decompress(compressed_data: bytes) -> Union[dict, list[dict]]:
|
17
|
+
decompressed_data = _decompress(compressed_data)
|
18
|
+
return from_json(decompressed_data)
|
19
|
+
|
20
|
+
|
21
|
+
def _compress(data: bytes, level: int) -> bytes:
|
22
|
+
compressor = zstd.ZstdCompressor(level=level)
|
23
|
+
compressed_data = compressor.compress(data)
|
24
|
+
b64_compressed_data = base64.b64encode(compressed_data)
|
25
|
+
return b64_compressed_data
|
26
|
+
|
27
|
+
|
28
|
+
def _decompress(data: bytes) -> bytes:
|
29
|
+
compressed_data = base64.b64decode(data)
|
30
|
+
decompressor = zstd.ZstdDecompressor()
|
31
|
+
return decompressor.decompress(compressed_data)
|
@@ -7,7 +7,6 @@ from pydantic import BaseModel, Field
|
|
7
7
|
from classiq.interface.debug_info import back_ref_util
|
8
8
|
from classiq.interface.generator.generated_circuit_data import (
|
9
9
|
FunctionDebugInfoInterface,
|
10
|
-
OperationLevel,
|
11
10
|
StatementType,
|
12
11
|
)
|
13
12
|
from classiq.interface.model.block import Block
|
@@ -18,21 +17,15 @@ ParameterValue = Union[float, int, str, None]
|
|
18
17
|
|
19
18
|
class FunctionDebugInfo(BaseModel):
|
20
19
|
name: str
|
21
|
-
level: OperationLevel = Field(default=OperationLevel.UNKNOWN)
|
22
20
|
statement_type: Union[StatementType, None] = None
|
23
|
-
is_allocate_or_free: bool = Field(default=False)
|
24
21
|
is_inverse: bool = Field(default=False)
|
25
22
|
release_by_inverse: bool = Field(default=False)
|
26
23
|
port_to_passed_variable_map: dict[str, str] = Field(default_factory=dict)
|
27
24
|
node: Optional[ConcreteQuantumStatement] = None
|
28
25
|
|
29
26
|
@property
|
30
|
-
def
|
31
|
-
return (
|
32
|
-
back_ref_util.is_allocate_or_free(self.node)
|
33
|
-
if self.node is not None
|
34
|
-
else self.is_allocate_or_free
|
35
|
-
)
|
27
|
+
def is_allocate_or_free(self) -> bool:
|
28
|
+
return back_ref_util.is_allocate_or_free(self.node) if self.node else False
|
36
29
|
|
37
30
|
def update_map_from_port_mapping(self, port_mapping: Mapping[str, str]) -> None:
|
38
31
|
new_port_to_passed_variable_map = self.port_to_passed_variable_map.copy()
|
@@ -103,7 +96,5 @@ def new_function_debug_info_by_node(
|
|
103
96
|
) -> FunctionDebugInfo:
|
104
97
|
return FunctionDebugInfo(
|
105
98
|
name="",
|
106
|
-
parameters=dict(),
|
107
|
-
level=OperationLevel.QMOD_STATEMENT,
|
108
99
|
node=node._as_back_ref(),
|
109
100
|
)
|
@@ -3,7 +3,7 @@ from typing import TYPE_CHECKING, Union
|
|
3
3
|
|
4
4
|
from sympy import Integer
|
5
5
|
|
6
|
-
from classiq.interface.exceptions import
|
6
|
+
from classiq.interface.exceptions import ClassiqIndexError
|
7
7
|
from classiq.interface.generator.expressions.expression import Expression
|
8
8
|
from classiq.interface.generator.expressions.non_symbolic_expr import NonSymbolicExpr
|
9
9
|
from classiq.interface.generator.expressions.proxies.classical.classical_proxy import (
|
@@ -51,9 +51,9 @@ class ClassicalArrayProxy(NonSymbolicExpr, ClassicalProxy):
|
|
51
51
|
start = int(slice_.start)
|
52
52
|
stop = int(slice_.stop)
|
53
53
|
if start >= stop:
|
54
|
-
raise
|
54
|
+
raise ClassiqIndexError("Array slice has non-positive length")
|
55
55
|
if start < 0 or stop > self._length:
|
56
|
-
raise
|
56
|
+
raise ClassiqIndexError("Array slice is out of bounds")
|
57
57
|
return ClassicalArrayProxy(
|
58
58
|
SlicedHandleBinding(
|
59
59
|
base_handle=self.handle,
|
@@ -67,11 +67,11 @@ class ClassicalArrayProxy(NonSymbolicExpr, ClassicalProxy):
|
|
67
67
|
def _get_subscript(self, index_: Union[int, Integer]) -> ClassicalProxy:
|
68
68
|
index = int(index_)
|
69
69
|
if index < 0:
|
70
|
-
raise
|
70
|
+
raise ClassiqIndexError(
|
71
71
|
"Array index is out of bounds (negative indices are not supported)"
|
72
72
|
)
|
73
73
|
if index >= self._length:
|
74
|
-
raise
|
74
|
+
raise ClassiqIndexError("Array index is out of bounds")
|
75
75
|
return self._element_type.get_classical_proxy(
|
76
76
|
SubscriptHandleBinding(
|
77
77
|
base_handle=self._handle, index=Expression(expr=str(index_))
|
@@ -15,7 +15,7 @@ from classiq.interface.generator.functions.classical_type import (
|
|
15
15
|
ClassicalArray,
|
16
16
|
ClassicalType,
|
17
17
|
)
|
18
|
-
from classiq.interface.generator.functions.type_name import
|
18
|
+
from classiq.interface.generator.functions.type_name import Struct
|
19
19
|
|
20
20
|
|
21
21
|
def get_proxy_type(proxy: ClassicalProxy) -> ClassicalType:
|
@@ -26,7 +26,7 @@ def get_proxy_type(proxy: ClassicalProxy) -> ClassicalType:
|
|
26
26
|
element_type=proxy._element_type, size=proxy.length
|
27
27
|
)
|
28
28
|
elif isinstance(proxy, ClassicalStructProxy):
|
29
|
-
classical_type =
|
29
|
+
classical_type = Struct(name=proxy._decl.name)
|
30
30
|
else:
|
31
31
|
raise ClassiqInternalExpansionError(
|
32
32
|
f"Unrecognized classical proxy {type(proxy).__name__}"
|
@@ -5,6 +5,7 @@ from pydantic import ConfigDict, PrivateAttr
|
|
5
5
|
from typing_extensions import Self
|
6
6
|
|
7
7
|
from classiq.interface.ast_node import HashableASTNode
|
8
|
+
from classiq.interface.generator.expressions.expression import Expression
|
8
9
|
from classiq.interface.generator.expressions.proxies.classical.classical_array_proxy import (
|
9
10
|
ClassicalArrayProxy,
|
10
11
|
)
|
@@ -39,9 +40,22 @@ class ClassicalType(HashableASTNode):
|
|
39
40
|
def is_generative(self) -> bool:
|
40
41
|
return self._is_generative
|
41
42
|
|
43
|
+
@property
|
44
|
+
def is_purely_declarative(self) -> bool:
|
45
|
+
return not self._is_generative
|
46
|
+
|
42
47
|
def get_classical_proxy(self, handle: HandleBinding) -> ClassicalProxy:
|
43
48
|
return ClassicalScalarProxy(handle, self)
|
44
49
|
|
50
|
+
@property
|
51
|
+
def expressions(self) -> list[Expression]:
|
52
|
+
return []
|
53
|
+
|
54
|
+
def clear_flags(self) -> Self:
|
55
|
+
res = self.model_copy()
|
56
|
+
res._is_generative = False
|
57
|
+
return res
|
58
|
+
|
45
59
|
|
46
60
|
class Integer(ClassicalType):
|
47
61
|
kind: Literal["int"]
|
@@ -82,6 +96,14 @@ class ClassicalList(ClassicalType):
|
|
82
96
|
def get_classical_proxy(self, handle: HandleBinding) -> ClassicalProxy:
|
83
97
|
raise NotImplementedError
|
84
98
|
|
99
|
+
@property
|
100
|
+
def expressions(self) -> list[Expression]:
|
101
|
+
return self.element_type.expressions
|
102
|
+
|
103
|
+
@property
|
104
|
+
def is_purely_declarative(self) -> bool:
|
105
|
+
return super().is_purely_declarative and self.element_type.is_purely_declarative
|
106
|
+
|
85
107
|
|
86
108
|
class StructMetaType(ClassicalType):
|
87
109
|
kind: Literal["type_proxy"]
|
@@ -108,6 +130,14 @@ class ClassicalArray(ClassicalType):
|
|
108
130
|
def get_classical_proxy(self, handle: HandleBinding) -> ClassicalProxy:
|
109
131
|
return ClassicalArrayProxy(handle, self.element_type, self.size)
|
110
132
|
|
133
|
+
@property
|
134
|
+
def expressions(self) -> list[Expression]:
|
135
|
+
return self.element_type.expressions
|
136
|
+
|
137
|
+
@property
|
138
|
+
def is_purely_declarative(self) -> bool:
|
139
|
+
return super().is_purely_declarative and self.element_type.is_purely_declarative
|
140
|
+
|
111
141
|
|
112
142
|
class OpaqueHandle(ClassicalType):
|
113
143
|
pass
|
@@ -115,12 +115,34 @@ class TypeName(ClassicalType, QuantumType):
|
|
115
115
|
|
116
116
|
@property
|
117
117
|
def expressions(self) -> list[Expression]:
|
118
|
-
|
119
|
-
|
120
|
-
|
118
|
+
if self.has_fields:
|
119
|
+
return list(
|
120
|
+
chain.from_iterable(
|
121
|
+
field_type.expressions for field_type in self.fields.values()
|
122
|
+
)
|
121
123
|
)
|
124
|
+
if self.has_classical_struct_decl:
|
125
|
+
return list(
|
126
|
+
chain.from_iterable(
|
127
|
+
field_type.expressions
|
128
|
+
for field_type in self.classical_struct_decl.variables.values()
|
129
|
+
)
|
130
|
+
)
|
131
|
+
return []
|
132
|
+
|
133
|
+
@property
|
134
|
+
def is_purely_declarative(self) -> bool:
|
135
|
+
if self.is_enum:
|
136
|
+
return False
|
137
|
+
return super().is_purely_declarative and all(
|
138
|
+
field_type.is_purely_declarative
|
139
|
+
for field_type in self.classical_struct_decl.variables.values()
|
122
140
|
)
|
123
141
|
|
142
|
+
@property
|
143
|
+
def is_enum(self) -> bool:
|
144
|
+
return not self.has_fields and not self.has_classical_struct_decl
|
145
|
+
|
124
146
|
|
125
147
|
class Enum(TypeName):
|
126
148
|
pass
|
@@ -40,6 +40,7 @@ IOQubitMapping: TypeAlias = dict[str, tuple[int, ...]]
|
|
40
40
|
CLASSIQ_HIERARCHY_SEPARATOR: Literal["__"] = "__"
|
41
41
|
QASM_SEPARATOR = "_"
|
42
42
|
SPLIT_MARKER: str = "part"
|
43
|
+
ARITH_ENGINE_PREFIX = "arith_eng__"
|
43
44
|
PART_SUFFIX_REGEX = re.compile(
|
44
45
|
rf".+{QASM_SEPARATOR}{SPLIT_MARKER}{QASM_SEPARATOR}(\d+)$"
|
45
46
|
)
|
@@ -177,10 +178,8 @@ class FunctionDebugInfoInterface(pydantic.BaseModel):
|
|
177
178
|
absolute_qubits: Optional[tuple[int, ...]] = Field(default=None)
|
178
179
|
is_basis_gate: Optional[bool] = Field(default=None)
|
179
180
|
is_inverse: bool = Field(default=False)
|
180
|
-
is_allocate_or_free: bool = Field(default=False)
|
181
181
|
is_unitary: bool = Field(default=True, exclude=True)
|
182
182
|
uuid: Optional[UUID] = Field(default=None, exclude=True)
|
183
|
-
level: OperationLevel = Field(default=OperationLevel.UNKNOWN)
|
184
183
|
port_to_passed_variable_map: dict[str, str] = Field(default={})
|
185
184
|
release_by_inverse: bool = Field(default=False)
|
186
185
|
back_refs: StatementBlock = Field(default_factory=list)
|
@@ -190,16 +189,8 @@ class FunctionDebugInfoInterface(pydantic.BaseModel):
|
|
190
189
|
override_debug_info: Optional["FunctionDebugInfoInterface"] = None
|
191
190
|
|
192
191
|
@property
|
193
|
-
def
|
194
|
-
|
195
|
-
temporary measure to handle the fact that in the current release we do not have
|
196
|
-
the backref, and therefore fallback to the old field of is_allocate_or_free
|
197
|
-
"""
|
198
|
-
return (
|
199
|
-
is_allocate_or_free_by_backref(self.back_refs)
|
200
|
-
if bool(self.back_refs)
|
201
|
-
else self.is_allocate_or_free
|
202
|
-
)
|
192
|
+
def is_allocate_or_free(self) -> bool:
|
193
|
+
return is_allocate_or_free_by_backref(self.back_refs)
|
203
194
|
|
204
195
|
@property
|
205
196
|
def name(self) -> str:
|
@@ -215,10 +206,8 @@ class FunctionDebugInfoInterface(pydantic.BaseModel):
|
|
215
206
|
if isinstance(back_ref, QuantumFunctionCall):
|
216
207
|
name = generate_original_function_name(back_ref.func_name)
|
217
208
|
if part_match := PART_SUFFIX_REGEX.match(generated_name):
|
218
|
-
|
219
|
-
|
220
|
-
suffix = ""
|
221
|
-
return f"{name}{suffix}"
|
209
|
+
name += f" [{part_match.group(1)}]"
|
210
|
+
return name.removeprefix(ARITH_ENGINE_PREFIX)
|
222
211
|
|
223
212
|
statement_kind: str = back_ref.kind
|
224
213
|
if isinstance(back_ref, ArithmeticOperation):
|
@@ -230,19 +219,16 @@ class FunctionDebugInfoInterface(pydantic.BaseModel):
|
|
230
219
|
return self.back_refs[0] if self.back_refs else None
|
231
220
|
|
232
221
|
@property
|
233
|
-
def
|
222
|
+
def level(self) -> OperationLevel:
|
234
223
|
# Temp fix for currently "supported" statements
|
235
224
|
if self.name in {StatementType.CONTROL, StatementType.POWER}:
|
236
225
|
return OperationLevel.QMOD_STATEMENT
|
237
226
|
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
# return OperationLevel.ENGINE_FUNCTION_CALL
|
244
|
-
return self.level
|
245
|
-
if isinstance(back_ref, QuantumFunctionCall):
|
227
|
+
if self.first_back_ref is None:
|
228
|
+
# we use ENGINE_FUNCTION_CALL in case where there's not back ref
|
229
|
+
return OperationLevel.ENGINE_FUNCTION_CALL
|
230
|
+
|
231
|
+
if isinstance(self.first_back_ref, QuantumFunctionCall):
|
246
232
|
return OperationLevel.QMOD_FUNCTION_CALL
|
247
233
|
return OperationLevel.QMOD_STATEMENT
|
248
234
|
|
@@ -8,6 +8,7 @@ from pydantic import model_validator
|
|
8
8
|
from pydantic_core.core_schema import ValidationInfo
|
9
9
|
from typing_extensions import TypeAlias
|
10
10
|
|
11
|
+
from classiq.interface.compression_utils import decompress
|
11
12
|
from classiq.interface.exceptions import (
|
12
13
|
ClassiqMissingOutputFormatError,
|
13
14
|
ClassiqStateInitializationError,
|
@@ -72,6 +73,7 @@ class QuantumProgram(VersionedModel, CircuitCodeInterface):
|
|
72
73
|
debug_info: Optional[list[FunctionDebugInfoInterface]] = pydantic.Field(
|
73
74
|
default=None
|
74
75
|
)
|
76
|
+
compressed_debug_info: Optional[bytes] = pydantic.Field(default=None)
|
75
77
|
program_id: str = pydantic.Field(default_factory=get_uuid_as_str)
|
76
78
|
execution_primitives_input: Optional[PrimitivesInput] = pydantic.Field(default=None)
|
77
79
|
|
@@ -198,3 +200,15 @@ class QuantumProgram(VersionedModel, CircuitCodeInterface):
|
|
198
200
|
if self.transpiled_circuit and self._can_use_transpiled_code
|
199
201
|
else self
|
200
202
|
)
|
203
|
+
|
204
|
+
def get_debug_info(self) -> Optional[list[FunctionDebugInfoInterface]]:
|
205
|
+
# Support legacy uncompressed debug info
|
206
|
+
if self.debug_info is not None:
|
207
|
+
return self.debug_info
|
208
|
+
if self.compressed_debug_info is None:
|
209
|
+
return None
|
210
|
+
decompressed_debug_info_dict_list = decompress(self.compressed_debug_info)
|
211
|
+
return [
|
212
|
+
FunctionDebugInfoInterface.model_validate(debug_info_dict)
|
213
|
+
for debug_info_dict in decompressed_debug_info_dict_list
|
214
|
+
]
|
@@ -1,10 +1,10 @@
|
|
1
|
+
from itertools import chain
|
1
2
|
from typing import Optional
|
2
3
|
|
3
4
|
import pydantic
|
4
5
|
import sympy
|
5
6
|
|
6
7
|
from classiq.interface.backend.pydantic_backend import PydanticExecutionParameter
|
7
|
-
from classiq.interface.exceptions import ClassiqValueError
|
8
8
|
from classiq.interface.generator.parameters import ParameterType
|
9
9
|
|
10
10
|
|
@@ -12,15 +12,10 @@ class FunctionExecutionData(pydantic.BaseModel):
|
|
12
12
|
power_parameter: Optional[ParameterType] = pydantic.Field(default=None)
|
13
13
|
|
14
14
|
@property
|
15
|
-
def
|
15
|
+
def power_vars(self) -> Optional[list[str]]:
|
16
16
|
if self.power_parameter is None:
|
17
17
|
return None
|
18
|
-
|
19
|
-
if len(power_vars) != 1:
|
20
|
-
raise ClassiqValueError(
|
21
|
-
f"Power parameter expression: {self.power_parameter} must contain exactly one variable"
|
22
|
-
)
|
23
|
-
return str(list(power_vars)[0])
|
18
|
+
return list(map(str, sympy.sympify(self.power_parameter).free_symbols))
|
24
19
|
|
25
20
|
|
26
21
|
class ExecutionData(pydantic.BaseModel):
|
@@ -32,8 +27,10 @@ class ExecutionData(pydantic.BaseModel):
|
|
32
27
|
def execution_parameters(
|
33
28
|
self,
|
34
29
|
) -> set[PydanticExecutionParameter]:
|
35
|
-
return
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
30
|
+
return set(
|
31
|
+
chain.from_iterable(
|
32
|
+
function_execution_data.power_vars
|
33
|
+
for function_execution_data in self.function_execution.values()
|
34
|
+
if function_execution_data.power_vars is not None
|
35
|
+
)
|
36
|
+
)
|
@@ -1,7 +1,19 @@
|
|
1
|
+
from typing import Any
|
2
|
+
|
1
3
|
import pydantic
|
2
4
|
|
3
5
|
from classiq.interface._version import VERSION
|
6
|
+
from classiq.interface.interface_version import INTERFACE_VERSION
|
4
7
|
|
5
8
|
|
6
9
|
class VersionedModel(pydantic.BaseModel):
|
7
10
|
version: str = pydantic.Field(default=VERSION)
|
11
|
+
interface_version: str = pydantic.Field(default="0")
|
12
|
+
|
13
|
+
@pydantic.model_validator(mode="before")
|
14
|
+
@classmethod
|
15
|
+
def set_interface_version(cls, values: dict[str, Any]) -> dict[str, Any]:
|
16
|
+
# We "override" the default value mechanism so that the schema does not depend on the version
|
17
|
+
if "interface_version" not in values:
|
18
|
+
values["interface_version"] = INTERFACE_VERSION
|
19
|
+
return values
|
@@ -1,4 +1,5 @@
|
|
1
1
|
from collections import Counter
|
2
|
+
from functools import cached_property
|
2
3
|
from typing import Any, Optional
|
3
4
|
|
4
5
|
import pydantic
|
@@ -58,11 +59,11 @@ class OperationLinks(pydantic.BaseModel):
|
|
58
59
|
inputs: list[OperationLink]
|
59
60
|
outputs: list[OperationLink]
|
60
61
|
|
61
|
-
@
|
62
|
+
@cached_property
|
62
63
|
def input_width(self) -> int:
|
63
64
|
return sum(len(link.qubits) for link in self.inputs)
|
64
65
|
|
65
|
-
@
|
66
|
+
@cached_property
|
66
67
|
def output_width(self) -> int:
|
67
68
|
return sum(len(link.qubits) for link in self.outputs)
|
68
69
|
|
@@ -122,6 +123,7 @@ class Operation(pydantic.BaseModel):
|
|
122
123
|
default=AtomicGate.UNKNOWN, description="Gate type"
|
123
124
|
)
|
124
125
|
is_daggered: bool = pydantic.Field(default=False)
|
126
|
+
expanded: bool = pydantic.Field(default=False)
|
125
127
|
|
126
128
|
|
127
129
|
class ProgramVisualModel(VersionedModel):
|
@@ -1 +1 @@
|
|
1
|
-
INTERFACE_VERSION = "
|
1
|
+
INTERFACE_VERSION = "10"
|
@@ -67,6 +67,9 @@ class HandleBinding(ASTNode):
|
|
67
67
|
def is_constant(self) -> bool:
|
68
68
|
return True
|
69
69
|
|
70
|
+
def expressions(self) -> list[Expression]:
|
71
|
+
return []
|
72
|
+
|
70
73
|
|
71
74
|
class NestedHandleBinding(HandleBinding):
|
72
75
|
base_handle: "ConcreteHandleBinding"
|
@@ -111,6 +114,9 @@ class NestedHandleBinding(HandleBinding):
|
|
111
114
|
def is_constant(self) -> bool:
|
112
115
|
return self.base_handle.is_constant()
|
113
116
|
|
117
|
+
def expressions(self) -> list[Expression]:
|
118
|
+
return self.base_handle.expressions()
|
119
|
+
|
114
120
|
|
115
121
|
class SubscriptHandleBinding(NestedHandleBinding):
|
116
122
|
index: Expression
|
@@ -191,6 +197,9 @@ class SubscriptHandleBinding(NestedHandleBinding):
|
|
191
197
|
and self.index.is_constant()
|
192
198
|
)
|
193
199
|
|
200
|
+
def expressions(self) -> list[Expression]:
|
201
|
+
return super().expressions() + [self.index]
|
202
|
+
|
194
203
|
|
195
204
|
class SlicedHandleBinding(NestedHandleBinding):
|
196
205
|
start: Expression
|
@@ -305,6 +314,9 @@ class SlicedHandleBinding(NestedHandleBinding):
|
|
305
314
|
and self.end.is_constant()
|
306
315
|
)
|
307
316
|
|
317
|
+
def expressions(self) -> list[Expression]:
|
318
|
+
return super().expressions() + [self.start, self.end]
|
319
|
+
|
308
320
|
|
309
321
|
class FieldHandleBinding(NestedHandleBinding):
|
310
322
|
field: str
|
@@ -52,6 +52,20 @@ class QuantumLambdaFunction(ASTNode):
|
|
52
52
|
def set_op_decl(self, fd: AnonQuantumOperandDeclaration) -> None:
|
53
53
|
self._func_decl = fd
|
54
54
|
|
55
|
+
@property
|
56
|
+
def named_func_decl(self) -> AnonQuantumOperandDeclaration:
|
57
|
+
named_params = [
|
58
|
+
param.rename(rename)
|
59
|
+
for param, rename in zip(
|
60
|
+
self.func_decl.positional_arg_declarations,
|
61
|
+
self.pos_rename_params,
|
62
|
+
strict=False, # strict=False enables lambda keyword args
|
63
|
+
)
|
64
|
+
]
|
65
|
+
return self.func_decl.model_copy(
|
66
|
+
update={"positional_arg_declarations": named_params}
|
67
|
+
)
|
68
|
+
|
55
69
|
|
56
70
|
class OperandIdentifier(ASTNode):
|
57
71
|
name: str
|
@@ -24,7 +24,12 @@ from classiq.interface.model.repeat import Repeat
|
|
24
24
|
from classiq.interface.model.variable_declaration_statement import (
|
25
25
|
VariableDeclarationStatement,
|
26
26
|
)
|
27
|
-
from classiq.interface.model.within_apply_operation import
|
27
|
+
from classiq.interface.model.within_apply_operation import (
|
28
|
+
Action,
|
29
|
+
Compute,
|
30
|
+
Uncompute,
|
31
|
+
WithinApply,
|
32
|
+
)
|
28
33
|
|
29
34
|
ConcreteQuantumStatement = Annotated[
|
30
35
|
Union[
|
@@ -43,6 +48,9 @@ ConcreteQuantumStatement = Annotated[
|
|
43
48
|
WithinApply,
|
44
49
|
PhaseOperation,
|
45
50
|
Block,
|
51
|
+
Compute,
|
52
|
+
Action,
|
53
|
+
Uncompute,
|
46
54
|
],
|
47
55
|
Field(..., discriminator="kind"),
|
48
56
|
]
|
@@ -15,3 +15,15 @@ class WithinApply(QuantumOperation):
|
|
15
15
|
|
16
16
|
def _as_back_ref(self: ASTNodeType) -> ASTNodeType:
|
17
17
|
return reset_lists(self, ["compute", "action"])
|
18
|
+
|
19
|
+
|
20
|
+
class Compute(QuantumOperation):
|
21
|
+
kind: Literal["Compute"]
|
22
|
+
|
23
|
+
|
24
|
+
class Action(QuantumOperation):
|
25
|
+
kind: Literal["Action"]
|
26
|
+
|
27
|
+
|
28
|
+
class Uncompute(QuantumOperation):
|
29
|
+
kind: Literal["Uncompute"]
|