classiq 0.79.1__py3-none-any.whl → 0.80.1__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 +7 -0
- classiq/_internals/api_wrapper.py +95 -19
- classiq/analyzer/show_interactive_hack.py +63 -48
- classiq/applications/chemistry/chemistry_model_constructor.py +1 -1
- classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +3 -3
- classiq/interface/_version.py +1 -1
- classiq/interface/analyzer/result.py +1 -1
- classiq/interface/chemistry/operator.py +3 -8
- classiq/interface/executor/quantum_program_params.py +18 -0
- classiq/interface/generator/application_apis/chemistry_declarations.py +3 -3
- classiq/interface/generator/application_apis/combinatorial_optimization_declarations.py +3 -3
- classiq/interface/generator/application_apis/entangler_declarations.py +3 -3
- classiq/interface/generator/arith/number_utils.py +8 -0
- classiq/interface/generator/expressions/proxies/classical/utils.py +8 -3
- classiq/interface/generator/functions/classical_type.py +63 -7
- classiq/interface/generator/generated_circuit_data.py +10 -1
- classiq/interface/helpers/custom_pydantic_types.py +2 -3
- classiq/interface/model/allocate.py +6 -0
- classiq/interface/model/quantum_type.py +26 -5
- classiq/interface/server/routes.py +6 -0
- classiq/model_expansions/atomic_expression_functions_defs.py +9 -1
- classiq/model_expansions/capturing/captured_vars.py +1 -1
- classiq/model_expansions/evaluators/classical_type_inference.py +39 -27
- classiq/model_expansions/evaluators/parameter_types.py +65 -3
- classiq/model_expansions/interpreters/frontend_generative_interpreter.py +1 -1
- classiq/model_expansions/quantum_operations/allocate.py +121 -34
- classiq/model_expansions/quantum_operations/call_emitter.py +1 -1
- classiq/model_expansions/scope.py +1 -2
- classiq/open_library/functions/__init__.py +2 -0
- classiq/open_library/functions/state_preparation.py +86 -3
- classiq/qmod/builtins/classical_execution_primitives.py +2 -4
- classiq/qmod/builtins/functions/__init__.py +3 -0
- classiq/qmod/builtins/functions/arithmetic.py +21 -1
- classiq/qmod/builtins/functions/exponentiation.py +24 -0
- classiq/qmod/builtins/operations.py +32 -2
- classiq/qmod/builtins/structs.py +47 -1
- classiq/qmod/native/pretty_printer.py +15 -6
- classiq/qmod/pretty_print/pretty_printer.py +15 -6
- classiq/qmod/python_classical_type.py +4 -4
- classiq/qmod/qmod_parameter.py +5 -1
- classiq/qmod/semantics/validation/main_validation.py +5 -0
- classiq/quantum_program.py +69 -0
- {classiq-0.79.1.dist-info → classiq-0.80.1.dist-info}/METADATA +1 -1
- {classiq-0.79.1.dist-info → classiq-0.80.1.dist-info}/RECORD +46 -44
- {classiq-0.79.1.dist-info → classiq-0.80.1.dist-info}/WHEEL +1 -1
- /classiq/{model_expansions/utils → interface/helpers}/text_utils.py +0 -0
@@ -1,12 +1,14 @@
|
|
1
1
|
from itertools import chain
|
2
|
-
from typing import TYPE_CHECKING, Any, Literal
|
2
|
+
from typing import TYPE_CHECKING, Any, Literal, Optional
|
3
3
|
|
4
4
|
import pydantic
|
5
5
|
from pydantic import ConfigDict, PrivateAttr
|
6
6
|
from typing_extensions import Self
|
7
7
|
|
8
8
|
from classiq.interface.ast_node import HashableASTNode
|
9
|
+
from classiq.interface.exceptions import ClassiqInternalExpansionError
|
9
10
|
from classiq.interface.generator.expressions.expression import Expression
|
11
|
+
from classiq.interface.generator.expressions.expression_types import ExpressionValue
|
10
12
|
from classiq.interface.generator.expressions.proxies.classical.any_classical_value import (
|
11
13
|
AnyClassicalValue,
|
12
14
|
)
|
@@ -123,7 +125,7 @@ class ClassicalList(ClassicalType):
|
|
123
125
|
return super().is_purely_generative and self.element_type.is_purely_generative
|
124
126
|
|
125
127
|
def get_raw_type(self) -> "ConcreteClassicalType":
|
126
|
-
raw_type =
|
128
|
+
raw_type = ClassicalArray(element_type=self.element_type.get_raw_type())
|
127
129
|
if self._is_generative:
|
128
130
|
raw_type.set_generative()
|
129
131
|
return raw_type
|
@@ -144,15 +146,69 @@ class StructMetaType(ClassicalType):
|
|
144
146
|
class ClassicalArray(ClassicalType):
|
145
147
|
kind: Literal["array"]
|
146
148
|
element_type: "ConcreteClassicalType"
|
147
|
-
size: int
|
149
|
+
size: Optional[int] = None
|
150
|
+
length: Optional[Expression] = pydantic.Field(exclude=True, default=None)
|
148
151
|
|
149
152
|
@pydantic.model_validator(mode="before")
|
150
153
|
@classmethod
|
151
154
|
def _set_kind(cls, values: Any) -> dict[str, Any]:
|
152
155
|
return values_with_discriminator(values, "kind", "array")
|
153
156
|
|
157
|
+
@pydantic.model_validator(mode="before")
|
158
|
+
@classmethod
|
159
|
+
def _set_length(cls, values: Any) -> Any:
|
160
|
+
if isinstance(values, dict):
|
161
|
+
size = values.get("size")
|
162
|
+
length = values.get("length")
|
163
|
+
else:
|
164
|
+
size = values.size
|
165
|
+
length = values.length
|
166
|
+
if size is not None:
|
167
|
+
if isinstance(values, dict):
|
168
|
+
values["length"] = Expression(expr=str(size))
|
169
|
+
else:
|
170
|
+
values.length = Expression(expr=str(size))
|
171
|
+
elif length is not None:
|
172
|
+
if isinstance(length, dict):
|
173
|
+
expr = length["expr"]
|
174
|
+
else:
|
175
|
+
expr = length.expr
|
176
|
+
expr_size: Optional[int] = None
|
177
|
+
try: # noqa: SIM105
|
178
|
+
expr_size = int(expr)
|
179
|
+
except ValueError:
|
180
|
+
pass
|
181
|
+
if expr_size is not None:
|
182
|
+
if isinstance(values, dict):
|
183
|
+
values["size"] = expr_size
|
184
|
+
else:
|
185
|
+
values.size = expr_size
|
186
|
+
return values
|
187
|
+
|
188
|
+
@property
|
189
|
+
def has_length(self) -> bool:
|
190
|
+
return self.length is not None and self.length.is_evaluated()
|
191
|
+
|
192
|
+
@property
|
193
|
+
def length_value(self) -> int:
|
194
|
+
if not self.has_length:
|
195
|
+
raise ClassiqInternalExpansionError(
|
196
|
+
"Tried to access unevaluated length of classical array"
|
197
|
+
)
|
198
|
+
assert self.length is not None
|
199
|
+
return self.length.to_int_value()
|
200
|
+
|
154
201
|
def get_classical_proxy(self, handle: HandleBinding) -> ClassicalProxy:
|
155
|
-
|
202
|
+
length: ExpressionValue
|
203
|
+
if self.length is None:
|
204
|
+
length = AnyClassicalValue(f"get_field({handle}, 'len')")
|
205
|
+
elif not self.length.is_evaluated():
|
206
|
+
raise ClassiqInternalExpansionError(
|
207
|
+
"Classical list length is not evaluated"
|
208
|
+
)
|
209
|
+
else:
|
210
|
+
length = self.length.value.value
|
211
|
+
return ClassicalArrayProxy(handle, self.element_type, length)
|
156
212
|
|
157
213
|
@property
|
158
214
|
def expressions(self) -> list[Expression]:
|
@@ -167,7 +223,7 @@ class ClassicalArray(ClassicalType):
|
|
167
223
|
return super().is_purely_generative and self.element_type.is_purely_generative
|
168
224
|
|
169
225
|
def get_raw_type(self) -> "ConcreteClassicalType":
|
170
|
-
raw_type =
|
226
|
+
raw_type = ClassicalArray(element_type=self.element_type.get_raw_type())
|
171
227
|
if self._is_generative:
|
172
228
|
raw_type.set_generative()
|
173
229
|
return raw_type
|
@@ -208,13 +264,13 @@ class ClassicalTuple(ClassicalType):
|
|
208
264
|
def get_raw_type(self) -> "ConcreteClassicalType":
|
209
265
|
if len(self.element_types) == 0:
|
210
266
|
return self
|
211
|
-
raw_type =
|
267
|
+
raw_type = ClassicalArray(element_type=self.element_types[0].get_raw_type())
|
212
268
|
if self._is_generative:
|
213
269
|
raw_type.set_generative()
|
214
270
|
return raw_type
|
215
271
|
|
216
272
|
@property
|
217
|
-
def
|
273
|
+
def length(self) -> int:
|
218
274
|
return len(self.element_types)
|
219
275
|
|
220
276
|
|
@@ -54,6 +54,8 @@ VISUALIZATION_HIDE_LIST = [
|
|
54
54
|
"stmt_block",
|
55
55
|
]
|
56
56
|
|
57
|
+
CONTROLLED_PREFIX = "c-"
|
58
|
+
|
57
59
|
|
58
60
|
def last_name_in_call_hierarchy(name: str) -> str:
|
59
61
|
return name.split(CLASSIQ_HIERARCHY_SEPARATOR)[-1]
|
@@ -213,7 +215,9 @@ class FunctionDebugInfoInterface(pydantic.BaseModel):
|
|
213
215
|
name = generate_original_function_name(back_ref.func_name).removeprefix(
|
214
216
|
ARITH_ENGINE_PREFIX
|
215
217
|
)
|
216
|
-
|
218
|
+
name_with_suffix = self.add_suffix_from_generated_name(generated_name, name)
|
219
|
+
modified_name = self.modify_name_for_controlled_qfunc(name_with_suffix)
|
220
|
+
return modified_name
|
217
221
|
|
218
222
|
statement_kind: str = back_ref.kind
|
219
223
|
if isinstance(back_ref, ArithmeticOperation):
|
@@ -222,6 +226,11 @@ class FunctionDebugInfoInterface(pydantic.BaseModel):
|
|
222
226
|
generated_name, STATEMENTS_NAME[statement_kind]
|
223
227
|
)
|
224
228
|
|
229
|
+
def modify_name_for_controlled_qfunc(self, generated_name: str) -> str:
|
230
|
+
if self.control_variable is None:
|
231
|
+
return generated_name
|
232
|
+
return f"{CONTROLLED_PREFIX}{generated_name}"
|
233
|
+
|
225
234
|
def add_suffix_from_generated_name(self, generated_name: str, name: str) -> str:
|
226
235
|
if part_match := PART_SUFFIX_REGEX.match(generated_name):
|
227
236
|
suffix = f" [{part_match.group(1)}]"
|
@@ -66,9 +66,8 @@ else:
|
|
66
66
|
StringConstraints(strip_whitespace=True, min_length=1, pattern="^[IXYZ]+$"),
|
67
67
|
]
|
68
68
|
|
69
|
-
|
70
|
-
|
71
|
-
]
|
69
|
+
PauliTuple = tuple[PydanticPauliMonomialStr, ParameterComplexType]
|
70
|
+
PydanticPauliList = Annotated[list[PauliTuple], Field(min_length=1)]
|
72
71
|
|
73
72
|
if TYPE_CHECKING:
|
74
73
|
PydanticFloatTuple = tuple[float, float]
|
@@ -9,6 +9,8 @@ from classiq.interface.model.quantum_statement import QuantumOperation
|
|
9
9
|
class Allocate(QuantumOperation):
|
10
10
|
kind: Literal["Allocate"]
|
11
11
|
size: Optional[Expression] = None
|
12
|
+
is_signed: Optional[Expression] = None
|
13
|
+
fraction_digits: Optional[Expression] = None
|
12
14
|
target: ConcreteHandleBinding
|
13
15
|
|
14
16
|
@property
|
@@ -20,4 +22,8 @@ class Allocate(QuantumOperation):
|
|
20
22
|
exprs = []
|
21
23
|
if self.size is not None:
|
22
24
|
exprs.append(self.size)
|
25
|
+
if self.is_signed is not None:
|
26
|
+
exprs.append(self.is_signed)
|
27
|
+
if self.fraction_digits is not None:
|
28
|
+
exprs.append(self.fraction_digits)
|
23
29
|
return exprs
|
@@ -6,6 +6,7 @@ from typing_extensions import Self
|
|
6
6
|
|
7
7
|
from classiq.interface.ast_node import HashableASTNode
|
8
8
|
from classiq.interface.exceptions import ClassiqValueError
|
9
|
+
from classiq.interface.generator.arith import number_utils
|
9
10
|
from classiq.interface.generator.arith.register_user_input import (
|
10
11
|
RegisterArithmeticInfo,
|
11
12
|
RegisterUserInput,
|
@@ -290,8 +291,20 @@ class QuantumNumeric(QuantumScalar):
|
|
290
291
|
exprs.append(self.fraction_digits)
|
291
292
|
return exprs
|
292
293
|
|
293
|
-
def get_bounds(
|
294
|
-
|
294
|
+
def get_bounds(
|
295
|
+
self, machine_precision: Optional[int] = None
|
296
|
+
) -> Optional[tuple[float, float]]:
|
297
|
+
if (
|
298
|
+
self._bounds is None
|
299
|
+
or machine_precision is None
|
300
|
+
or self.fraction_digits_value <= machine_precision
|
301
|
+
):
|
302
|
+
return self._bounds
|
303
|
+
|
304
|
+
return (
|
305
|
+
number_utils.limit_fraction_places(self._bounds[0], machine_precision),
|
306
|
+
number_utils.limit_fraction_places(self._bounds[1], machine_precision),
|
307
|
+
)
|
295
308
|
|
296
309
|
def set_bounds(self, bounds: Optional[tuple[float, float]]) -> None:
|
297
310
|
self._bounds = bounds
|
@@ -299,11 +312,19 @@ class QuantumNumeric(QuantumScalar):
|
|
299
312
|
def reset_bounds(self) -> None:
|
300
313
|
self.set_bounds(None)
|
301
314
|
|
302
|
-
def get_maximal_bounds(
|
315
|
+
def get_maximal_bounds(
|
316
|
+
self, machine_precision: Optional[int] = None
|
317
|
+
) -> tuple[float, float]:
|
318
|
+
size = self.size_in_bits
|
319
|
+
fraction_digits = self.fraction_digits_value
|
320
|
+
if machine_precision is not None and fraction_digits > machine_precision:
|
321
|
+
size -= fraction_digits - machine_precision
|
322
|
+
fraction_digits = machine_precision
|
323
|
+
|
303
324
|
return RegisterArithmeticInfo.get_maximal_bounds(
|
304
|
-
size=
|
325
|
+
size=size,
|
305
326
|
is_signed=self.sign_value,
|
306
|
-
fraction_places=
|
327
|
+
fraction_places=fraction_digits,
|
307
328
|
)
|
308
329
|
|
309
330
|
|
@@ -57,6 +57,12 @@ TASKS_VISUAL_MODEL_FULL_PATH = ANALYZER_PREFIX + TASKS_VISUAL_MODEL_SUFFIX
|
|
57
57
|
EXECUTION_JOBS_SUFFIX = "/jobs"
|
58
58
|
EXECUTION_JOBS_FULL_PATH = EXECUTION_PREFIX + EXECUTION_JOBS_SUFFIX
|
59
59
|
|
60
|
+
QUANTUM_PROGRAM_PREFIX = "/quantum_program"
|
61
|
+
TRANSPILATION_SUFFIX = "/transpilation"
|
62
|
+
TRANSPILATION_FULL_PATH = QUANTUM_PROGRAM_PREFIX + TRANSPILATION_SUFFIX
|
63
|
+
ASSIGN_PARAMETERS_SUFFIX = "/assign_parameters"
|
64
|
+
ASSIGN_PARAMETERS_FULL_PATH = QUANTUM_PROGRAM_PREFIX + ASSIGN_PARAMETERS_SUFFIX
|
65
|
+
|
60
66
|
ANALYZER_FULL_PATH = ANALYZER_PREFIX + TASKS_SUFFIX
|
61
67
|
ANALYZER_RB_FULL_PATH = ANALYZER_PREFIX + TASK_RB_SUFFIX
|
62
68
|
GENERATE_RESOURCE_ESTIMATOR_REPORT = "/resource_estimator_report"
|
@@ -147,11 +147,19 @@ def python_val_to_qmod(val: Any, qmod_type: ClassicalType) -> ExpressionValue:
|
|
147
147
|
}
|
148
148
|
return QmodStructInstance(struct_decl.model_copy(), qmod_dict)
|
149
149
|
|
150
|
-
if isinstance(qmod_type, ClassicalList):
|
150
|
+
if isinstance(qmod_type, (ClassicalList, ClassicalArray)):
|
151
151
|
if not isinstance(val, list):
|
152
152
|
raise ClassiqInternalExpansionError("Bad value for list")
|
153
153
|
return [python_val_to_qmod(elem, qmod_type.element_type) for elem in val]
|
154
154
|
|
155
|
+
if isinstance(qmod_type, ClassicalTuple):
|
156
|
+
if not isinstance(val, list):
|
157
|
+
raise ClassiqInternalExpansionError("Bad value for list")
|
158
|
+
return [
|
159
|
+
python_val_to_qmod(elem, elem_type)
|
160
|
+
for elem, elem_type in zip_strict(val, qmod_type.element_types, strict=True)
|
161
|
+
]
|
162
|
+
|
155
163
|
if isinstance(qmod_type, OpaqueHandle):
|
156
164
|
if not isinstance(val, QmodPyObject):
|
157
165
|
raise ClassiqInternalExpansionError("Bad value opaque handle")
|
@@ -16,6 +16,7 @@ from classiq.interface.generator.functions.port_declaration import (
|
|
16
16
|
PortDeclarationDirection,
|
17
17
|
)
|
18
18
|
from classiq.interface.generator.functions.type_qualifier import TypeQualifier
|
19
|
+
from classiq.interface.helpers.text_utils import are, readable_list, s, they
|
19
20
|
from classiq.interface.model.classical_parameter_declaration import (
|
20
21
|
ClassicalParameterDeclaration,
|
21
22
|
)
|
@@ -42,7 +43,6 @@ from classiq.model_expansions.transformers.model_renamer import (
|
|
42
43
|
SymbolRenaming,
|
43
44
|
)
|
44
45
|
from classiq.model_expansions.transformers.var_splitter import SymbolPart
|
45
|
-
from classiq.model_expansions.utils.text_utils import are, readable_list, s, they
|
46
46
|
|
47
47
|
if TYPE_CHECKING:
|
48
48
|
from classiq.model_expansions.closure import FunctionClosure
|
@@ -1,6 +1,7 @@
|
|
1
1
|
from typing import TYPE_CHECKING, Any, Union
|
2
2
|
|
3
3
|
from classiq.interface.exceptions import ClassiqExpansionError
|
4
|
+
from classiq.interface.generator.expressions.expression import Expression
|
4
5
|
from classiq.interface.generator.expressions.proxies.classical.any_classical_value import (
|
5
6
|
AnyClassicalValue,
|
6
7
|
)
|
@@ -67,15 +68,22 @@ def _infer_classical_array_type(
|
|
67
68
|
return classical_type
|
68
69
|
else:
|
69
70
|
raise ClassiqExpansionError(f"Array expected, got {str(val)!r}")
|
70
|
-
if (
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
71
|
+
if isinstance(val_length, int) and (
|
72
|
+
(
|
73
|
+
isinstance(classical_type, ClassicalArray)
|
74
|
+
and classical_type.length is not None
|
75
|
+
and classical_type.length.is_evaluated()
|
76
|
+
and _is_int(classical_type.length.value.value)
|
77
|
+
and val_length != (type_length := int(classical_type.length.value.value))
|
78
|
+
)
|
79
|
+
or (
|
80
|
+
isinstance(classical_type, ClassicalTuple)
|
81
|
+
and val_length != (type_length := classical_type.length)
|
82
|
+
)
|
75
83
|
):
|
76
84
|
raise ClassiqExpansionError(
|
77
85
|
f"Type mismatch: Argument has {val_length} items but "
|
78
|
-
f"{
|
86
|
+
f"{type_length} expected"
|
79
87
|
)
|
80
88
|
new_classical_type = _infer_inner_array_types(classical_type, val, val_length)
|
81
89
|
if classical_type.is_generative:
|
@@ -86,25 +94,29 @@ def _infer_classical_array_type(
|
|
86
94
|
def _infer_inner_array_types(
|
87
95
|
classical_type: ClassicalType, val: Any, val_length: Any
|
88
96
|
) -> ClassicalType:
|
89
|
-
if isinstance(classical_type,
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
)
|
97
|
-
element_type: ClassicalType
|
98
|
-
if val_length == 0:
|
99
|
-
element_type = classical_type.element_type
|
100
|
-
else:
|
101
|
-
element_type = infer_classical_type(val[0], classical_type.element_type)
|
102
|
-
return ClassicalArray(element_type=element_type, size=val_length)
|
97
|
+
if isinstance(classical_type, ClassicalTuple):
|
98
|
+
return ClassicalTuple(
|
99
|
+
element_types=(
|
100
|
+
infer_classical_type(val[idx], element_type)
|
101
|
+
for idx, element_type in enumerate(classical_type.element_types)
|
102
|
+
),
|
103
|
+
)
|
103
104
|
if TYPE_CHECKING:
|
104
|
-
assert isinstance(classical_type,
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
105
|
+
assert isinstance(classical_type, (ClassicalList, ClassicalArray))
|
106
|
+
if _is_int(val_length) and val_length != 0:
|
107
|
+
return ClassicalTuple(
|
108
|
+
element_types=(
|
109
|
+
infer_classical_type(val[i], classical_type.element_type)
|
110
|
+
for i in range(int(val_length))
|
111
|
+
),
|
112
|
+
)
|
113
|
+
element_type: ClassicalType
|
114
|
+
if val_length == 0:
|
115
|
+
element_type = classical_type.element_type
|
116
|
+
else:
|
117
|
+
element_type = infer_classical_type(val[0], classical_type.element_type)
|
118
|
+
if _is_int(val_length):
|
119
|
+
length = Expression(expr=str(int(val_length)))
|
120
|
+
else:
|
121
|
+
length = None
|
122
|
+
return ClassicalArray(element_type=element_type, length=length)
|
@@ -10,6 +10,12 @@ from classiq.interface.generator.expressions.expression import Expression
|
|
10
10
|
from classiq.interface.generator.expressions.proxies.classical.any_classical_value import (
|
11
11
|
AnyClassicalValue,
|
12
12
|
)
|
13
|
+
from classiq.interface.generator.functions.classical_type import (
|
14
|
+
ClassicalArray,
|
15
|
+
ClassicalList,
|
16
|
+
ClassicalTuple,
|
17
|
+
ClassicalType,
|
18
|
+
)
|
13
19
|
from classiq.interface.generator.functions.concrete_types import ConcreteQuantumType
|
14
20
|
from classiq.interface.generator.functions.port_declaration import (
|
15
21
|
PortDeclarationDirection,
|
@@ -139,11 +145,12 @@ def _evaluate_type_from_arg(
|
|
139
145
|
parameter: PositionalArg, argument: Evaluated, inner_scope: Scope
|
140
146
|
) -> PositionalArg:
|
141
147
|
if isinstance(parameter, ClassicalParameterDeclaration):
|
148
|
+
updated_classical_type = evaluate_type_in_classical_symbol(
|
149
|
+
parameter.classical_type.model_copy(), inner_scope, parameter.name
|
150
|
+
)
|
142
151
|
return ClassicalParameterDeclaration(
|
143
152
|
name=parameter.name,
|
144
|
-
classical_type=infer_classical_type(
|
145
|
-
argument.value, parameter.classical_type
|
146
|
-
),
|
153
|
+
classical_type=infer_classical_type(argument.value, updated_classical_type),
|
147
154
|
)
|
148
155
|
|
149
156
|
if not isinstance(parameter, PortDeclaration):
|
@@ -297,3 +304,58 @@ def _inject_quantum_arg_info_to_type(
|
|
297
304
|
param_name,
|
298
305
|
)
|
299
306
|
return parameter_type
|
307
|
+
|
308
|
+
|
309
|
+
def evaluate_type_in_classical_symbol(
|
310
|
+
type_to_update: ClassicalType, scope: Scope, param_name: str
|
311
|
+
) -> ClassicalType:
|
312
|
+
updated_type: ClassicalType
|
313
|
+
if isinstance(type_to_update, ClassicalList):
|
314
|
+
updated_type = ClassicalArray(
|
315
|
+
element_type=evaluate_type_in_classical_symbol(
|
316
|
+
type_to_update.element_type, scope, param_name
|
317
|
+
)
|
318
|
+
)
|
319
|
+
elif isinstance(type_to_update, ClassicalArray):
|
320
|
+
length = type_to_update.length
|
321
|
+
if length is not None:
|
322
|
+
new_length = _eval_expr(
|
323
|
+
length, scope, int, "classical array", "length", param_name
|
324
|
+
)
|
325
|
+
if new_length is not None:
|
326
|
+
length = Expression(expr=str(new_length))
|
327
|
+
updated_type = ClassicalArray(
|
328
|
+
element_type=evaluate_type_in_classical_symbol(
|
329
|
+
type_to_update.element_type, scope, param_name
|
330
|
+
),
|
331
|
+
length=length,
|
332
|
+
)
|
333
|
+
elif isinstance(type_to_update, ClassicalTuple):
|
334
|
+
updated_type = ClassicalTuple(
|
335
|
+
element_types=[
|
336
|
+
evaluate_type_in_classical_symbol(element_type, scope, param_name)
|
337
|
+
for element_type in type_to_update.element_types
|
338
|
+
],
|
339
|
+
)
|
340
|
+
elif (
|
341
|
+
isinstance(type_to_update, TypeName)
|
342
|
+
and type_to_update.has_classical_struct_decl
|
343
|
+
):
|
344
|
+
updated_type = TypeName(name=type_to_update.name)
|
345
|
+
updated_type.set_classical_struct_decl(
|
346
|
+
type_to_update.classical_struct_decl.model_copy(
|
347
|
+
update=dict(
|
348
|
+
variables={
|
349
|
+
field_name: evaluate_type_in_classical_symbol(
|
350
|
+
field_type, scope, param_name
|
351
|
+
)
|
352
|
+
for field_name, field_type in type_to_update.classical_struct_decl.variables.items()
|
353
|
+
}
|
354
|
+
)
|
355
|
+
)
|
356
|
+
)
|
357
|
+
else:
|
358
|
+
updated_type = type_to_update
|
359
|
+
if type_to_update.is_generative:
|
360
|
+
updated_type.set_generative()
|
361
|
+
return updated_type
|
@@ -24,7 +24,7 @@ from classiq.qmod.model_state_container import QMODULE
|
|
24
24
|
|
25
25
|
class FrontendGenerativeInterpreter(GenerativeInterpreter):
|
26
26
|
def emit_allocate(self, allocate: Allocate) -> None:
|
27
|
-
AllocateEmitter(self,
|
27
|
+
AllocateEmitter(self, allow_symbolic_attrs=True).emit(allocate)
|
28
28
|
|
29
29
|
def emit_bind(self, bind: BindOperation) -> None:
|
30
30
|
BindEmitter(self, allow_symbolic_size=True).emit(bind)
|
@@ -1,16 +1,16 @@
|
|
1
|
-
from typing import TYPE_CHECKING,
|
1
|
+
from typing import TYPE_CHECKING, Union
|
2
2
|
|
3
3
|
import sympy
|
4
4
|
|
5
5
|
from classiq.interface.debug_info.debug_info import FunctionDebugInfo
|
6
6
|
from classiq.interface.exceptions import ClassiqValueError
|
7
7
|
from classiq.interface.generator.expressions.expression import Expression
|
8
|
-
from classiq.interface.generator.expressions.proxies.classical.any_classical_value import (
|
9
|
-
AnyClassicalValue,
|
10
|
-
)
|
11
8
|
from classiq.interface.model.allocate import Allocate
|
12
9
|
from classiq.interface.model.handle_binding import NestedHandleBinding
|
13
|
-
from classiq.interface.model.quantum_type import
|
10
|
+
from classiq.interface.model.quantum_type import (
|
11
|
+
QuantumBitvector,
|
12
|
+
QuantumNumeric,
|
13
|
+
)
|
14
14
|
|
15
15
|
from classiq.model_expansions.evaluators.quantum_type_utils import copy_type_information
|
16
16
|
from classiq.model_expansions.quantum_operations.emitter import Emitter
|
@@ -22,10 +22,10 @@ if TYPE_CHECKING:
|
|
22
22
|
|
23
23
|
class AllocateEmitter(Emitter[Allocate]):
|
24
24
|
def __init__(
|
25
|
-
self, interpreter: "BaseInterpreter",
|
25
|
+
self, interpreter: "BaseInterpreter", allow_symbolic_attrs: bool = False
|
26
26
|
) -> None:
|
27
27
|
super().__init__(interpreter)
|
28
|
-
self.
|
28
|
+
self._allow_symbolic_attrs = allow_symbolic_attrs
|
29
29
|
|
30
30
|
def emit(self, allocate: Allocate, /) -> bool:
|
31
31
|
target: QuantumSymbol = self._interpreter.evaluate(allocate.target).as_type(
|
@@ -37,45 +37,132 @@ class AllocateEmitter(Emitter[Allocate]):
|
|
37
37
|
f"Cannot allocate partial quantum variable {str(target.handle)!r}"
|
38
38
|
)
|
39
39
|
|
40
|
-
|
40
|
+
op_update_dict: dict[str, Expression] = {}
|
41
|
+
|
42
|
+
if allocate.size is None:
|
43
|
+
if allocate.is_signed is not None or allocate.fraction_digits is not None:
|
44
|
+
raise ClassiqValueError(
|
45
|
+
"Numeric attributes cannot be specified without size"
|
46
|
+
)
|
47
|
+
self._handle_without_size(target, op_update_dict)
|
48
|
+
|
49
|
+
elif allocate.is_signed is None and allocate.fraction_digits is None:
|
50
|
+
self._handle_with_size(target, allocate.size, op_update_dict)
|
51
|
+
|
52
|
+
elif allocate.is_signed is not None and allocate.fraction_digits is not None:
|
53
|
+
self._handle_with_numeric_attrs(
|
54
|
+
target,
|
55
|
+
allocate.size,
|
56
|
+
allocate.is_signed,
|
57
|
+
allocate.fraction_digits,
|
58
|
+
op_update_dict,
|
59
|
+
)
|
60
|
+
|
61
|
+
else:
|
62
|
+
raise ClassiqValueError(
|
63
|
+
"Sign and fraction digits must be specified together"
|
64
|
+
)
|
65
|
+
|
41
66
|
if isinstance(target.quantum_type, QuantumNumeric):
|
42
67
|
target.quantum_type.set_bounds((0, 0))
|
43
|
-
|
44
|
-
|
45
|
-
size=Expression(expr=size_expr),
|
46
|
-
target=target.handle,
|
47
|
-
back_ref=allocate.uuid,
|
48
|
-
)
|
49
|
-
)
|
68
|
+
|
69
|
+
allocate = allocate.model_copy(update=op_update_dict)
|
50
70
|
self._register_debug_info(allocate)
|
51
71
|
self.emit_statement(allocate)
|
52
72
|
return True
|
53
73
|
|
54
|
-
def
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
74
|
+
def _handle_without_size(
|
75
|
+
self,
|
76
|
+
target: QuantumSymbol,
|
77
|
+
op_update_dict: dict[str, Expression],
|
78
|
+
) -> None:
|
79
|
+
if not target.quantum_type.is_evaluated:
|
80
|
+
raise ClassiqValueError(
|
81
|
+
f"Could not infer the size of variable {str(target.handle)!r}"
|
82
|
+
)
|
83
|
+
op_update_dict["size"] = Expression(expr=str(target.quantum_type.size_in_bits))
|
61
84
|
|
62
|
-
|
63
|
-
|
64
|
-
|
85
|
+
def _handle_with_size(
|
86
|
+
self,
|
87
|
+
target: QuantumSymbol,
|
88
|
+
size: Expression,
|
89
|
+
op_update_dict: dict[str, Expression],
|
90
|
+
) -> None:
|
91
|
+
size_value = self._interpret_size(size)
|
92
|
+
op_update_dict["size"] = Expression(expr=str(size_value))
|
93
|
+
|
94
|
+
if not isinstance(size_value, sympy.Basic):
|
95
|
+
copy_type_information(
|
96
|
+
QuantumBitvector(length=op_update_dict["size"]),
|
97
|
+
target.quantum_type,
|
98
|
+
str(target.handle),
|
99
|
+
)
|
100
|
+
|
101
|
+
def _handle_with_numeric_attrs(
|
102
|
+
self,
|
103
|
+
target: QuantumSymbol,
|
104
|
+
size: Expression,
|
105
|
+
is_signed: Expression,
|
106
|
+
fraction_digits: Expression,
|
107
|
+
op_update_dict: dict[str, Expression],
|
108
|
+
) -> None:
|
109
|
+
if not isinstance(target.quantum_type, QuantumNumeric):
|
110
|
+
raise ClassiqValueError(
|
111
|
+
f"Non-numeric variable {str(target.handle)!r} cannot be allocated with numeric attributes"
|
112
|
+
)
|
113
|
+
|
114
|
+
size_value = self._interpret_size(size)
|
115
|
+
op_update_dict["size"] = Expression(expr=str(size_value))
|
116
|
+
is_signed_value = self._interpret_is_signed(is_signed)
|
117
|
+
op_update_dict["is_signed"] = Expression(expr=str(is_signed_value))
|
118
|
+
fraction_digits_value = self._interpret_fraction_digits(fraction_digits)
|
119
|
+
op_update_dict["fraction_digits"] = Expression(expr=str(fraction_digits_value))
|
120
|
+
|
121
|
+
if not (
|
122
|
+
isinstance(size_value, sympy.Basic)
|
123
|
+
or isinstance(is_signed_value, sympy.Basic)
|
124
|
+
or isinstance(fraction_digits_value, sympy.Basic)
|
65
125
|
):
|
66
|
-
|
67
|
-
|
126
|
+
copy_type_information(
|
127
|
+
QuantumNumeric(
|
128
|
+
size=op_update_dict["size"],
|
129
|
+
is_signed=op_update_dict["is_signed"],
|
130
|
+
fraction_digits=op_update_dict["fraction_digits"],
|
131
|
+
),
|
132
|
+
target.quantum_type,
|
133
|
+
str(target.handle),
|
134
|
+
)
|
135
|
+
|
136
|
+
def _interpret_size(self, size: Expression) -> Union[int, float, sympy.Basic]:
|
137
|
+
size_value = self._interpreter.evaluate(size).value
|
138
|
+
if not self._allow_symbolic_attrs and not isinstance(size_value, (int, float)):
|
68
139
|
raise ClassiqValueError(
|
69
140
|
f"The number of allocated qubits must be an integer. Got "
|
70
141
|
f"{str(size_value)!r}"
|
71
142
|
)
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
143
|
+
return size_value
|
144
|
+
|
145
|
+
def _interpret_is_signed(self, is_signed: Expression) -> Union[bool, sympy.Basic]:
|
146
|
+
is_signed_value = self._interpreter.evaluate(is_signed).value
|
147
|
+
if not self._allow_symbolic_attrs and not isinstance(is_signed_value, bool):
|
148
|
+
raise ClassiqValueError(
|
149
|
+
f"The sign of a variable must be boolean. Got "
|
150
|
+
f"{str(is_signed_value)!r}"
|
151
|
+
)
|
152
|
+
return is_signed_value
|
153
|
+
|
154
|
+
def _interpret_fraction_digits(
|
155
|
+
self, fraction_digits: Expression
|
156
|
+
) -> Union[int, float, sympy.Basic]:
|
157
|
+
fraction_digits_value = self._interpreter.evaluate(fraction_digits).value
|
158
|
+
if not self._allow_symbolic_attrs and not isinstance(
|
159
|
+
fraction_digits_value, (int, float)
|
160
|
+
):
|
161
|
+
raise ClassiqValueError(
|
162
|
+
f"The fraction digits of a variable must be an integer. Got "
|
163
|
+
f"{str(fraction_digits_value)!r}"
|
164
|
+
)
|
165
|
+
return fraction_digits_value
|
79
166
|
|
80
167
|
def _register_debug_info(self, allocate: Allocate) -> None:
|
81
168
|
if (
|