classiq 0.74.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/analyzer/show_interactive_hack.py +1 -1
- classiq/applications/qnn/qlayer.py +9 -0
- classiq/interface/_version.py +1 -1
- 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/synthesis_metadata/synthesis_execution_data.py +10 -13
- classiq/interface/helpers/versioned_model.py +12 -0
- classiq/interface/interface_version.py +1 -1
- classiq/interface/model/handle_binding.py +12 -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/closure.py +13 -0
- classiq/model_expansions/evaluators/argument_types.py +6 -5
- classiq/model_expansions/evaluators/type_type_match.py +2 -2
- 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 +12 -0
- 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 +0 -5
- 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/qmod/qmod_variable.py +23 -1
- classiq/qmod/symbolic_expr.py +8 -2
- classiq/qmod/write_qmod.py +5 -1
- {classiq-0.74.0.dist-info → classiq-0.75.0.dist-info}/METADATA +1 -1
- {classiq-0.74.0.dist-info → classiq-0.75.0.dist-info}/RECORD +37 -36
- {classiq-0.74.0.dist-info → classiq-0.75.0.dist-info}/WHEEL +1 -1
@@ -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
@@ -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
|
|
@@ -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 +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
|
@@ -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"]
|
@@ -15,6 +15,9 @@ from classiq.interface.generator.expressions.expression_types import (
|
|
15
15
|
from classiq.interface.generator.expressions.proxies.classical.any_classical_value import (
|
16
16
|
AnyClassicalValue,
|
17
17
|
)
|
18
|
+
from classiq.interface.generator.expressions.proxies.classical.classical_array_proxy import (
|
19
|
+
ClassicalArrayProxy,
|
20
|
+
)
|
18
21
|
from classiq.interface.generator.expressions.proxies.classical.classical_proxy import (
|
19
22
|
ClassicalProxy,
|
20
23
|
)
|
@@ -170,7 +173,11 @@ def get_field(
|
|
170
173
|
return AnyClassicalValue(f"({proxy}).{field}")
|
171
174
|
if isinstance(proxy, type) and issubclass(proxy, Enum):
|
172
175
|
return getattr(proxy, field)
|
173
|
-
if
|
176
|
+
if (
|
177
|
+
isinstance(proxy, Symbol)
|
178
|
+
and not isinstance(proxy, QmodSizedProxy)
|
179
|
+
and not isinstance(proxy, ClassicalProxy)
|
180
|
+
):
|
174
181
|
raise ClassiqExpansionError(
|
175
182
|
f"Cannot evaluate '{proxy}.{field}': Variable {str(proxy)!r} is not "
|
176
183
|
f"initialized"
|
@@ -229,7 +236,9 @@ def _is_qmod_value(val: Any) -> bool:
|
|
229
236
|
|
230
237
|
|
231
238
|
def do_subscript(value: Any, index: Any) -> Any:
|
232
|
-
if not isinstance(value, list) or not isinstance(
|
239
|
+
if not isinstance(value, (list, ClassicalArrayProxy)) or not isinstance(
|
240
|
+
index, QmodQNumProxy
|
241
|
+
):
|
233
242
|
if isinstance(index, (QmodSizedProxy, QmodStructInstance)):
|
234
243
|
raise ClassiqExpansionError(
|
235
244
|
f"Subscript {value}[{index}] is not supported. Supported subscripts "
|
@@ -251,15 +260,22 @@ def do_subscript(value: Any, index: Any) -> Any:
|
|
251
260
|
"Quantum numeric subscript must be an unsigned integer (is_signed=False, "
|
252
261
|
"fraction_digits=0)"
|
253
262
|
)
|
254
|
-
if
|
263
|
+
if isinstance(value, ClassicalArrayProxy):
|
264
|
+
length = value.length
|
265
|
+
else:
|
266
|
+
length = len(value)
|
267
|
+
if length != 2**index.size:
|
255
268
|
raise ClassiqExpansionError(
|
256
269
|
f"Quantum numeric subscript size mismatch: The quantum numeric has "
|
257
|
-
f"{index.size} qubits but the list size is {
|
270
|
+
f"{index.size} qubits but the list size is {length} != 2**{index.size}"
|
271
|
+
)
|
272
|
+
if isinstance(value, ClassicalArrayProxy):
|
273
|
+
return AnyClassicalValue(f"{value}[{index}]")
|
274
|
+
else:
|
275
|
+
return Piecewise(
|
276
|
+
*[(item, Eq(index, idx)) for idx, item in enumerate(value[:-1])],
|
277
|
+
(value[-1], True),
|
258
278
|
)
|
259
|
-
return Piecewise(
|
260
|
-
*[(item, Eq(index, idx)) for idx, item in enumerate(value[:-1])],
|
261
|
-
(value[-1], True),
|
262
|
-
)
|
263
279
|
|
264
280
|
|
265
281
|
def do_slice(value: Any, lower: Any, upper: Any) -> Any:
|
@@ -8,6 +8,15 @@ from typing import Any, Optional
|
|
8
8
|
from typing_extensions import Self
|
9
9
|
|
10
10
|
from classiq.interface.exceptions import ClassiqInternalExpansionError
|
11
|
+
from classiq.interface.generator.expressions.proxies.classical.classical_proxy import (
|
12
|
+
ClassicalProxy,
|
13
|
+
)
|
14
|
+
from classiq.interface.generator.expressions.proxies.classical.classical_struct_proxy import (
|
15
|
+
ClassicalStructProxy,
|
16
|
+
)
|
17
|
+
from classiq.interface.generator.expressions.proxies.classical.utils import (
|
18
|
+
get_proxy_type,
|
19
|
+
)
|
11
20
|
from classiq.interface.model.quantum_function_declaration import (
|
12
21
|
NamedParamsQuantumFunctionDeclaration,
|
13
22
|
PositionalArg,
|
@@ -139,6 +148,10 @@ def _evaluated_arg_to_str(arg: Any) -> str:
|
|
139
148
|
return _evaluated_one_operand_to_str(arg)
|
140
149
|
if isinstance(arg, list) and arg and isinstance(arg[0], FunctionClosure):
|
141
150
|
return _evaluated_operands_list_to_str(arg)
|
151
|
+
if isinstance(arg, ClassicalProxy):
|
152
|
+
if isinstance(arg, ClassicalStructProxy):
|
153
|
+
return repr(arg.struct_declaration)
|
154
|
+
return repr(get_proxy_type(arg))
|
142
155
|
return evaluated_classical_param_to_str(arg)
|
143
156
|
|
144
157
|
|
@@ -34,9 +34,10 @@ def add_information_from_output_arguments(
|
|
34
34
|
if parameter.direction != PortDeclarationDirection.Output:
|
35
35
|
continue
|
36
36
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
37
|
+
if parameter.quantum_type.is_evaluated:
|
38
|
+
copy_type_information(
|
39
|
+
parameter.quantum_type,
|
40
|
+
argument_as_quantum_symbol.quantum_type,
|
41
|
+
str(argument_as_quantum_symbol.handle),
|
42
|
+
)
|
42
43
|
return args
|
@@ -85,7 +85,7 @@ def _check_classical_type_match(
|
|
85
85
|
) -> None:
|
86
86
|
if (
|
87
87
|
not isinstance(op_param, AnonClassicalParameterDeclaration)
|
88
|
-
or decl_param.classical_type.
|
89
|
-
!= op_param.classical_type.
|
88
|
+
or decl_param.classical_type.clear_flags()
|
89
|
+
!= op_param.classical_type.clear_flags()
|
90
90
|
):
|
91
91
|
raise ClassiqExpansionError(error_message)
|
@@ -81,11 +81,14 @@ def translate_ast_arg_to_python_qmod(param: PositionalArg, evaluated: Evaluated)
|
|
81
81
|
]
|
82
82
|
classical_value = evaluated.value
|
83
83
|
if isinstance(classical_value, QmodStructInstance):
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
84
|
+
if param.classical_type.is_purely_declarative:
|
85
|
+
return CParamStruct(
|
86
|
+
expr=param.name,
|
87
|
+
struct_type=Struct(name=classical_value.struct_declaration.name),
|
88
|
+
qmodule=QMODULE,
|
89
|
+
)
|
90
|
+
else:
|
91
|
+
return get_sdk_compatible_python_object(dict(classical_value.fields))
|
89
92
|
if isinstance(classical_value, ClassicalProxy):
|
90
93
|
return create_param(
|
91
94
|
str(classical_value.handle), get_proxy_type(classical_value), QMODULE
|
@@ -111,15 +114,18 @@ class _InterpreterExpandable(QFunc):
|
|
111
114
|
for name, func in self._qmodule.native_defs.items()
|
112
115
|
if name not in self._interpreter._top_level_scope
|
113
116
|
}
|
117
|
+
generative_functions = self._qmodule.generative_functions
|
114
118
|
self._interpreter.update_declarative_functions(
|
115
119
|
declarative_functions, self._qmodule
|
116
120
|
)
|
117
|
-
self._interpreter.update_generative_functions(
|
118
|
-
self._qmodule.generative_functions
|
119
|
-
)
|
121
|
+
self._interpreter.update_generative_functions(generative_functions)
|
120
122
|
func_decls = self._get_function_declarations()
|
121
123
|
for dec_func in declarative_functions.values():
|
122
124
|
resolve_function_calls(dec_func, func_decls)
|
125
|
+
self._interpreter.infer_symbolic_parameters(
|
126
|
+
list(declarative_functions.values()),
|
127
|
+
[func.func_decl for func in generative_functions.values()],
|
128
|
+
)
|
123
129
|
resolve_function_calls(dummy_function, func_decls)
|
124
130
|
stmt = dummy_function.body[-1]
|
125
131
|
with generative_mode_context(False):
|
@@ -9,14 +9,15 @@ from typing import Any, cast
|
|
9
9
|
import sympy
|
10
10
|
from pydantic import ValidationError
|
11
11
|
|
12
|
-
from classiq.interface.debug_info.debug_info import
|
12
|
+
from classiq.interface.debug_info.debug_info import (
|
13
|
+
new_function_debug_info_by_node,
|
14
|
+
)
|
13
15
|
from classiq.interface.exceptions import (
|
14
16
|
ClassiqError,
|
15
17
|
ClassiqExpansionError,
|
16
18
|
ClassiqInternalExpansionError,
|
17
19
|
)
|
18
20
|
from classiq.interface.generator.expressions.expression import Expression
|
19
|
-
from classiq.interface.generator.generated_circuit_data import OperationLevel
|
20
21
|
from classiq.interface.generator.types.compilation_metadata import CompilationMetadata
|
21
22
|
from classiq.interface.model.handle_binding import (
|
22
23
|
FieldHandleBinding,
|
@@ -237,20 +238,16 @@ class BaseInterpreter:
|
|
237
238
|
if source_ref is not None
|
238
239
|
else nullcontext()
|
239
240
|
)
|
240
|
-
|
241
|
-
self._model.debug_info[statement.uuid] = FunctionDebugInfo(
|
242
|
-
name="",
|
243
|
-
parameters={},
|
244
|
-
level=OperationLevel.QMOD_STATEMENT,
|
245
|
-
statement_type=None,
|
246
|
-
is_allocate_or_free=False,
|
247
|
-
is_inverse=False,
|
248
|
-
port_to_passed_variable_map={},
|
249
|
-
node=statement._as_back_ref(),
|
250
|
-
)
|
241
|
+
self.add_to_debug_info(statement)
|
251
242
|
with error_context, self._builder.source_ref_context(source_ref):
|
252
243
|
self.emit(statement)
|
253
244
|
|
245
|
+
def add_to_debug_info(self, statement: QuantumStatement) -> None:
|
246
|
+
if statement.uuid not in self._model.debug_info:
|
247
|
+
self._model.debug_info[statement.uuid] = new_function_debug_info_by_node(
|
248
|
+
statement # type: ignore[arg-type]
|
249
|
+
)
|
250
|
+
|
254
251
|
def _expand_operation(self, operation: Closure) -> OperationContext:
|
255
252
|
with self._builder.operation_context(operation) as context:
|
256
253
|
if isinstance(operation, FunctionClosure) and (
|