classiq 0.43.2__py3-none-any.whl → 0.44.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 +7 -1
- classiq/_internals/client.py +4 -7
- classiq/_internals/host_checker.py +34 -12
- classiq/_internals/jobs.py +2 -2
- classiq/applications/chemistry/chemistry_model_constructor.py +12 -6
- classiq/applications/combinatorial_helpers/allowed_constraints.py +4 -1
- classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +1 -1
- classiq/applications/finance/finance_model_constructor.py +3 -2
- classiq/applications/grover/grover_model_constructor.py +7 -5
- classiq/applications/hamiltonian/__init__.py +0 -0
- classiq/applications/hamiltonian/pauli_decomposition.py +113 -0
- classiq/applications/qnn/qlayer.py +1 -1
- classiq/exceptions.py +4 -0
- classiq/interface/_version.py +1 -1
- classiq/interface/ast_node.py +1 -18
- classiq/interface/backend/backend_preferences.py +15 -16
- classiq/interface/backend/ionq/ionq_quantum_program.py +1 -1
- classiq/interface/backend/pydantic_backend.py +0 -5
- classiq/interface/backend/quantum_backend_providers.py +3 -2
- classiq/interface/chemistry/operator.py +5 -1
- classiq/interface/debug_info/__init__.py +0 -0
- classiq/interface/debug_info/debug_info.py +32 -0
- classiq/interface/executor/execution_preferences.py +1 -45
- classiq/interface/executor/result.py +25 -12
- classiq/interface/generator/application_apis/arithmetic_declarations.py +8 -5
- classiq/interface/generator/application_apis/chemistry_declarations.py +78 -60
- classiq/interface/generator/application_apis/combinatorial_optimization_declarations.py +19 -10
- classiq/interface/generator/application_apis/entangler_declarations.py +11 -6
- classiq/interface/generator/application_apis/finance_declarations.py +36 -22
- classiq/interface/generator/application_apis/qsvm_declarations.py +21 -15
- classiq/interface/generator/arith/arithmetic_expression_abc.py +21 -1
- classiq/interface/generator/arith/binary_ops.py +5 -4
- classiq/interface/generator/arith/extremum_operations.py +43 -19
- classiq/interface/generator/constant.py +1 -1
- classiq/interface/generator/expressions/atomic_expression_functions.py +1 -0
- classiq/interface/generator/expressions/expression_constants.py +3 -1
- classiq/interface/generator/expressions/qmod_qarray_proxy.py +52 -66
- classiq/interface/generator/expressions/qmod_qstruct_proxy.py +35 -0
- classiq/interface/generator/expressions/sympy_supported_expressions.py +2 -1
- classiq/interface/generator/functions/builtins/core_library/__init__.py +4 -2
- classiq/interface/generator/functions/builtins/core_library/atomic_quantum_functions.py +41 -41
- classiq/interface/generator/functions/builtins/core_library/exponentiation_functions.py +52 -42
- classiq/interface/generator/functions/builtins/open_lib_functions.py +1095 -3347
- classiq/interface/generator/functions/builtins/quantum_operators.py +9 -22
- classiq/interface/generator/functions/classical_function_declaration.py +14 -6
- classiq/interface/generator/functions/classical_type.py +7 -76
- classiq/interface/generator/functions/concrete_types.py +55 -0
- classiq/interface/generator/functions/function_declaration.py +10 -10
- classiq/interface/generator/functions/type_name.py +104 -0
- classiq/interface/generator/generated_circuit_data.py +3 -3
- classiq/interface/generator/model/model.py +11 -0
- classiq/interface/generator/model/preferences/preferences.py +5 -0
- classiq/interface/generator/quantum_function_call.py +3 -0
- classiq/interface/generator/quantum_program.py +2 -2
- classiq/interface/generator/register_role.py +7 -1
- classiq/interface/generator/synthesis_metadata/synthesis_execution_data.py +1 -3
- classiq/interface/generator/types/builtin_struct_declarations/pauli_struct_declarations.py +1 -2
- classiq/interface/generator/types/qstruct_declaration.py +17 -0
- classiq/interface/generator/types/struct_declaration.py +1 -1
- classiq/interface/helpers/validation_helpers.py +1 -17
- classiq/interface/ide/visual_model.py +9 -2
- classiq/interface/interface_version.py +1 -0
- classiq/interface/model/bind_operation.py +25 -5
- classiq/interface/model/classical_parameter_declaration.py +8 -5
- classiq/interface/model/control.py +5 -5
- classiq/interface/model/handle_binding.py +185 -12
- classiq/interface/model/inplace_binary_operation.py +16 -4
- classiq/interface/model/model.py +28 -5
- classiq/interface/model/native_function_definition.py +8 -4
- classiq/interface/model/parameter.py +14 -0
- classiq/interface/model/port_declaration.py +20 -2
- classiq/interface/model/quantum_expressions/amplitude_loading_operation.py +21 -6
- classiq/interface/model/quantum_expressions/arithmetic_operation.py +30 -6
- classiq/interface/model/quantum_expressions/quantum_expression.py +4 -9
- classiq/interface/model/quantum_function_call.py +135 -192
- classiq/interface/model/quantum_function_declaration.py +147 -165
- classiq/interface/model/quantum_lambda_function.py +24 -6
- classiq/interface/model/quantum_statement.py +34 -8
- classiq/interface/model/quantum_type.py +61 -10
- classiq/interface/model/quantum_variable_declaration.py +1 -1
- classiq/interface/model/statement_block.py +2 -0
- classiq/interface/model/validation_handle.py +7 -0
- classiq/interface/server/global_versions.py +4 -4
- classiq/interface/server/routes.py +2 -0
- classiq/interface/source_reference.py +59 -0
- classiq/qmod/__init__.py +2 -3
- classiq/qmod/builtins/functions.py +39 -11
- classiq/qmod/builtins/operations.py +171 -40
- classiq/qmod/declaration_inferrer.py +99 -56
- classiq/qmod/expression_query.py +1 -1
- classiq/qmod/model_state_container.py +2 -0
- classiq/qmod/native/pretty_printer.py +71 -53
- classiq/qmod/pretty_print/pretty_printer.py +98 -52
- classiq/qmod/qfunc.py +11 -5
- classiq/qmod/qmod_parameter.py +1 -2
- classiq/qmod/qmod_variable.py +364 -172
- classiq/qmod/quantum_callable.py +3 -3
- classiq/qmod/quantum_expandable.py +119 -65
- classiq/qmod/quantum_function.py +15 -3
- classiq/qmod/semantics/annotation.py +12 -13
- classiq/qmod/semantics/error_manager.py +36 -10
- classiq/qmod/semantics/static_semantics_visitor.py +163 -75
- classiq/qmod/semantics/validation/func_call_validation.py +42 -96
- classiq/qmod/semantics/validation/handle_validation.py +85 -0
- classiq/qmod/semantics/validation/types_validation.py +108 -1
- classiq/qmod/type_attribute_remover.py +32 -0
- classiq/qmod/utilities.py +26 -5
- {classiq-0.43.2.dist-info → classiq-0.44.0.dist-info}/METADATA +3 -3
- {classiq-0.43.2.dist-info → classiq-0.44.0.dist-info}/RECORD +111 -99
- classiq/qmod/qmod_struct.py +0 -13
- /classiq/{interface/ide/show.py → show.py} +0 -0
- {classiq-0.43.2.dist-info → classiq-0.44.0.dist-info}/WHEEL +0 -0
classiq/qmod/quantum_callable.py
CHANGED
@@ -12,13 +12,13 @@ from typing import ( # type: ignore[attr-defined]
|
|
12
12
|
|
13
13
|
from typing_extensions import ParamSpec
|
14
14
|
|
15
|
-
from classiq.interface.ast_node import SourceReference
|
16
15
|
from classiq.interface.model.quantum_function_call import QuantumFunctionCall
|
17
16
|
from classiq.interface.model.quantum_function_declaration import (
|
18
|
-
|
17
|
+
AnonQuantumFunctionDeclaration,
|
19
18
|
)
|
20
19
|
from classiq.interface.model.quantum_statement import QuantumStatement
|
21
20
|
from classiq.interface.model.quantum_type import QuantumType
|
21
|
+
from classiq.interface.source_reference import SourceReference
|
22
22
|
|
23
23
|
from classiq.qmod.qmod_parameter import CInt
|
24
24
|
from classiq.qmod.utilities import get_source_ref
|
@@ -58,7 +58,7 @@ class QCallable(Generic[P], ABC):
|
|
58
58
|
|
59
59
|
@property
|
60
60
|
@abstractmethod
|
61
|
-
def func_decl(self) ->
|
61
|
+
def func_decl(self) -> AnonQuantumFunctionDeclaration:
|
62
62
|
raise NotImplementedError
|
63
63
|
|
64
64
|
# Support comma-separated generic args in older Python versions
|
@@ -20,24 +20,22 @@ from typing import (
|
|
20
20
|
from sympy import Basic
|
21
21
|
from typing_extensions import Self
|
22
22
|
|
23
|
-
from classiq.interface.ast_node import SourceReference
|
24
23
|
from classiq.interface.generator.expressions.expression import Expression
|
25
|
-
from classiq.interface.generator.functions.
|
26
|
-
PythonClassicalTypes,
|
27
|
-
)
|
24
|
+
from classiq.interface.generator.functions.concrete_types import PythonClassicalTypes
|
28
25
|
from classiq.interface.model.classical_parameter_declaration import (
|
29
|
-
|
26
|
+
AnonClassicalParameterDeclaration,
|
30
27
|
)
|
31
|
-
from classiq.interface.model.port_declaration import
|
28
|
+
from classiq.interface.model.port_declaration import AnonPortDeclaration
|
32
29
|
from classiq.interface.model.quantum_function_call import (
|
33
30
|
ArgValue,
|
34
31
|
OperandIdentifier,
|
35
32
|
QuantumFunctionCall,
|
36
33
|
)
|
37
34
|
from classiq.interface.model.quantum_function_declaration import (
|
38
|
-
|
35
|
+
AnonPositionalArg,
|
36
|
+
AnonQuantumFunctionDeclaration,
|
37
|
+
AnonQuantumOperandDeclaration,
|
39
38
|
QuantumFunctionDeclaration,
|
40
|
-
QuantumOperandDeclaration,
|
41
39
|
)
|
42
40
|
from classiq.interface.model.quantum_lambda_function import QuantumLambdaFunction
|
43
41
|
from classiq.interface.model.quantum_statement import QuantumStatement
|
@@ -45,14 +43,20 @@ from classiq.interface.model.quantum_type import QuantumType
|
|
45
43
|
from classiq.interface.model.variable_declaration_statement import (
|
46
44
|
VariableDeclarationStatement,
|
47
45
|
)
|
46
|
+
from classiq.interface.source_reference import SourceReference
|
48
47
|
|
49
48
|
from classiq.exceptions import ClassiqValueError
|
50
49
|
from classiq.qmod.model_state_container import QMODULE, ModelStateContainer
|
51
50
|
from classiq.qmod.qmod_constant import QConstant
|
52
51
|
from classiq.qmod.qmod_parameter import CInt, CParam, CParamScalar, create_param
|
53
|
-
from classiq.qmod.qmod_variable import
|
52
|
+
from classiq.qmod.qmod_variable import (
|
53
|
+
QVar,
|
54
|
+
create_qvar_for_port_decl,
|
55
|
+
set_symbolic_qvar_properties,
|
56
|
+
)
|
54
57
|
from classiq.qmod.quantum_callable import QCallable, QExpandableInterface
|
55
58
|
from classiq.qmod.symbolic_expr import SymbolicExpr
|
59
|
+
from classiq.qmod.type_attribute_remover import decl_without_type_attributes
|
56
60
|
from classiq.qmod.utilities import mangle_keyword, qmod_val_to_expr_str
|
57
61
|
|
58
62
|
ArgType = Union[CParam, QVar, QCallable]
|
@@ -89,11 +93,11 @@ class QExpandable(QCallable, QExpandableInterface, ABC):
|
|
89
93
|
|
90
94
|
def expand(self) -> None:
|
91
95
|
if self not in QExpandable.STACK:
|
92
|
-
with self:
|
96
|
+
with self, set_symbolic_qvar_properties(True):
|
93
97
|
self._py_callable(*self._get_positional_args())
|
94
98
|
|
95
|
-
def infer_rename_params(self) ->
|
96
|
-
return
|
99
|
+
def infer_rename_params(self) -> Optional[List[str]]:
|
100
|
+
return None
|
97
101
|
|
98
102
|
def add_local_handle(
|
99
103
|
self,
|
@@ -112,60 +116,94 @@ class QExpandable(QCallable, QExpandableInterface, ABC):
|
|
112
116
|
|
113
117
|
def _get_positional_args(self) -> List[ArgType]:
|
114
118
|
result: List[ArgType] = []
|
115
|
-
for arg in self.func_decl.
|
116
|
-
|
117
|
-
actual_name =
|
118
|
-
|
119
|
+
for idx, arg in enumerate(self.func_decl.positional_arg_declarations):
|
120
|
+
rename_params = self.infer_rename_params()
|
121
|
+
actual_name = (
|
122
|
+
rename_params[idx] if rename_params is not None else arg.get_name()
|
123
|
+
)
|
124
|
+
if isinstance(arg, AnonClassicalParameterDeclaration):
|
119
125
|
result.append(
|
120
126
|
create_param(actual_name, arg.classical_type, self._qmodule)
|
121
127
|
)
|
122
|
-
elif isinstance(arg,
|
128
|
+
elif isinstance(arg, AnonPortDeclaration):
|
123
129
|
result.append(create_qvar_for_port_decl(arg, actual_name))
|
124
130
|
else:
|
125
|
-
assert isinstance(arg,
|
126
|
-
result.append(QTerminalCallable(arg))
|
131
|
+
assert isinstance(arg, AnonQuantumOperandDeclaration)
|
132
|
+
result.append(QTerminalCallable(arg, idx))
|
127
133
|
return result
|
128
134
|
|
129
135
|
def create_quantum_function_call(
|
130
136
|
self, source_ref_: SourceReference, *args: Any, **kwargs: Any
|
131
137
|
) -> QuantumFunctionCall:
|
138
|
+
func_decl = self.func_decl
|
139
|
+
if not isinstance(func_decl, QuantumFunctionDeclaration):
|
140
|
+
raise NotImplementedError
|
132
141
|
return _create_quantum_function_call(
|
133
|
-
|
142
|
+
func_decl, None, source_ref_, *args, **kwargs
|
134
143
|
)
|
135
144
|
|
136
145
|
|
137
146
|
class QLambdaFunction(QExpandable):
|
138
|
-
def __init__(
|
147
|
+
def __init__(
|
148
|
+
self, decl: AnonQuantumFunctionDeclaration, py_callable: Callable
|
149
|
+
) -> None:
|
139
150
|
py_callable.__annotations__.pop("return", None)
|
140
151
|
super().__init__(py_callable)
|
141
152
|
self._decl = decl
|
142
153
|
|
143
154
|
@property
|
144
|
-
def func_decl(self) ->
|
155
|
+
def func_decl(self) -> AnonQuantumFunctionDeclaration:
|
145
156
|
return self._decl
|
146
157
|
|
147
|
-
def infer_rename_params(self) ->
|
148
|
-
|
149
|
-
decl_params = self.func_decl.get_positional_arg_decls()
|
150
|
-
return {
|
151
|
-
decl_param.name: py_param
|
152
|
-
for decl_param, py_param in zip(decl_params, py_params.args)
|
153
|
-
if decl_param.name != py_param
|
154
|
-
}
|
158
|
+
def infer_rename_params(self) -> List[str]:
|
159
|
+
return inspect.getfullargspec(self._py_callable).args
|
155
160
|
|
156
161
|
|
157
162
|
class QTerminalCallable(QCallable):
|
163
|
+
@overload
|
158
164
|
def __init__(
|
159
165
|
self,
|
160
166
|
decl: QuantumFunctionDeclaration,
|
167
|
+
param_idx: Optional[int] = None,
|
161
168
|
index_: Optional[Union[int, CParamScalar]] = None,
|
162
169
|
) -> None:
|
163
|
-
|
170
|
+
pass
|
171
|
+
|
172
|
+
@overload
|
173
|
+
def __init__(
|
174
|
+
self,
|
175
|
+
decl: AnonQuantumFunctionDeclaration,
|
176
|
+
param_idx: int,
|
177
|
+
index_: Optional[Union[int, CParamScalar]] = None,
|
178
|
+
) -> None:
|
179
|
+
pass
|
180
|
+
|
181
|
+
def __init__(
|
182
|
+
self,
|
183
|
+
decl: AnonQuantumFunctionDeclaration,
|
184
|
+
param_idx: Optional[int] = None,
|
185
|
+
index_: Optional[Union[int, CParamScalar]] = None,
|
186
|
+
) -> None:
|
187
|
+
self._decl = self._override_decl_name(decl, param_idx)
|
164
188
|
self._index = index_
|
165
189
|
|
190
|
+
@staticmethod
|
191
|
+
def _override_decl_name(
|
192
|
+
decl: AnonQuantumFunctionDeclaration, param_idx: Optional[int]
|
193
|
+
) -> QuantumFunctionDeclaration:
|
194
|
+
if (
|
195
|
+
not isinstance(QCallable.CURRENT_EXPANDABLE, QLambdaFunction)
|
196
|
+
or param_idx is None
|
197
|
+
):
|
198
|
+
return decl.rename(decl.get_name())
|
199
|
+
rename_params = QCallable.CURRENT_EXPANDABLE.infer_rename_params()
|
200
|
+
return decl.rename(new_name=rename_params[param_idx])
|
201
|
+
|
166
202
|
@property
|
167
203
|
def is_list(self) -> bool:
|
168
|
-
return
|
204
|
+
return (
|
205
|
+
isinstance(self._decl, AnonQuantumOperandDeclaration) and self._decl.is_list
|
206
|
+
)
|
169
207
|
|
170
208
|
def __getitem__(self, key: Union[slice, int, CInt]) -> "QTerminalCallable":
|
171
209
|
if not self.is_list:
|
@@ -174,7 +212,7 @@ class QTerminalCallable(QCallable):
|
|
174
212
|
raise NotImplementedError("Operand lists don't support slicing")
|
175
213
|
if isinstance(key, CParam) and not isinstance(key, CParamScalar):
|
176
214
|
raise ClassiqValueError("Non-classical parameter for slicing")
|
177
|
-
return QTerminalCallable(self._decl, key)
|
215
|
+
return QTerminalCallable(self._decl, index_=key)
|
178
216
|
|
179
217
|
def __len__(self) -> int:
|
180
218
|
raise ClassiqValueError(
|
@@ -212,43 +250,54 @@ class QTerminalCallable(QCallable):
|
|
212
250
|
|
213
251
|
@overload
|
214
252
|
def prepare_arg(
|
215
|
-
arg_decl:
|
253
|
+
arg_decl: AnonPositionalArg,
|
254
|
+
val: Union[QCallable, Callable[..., None]],
|
255
|
+
func_name: Optional[str],
|
256
|
+
param_name: str,
|
216
257
|
) -> QuantumLambdaFunction: ...
|
217
258
|
|
218
259
|
|
219
260
|
@overload
|
220
|
-
def prepare_arg(
|
261
|
+
def prepare_arg(
|
262
|
+
arg_decl: AnonPositionalArg, val: Any, func_name: Optional[str], param_name: str
|
263
|
+
) -> ArgValue: ...
|
221
264
|
|
222
265
|
|
223
|
-
def prepare_arg(
|
266
|
+
def prepare_arg(
|
267
|
+
arg_decl: AnonPositionalArg, val: Any, func_name: Optional[str], param_name: str
|
268
|
+
) -> ArgValue:
|
224
269
|
if isinstance(val, QConstant):
|
225
270
|
val.add_to_model()
|
226
271
|
return Expression(expr=str(val.name))
|
227
|
-
if isinstance(arg_decl,
|
272
|
+
if isinstance(arg_decl, AnonClassicalParameterDeclaration):
|
228
273
|
_validate_classical_arg(val, arg_decl, func_name)
|
229
274
|
return Expression(expr=qmod_val_to_expr_str(val))
|
230
|
-
elif isinstance(arg_decl,
|
275
|
+
elif isinstance(arg_decl, AnonPortDeclaration):
|
231
276
|
if not isinstance(val, QVar):
|
277
|
+
func_name_message = (
|
278
|
+
"" if func_name is None else f" of function {func_name!r}"
|
279
|
+
)
|
232
280
|
raise ClassiqValueError(
|
233
|
-
f"Argument {str(val)!r} to parameter {
|
234
|
-
f"
|
281
|
+
f"Argument {str(val)!r} to parameter {param_name!r}{func_name_message} "
|
282
|
+
f"has incompatible type; expected quantum variable"
|
235
283
|
)
|
236
284
|
return val.get_handle_binding()
|
237
285
|
else:
|
238
286
|
if isinstance(val, list):
|
239
287
|
if not all(isinstance(v, QCallable) or callable(v) for v in val):
|
240
288
|
raise ClassiqValueError(
|
241
|
-
f"Quantum operand {
|
289
|
+
f"Quantum operand {param_name!r} cannot be initialized with a "
|
242
290
|
f"list of non-callables"
|
243
291
|
)
|
244
292
|
val = cast(List[Union[QCallable, Callable[[Any], None]]], val)
|
245
|
-
return [prepare_arg(arg_decl, v, func_name) for v in val]
|
293
|
+
return [prepare_arg(arg_decl, v, func_name, param_name) for v in val]
|
246
294
|
|
247
295
|
if not isinstance(val, QCallable):
|
248
|
-
|
296
|
+
new_arg_decl = decl_without_type_attributes(arg_decl)
|
297
|
+
val = QLambdaFunction(new_arg_decl, val)
|
249
298
|
val.expand()
|
250
299
|
return QuantumLambdaFunction(
|
251
|
-
|
300
|
+
pos_rename_params=val.infer_rename_params(),
|
252
301
|
body=val.body,
|
253
302
|
)
|
254
303
|
|
@@ -258,7 +307,7 @@ def prepare_arg(arg_decl: PositionalArg, val: Any, func_name: str) -> ArgValue:
|
|
258
307
|
|
259
308
|
|
260
309
|
def _validate_classical_arg(
|
261
|
-
arg: Any, arg_decl:
|
310
|
+
arg: Any, arg_decl: AnonClassicalParameterDeclaration, func_name: Optional[str]
|
262
311
|
) -> None:
|
263
312
|
if (
|
264
313
|
not isinstance(
|
@@ -268,15 +317,16 @@ def _validate_classical_arg(
|
|
268
317
|
or isinstance(arg, SymbolicExpr)
|
269
318
|
and arg.is_quantum
|
270
319
|
):
|
320
|
+
func_name_message = "" if func_name is None else f" of function {func_name!r}"
|
271
321
|
raise ClassiqValueError(
|
272
|
-
f"Argument {str(arg)!r} to parameter {arg_decl.name!r}
|
273
|
-
f"
|
322
|
+
f"Argument {str(arg)!r} to parameter {arg_decl.name!r}{func_name_message} "
|
323
|
+
f"has incompatible type; expected "
|
274
324
|
f"{arg_decl.classical_type.qmod_type.__name__}"
|
275
325
|
)
|
276
326
|
|
277
327
|
|
278
328
|
def _get_operand_hint_args(
|
279
|
-
func:
|
329
|
+
func: AnonQuantumFunctionDeclaration, param: AnonPositionalArg, param_value: str
|
280
330
|
) -> str:
|
281
331
|
return ", ".join(
|
282
332
|
[
|
@@ -285,39 +335,43 @@ def _get_operand_hint_args(
|
|
285
335
|
if decl.name == param.name
|
286
336
|
else f"{decl.name}=..."
|
287
337
|
)
|
288
|
-
for decl in func.
|
338
|
+
for decl in func.positional_arg_declarations
|
289
339
|
]
|
290
340
|
)
|
291
341
|
|
292
342
|
|
293
|
-
def _get_operand_hint(
|
343
|
+
def _get_operand_hint(
|
344
|
+
func: AnonQuantumFunctionDeclaration, param: AnonPositionalArg
|
345
|
+
) -> str:
|
294
346
|
return (
|
295
|
-
f"\nHint: To
|
296
|
-
f"
|
297
|
-
f"
|
298
|
-
f"
|
299
|
-
f"or a quantum function "
|
300
|
-
f"`{func.name}({_get_operand_hint_args(func, param, 'my_func')})`"
|
347
|
+
f"\nHint: To call a function under {func.name!r} use a lambda function as in "
|
348
|
+
f"'{func.name}({_get_operand_hint_args(func, param, 'lambda: f(q)')})' "
|
349
|
+
f"or pass the quantum function directly as in "
|
350
|
+
f"'{func.name}({_get_operand_hint_args(func, param, 'f')})'."
|
301
351
|
)
|
302
352
|
|
303
353
|
|
304
354
|
def _prepare_args(
|
305
|
-
decl:
|
355
|
+
decl: AnonQuantumFunctionDeclaration, arg_list: List[Any], kwargs: Dict[str, Any]
|
306
356
|
) -> List[ArgValue]:
|
307
357
|
result = []
|
308
|
-
for arg_decl in decl.
|
358
|
+
for idx, arg_decl in enumerate(decl.positional_arg_declarations):
|
359
|
+
arg = None
|
309
360
|
if arg_list:
|
310
361
|
arg = arg_list.pop(0)
|
311
|
-
|
362
|
+
elif arg_decl.name is not None:
|
312
363
|
arg = kwargs.pop(mangle_keyword(arg_decl.name), None)
|
313
364
|
if arg is None:
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
365
|
+
if arg_decl.name is not None:
|
366
|
+
param_name = repr(arg_decl.name)
|
367
|
+
else:
|
368
|
+
param_name = f"#{idx + 1}"
|
369
|
+
error_message = f"Missing required argument for parameter {param_name}"
|
370
|
+
if isinstance(arg_decl, AnonQuantumOperandDeclaration):
|
318
371
|
error_message += _get_operand_hint(decl, arg_decl)
|
319
372
|
raise ClassiqValueError(error_message)
|
320
|
-
|
373
|
+
param_name = arg_decl.name if arg_decl.name is not None else f"#{idx + 1}"
|
374
|
+
result.append(prepare_arg(arg_decl, arg, decl.name, param_name))
|
321
375
|
|
322
376
|
return result
|
323
377
|
|
@@ -329,7 +383,7 @@ def _create_quantum_function_call(
|
|
329
383
|
*args: Any,
|
330
384
|
**kwargs: Any,
|
331
385
|
) -> QuantumFunctionCall:
|
332
|
-
arg_decls = decl_.
|
386
|
+
arg_decls = decl_.positional_arg_declarations
|
333
387
|
arg_list = list(args)
|
334
388
|
prepared_args = _prepare_args(decl_, arg_list, kwargs)
|
335
389
|
|
classiq/qmod/quantum_function.py
CHANGED
@@ -11,12 +11,14 @@ from classiq.interface.generator.model.preferences.preferences import Preference
|
|
11
11
|
from classiq.interface.model.model import Model, SerializedModel
|
12
12
|
from classiq.interface.model.native_function_definition import NativeFunctionDefinition
|
13
13
|
from classiq.interface.model.quantum_function_declaration import (
|
14
|
+
NamedParamsQuantumFunctionDeclaration,
|
14
15
|
QuantumFunctionDeclaration,
|
15
16
|
)
|
16
17
|
|
17
18
|
from classiq.exceptions import ClassiqError
|
18
19
|
from classiq.qmod.classical_function import CFunc
|
19
20
|
from classiq.qmod.declaration_inferrer import infer_func_decl
|
21
|
+
from classiq.qmod.model_state_container import QMODULE
|
20
22
|
from classiq.qmod.qmod_constant import QConstant
|
21
23
|
from classiq.qmod.qmod_parameter import CArray, CParam
|
22
24
|
from classiq.qmod.qmod_variable import QVar
|
@@ -25,7 +27,7 @@ from classiq.qmod.quantum_expandable import QExpandable, QTerminalCallable
|
|
25
27
|
from classiq.qmod.utilities import mangle_keyword, unmangle_keyword
|
26
28
|
|
27
29
|
|
28
|
-
def _lookup_qfunc(name: str) -> Optional[
|
30
|
+
def _lookup_qfunc(name: str) -> Optional[NamedParamsQuantumFunctionDeclaration]:
|
29
31
|
# FIXME: to be generalized to existing user-defined functions
|
30
32
|
return QuantumFunctionDeclaration.BUILTIN_FUNCTION_DECLARATIONS.get(name)
|
31
33
|
|
@@ -55,7 +57,7 @@ class QFunc(QExpandable):
|
|
55
57
|
functools.update_wrapper(self, py_callable)
|
56
58
|
|
57
59
|
@property
|
58
|
-
def func_decl(self) ->
|
60
|
+
def func_decl(self) -> NamedParamsQuantumFunctionDeclaration:
|
59
61
|
return self._qmodule.native_defs.get(
|
60
62
|
self._py_callable.__name__,
|
61
63
|
infer_func_decl(self._py_callable, qmodule=self._qmodule),
|
@@ -74,6 +76,7 @@ class QFunc(QExpandable):
|
|
74
76
|
) -> Model:
|
75
77
|
self._qmodule.enum_decls = dict()
|
76
78
|
self._qmodule.type_decls = dict()
|
79
|
+
self._qmodule.qstruct_decls = dict()
|
77
80
|
self._qmodule.native_defs = dict()
|
78
81
|
self._qmodule.constants = dict()
|
79
82
|
QConstant.set_current_model(self._qmodule)
|
@@ -93,6 +96,7 @@ class QFunc(QExpandable):
|
|
93
96
|
functions=list(self._qmodule.native_defs.values()),
|
94
97
|
enums=list(self._qmodule.enum_decls.values()),
|
95
98
|
types=list(self._qmodule.type_decls.values()),
|
99
|
+
qstructs=list(self._qmodule.qstruct_decls.values()),
|
96
100
|
**{key: value for key, value in model_extra_settings if value},
|
97
101
|
)
|
98
102
|
|
@@ -138,7 +142,7 @@ class ExternalQFunc(QTerminalCallable):
|
|
138
142
|
|
139
143
|
py_callable.__annotations__.pop("return", None)
|
140
144
|
if py_callable.__annotations__.keys() != {
|
141
|
-
mangle_keyword(arg.name) for arg in decl.
|
145
|
+
mangle_keyword(arg.name) for arg in decl.positional_arg_declarations
|
142
146
|
}:
|
143
147
|
raise ClassiqError(
|
144
148
|
f"Parameter type hints for {py_callable.__name__!r} do not match imported declaration"
|
@@ -147,6 +151,14 @@ class ExternalQFunc(QTerminalCallable):
|
|
147
151
|
functools.update_wrapper(self, py_callable)
|
148
152
|
|
149
153
|
|
154
|
+
class GenerativeQFunc(QTerminalCallable):
|
155
|
+
func_decl: NamedParamsQuantumFunctionDeclaration
|
156
|
+
|
157
|
+
def __init__(self, py_callable: Callable) -> None:
|
158
|
+
super().__init__(infer_func_decl(py_callable, QMODULE))
|
159
|
+
self.py_callable = py_callable
|
160
|
+
|
161
|
+
|
150
162
|
ILLEGAL_PARAM_ERROR = "Unsupported type hint '{annotation}' for argument '{name}'."
|
151
163
|
|
152
164
|
|
@@ -1,13 +1,12 @@
|
|
1
|
-
from typing import
|
1
|
+
from typing import Mapping
|
2
2
|
|
3
3
|
from classiq.interface.model.quantum_function_call import QuantumFunctionCall
|
4
4
|
from classiq.interface.model.quantum_function_declaration import (
|
5
|
+
AnonQuantumOperandDeclaration,
|
5
6
|
QuantumFunctionDeclaration,
|
6
7
|
)
|
7
8
|
from classiq.interface.model.quantum_lambda_function import (
|
8
|
-
QuantumCallable,
|
9
9
|
QuantumLambdaFunction,
|
10
|
-
QuantumOperand,
|
11
10
|
)
|
12
11
|
|
13
12
|
from classiq.exceptions import ClassiqError
|
@@ -25,14 +24,14 @@ def annotate_function_call_decl(
|
|
25
24
|
)
|
26
25
|
fc.set_func_decl(func_decl)
|
27
26
|
|
28
|
-
for
|
29
|
-
|
30
|
-
|
27
|
+
for arg, param in zip(fc.positional_args, fc.func_decl.positional_arg_declarations):
|
28
|
+
if not isinstance(param, AnonQuantumOperandDeclaration):
|
29
|
+
continue
|
30
|
+
args: list
|
31
|
+
if isinstance(arg, list):
|
32
|
+
args = arg
|
33
|
+
else:
|
34
|
+
args = [arg]
|
35
|
+
for qlambda in args:
|
31
36
|
if isinstance(qlambda, QuantumLambdaFunction):
|
32
|
-
qlambda.set_op_decl(
|
33
|
-
|
34
|
-
|
35
|
-
def _get_lambda_defs(operand: QuantumOperand) -> List[QuantumCallable]:
|
36
|
-
if isinstance(operand, list):
|
37
|
-
return operand
|
38
|
-
return [operand]
|
37
|
+
qlambda.set_op_decl(param)
|
@@ -1,7 +1,8 @@
|
|
1
1
|
from contextlib import contextmanager
|
2
|
-
from typing import Iterator, List, Type
|
2
|
+
from typing import Iterator, List, Optional, Type
|
3
3
|
|
4
4
|
from classiq.interface.ast_node import ASTNode
|
5
|
+
from classiq.interface.source_reference import SourceReferencedError
|
5
6
|
|
6
7
|
|
7
8
|
class ErrorManager:
|
@@ -14,19 +15,28 @@ class ErrorManager:
|
|
14
15
|
if hasattr(self, "_instantiated"):
|
15
16
|
return
|
16
17
|
self._instantiated = True
|
17
|
-
self._errors: List[
|
18
|
+
self._errors: List[SourceReferencedError] = []
|
18
19
|
self._current_nodes_stack: List[ASTNode] = []
|
20
|
+
self._call_stack: List[str] = []
|
21
|
+
|
22
|
+
@property
|
23
|
+
def annotated_errors(self) -> List[str]:
|
24
|
+
return [str(error) for error in self._errors]
|
19
25
|
|
20
26
|
def add_error(self, error: str) -> None:
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
27
|
+
self._errors.append(
|
28
|
+
SourceReferencedError(
|
29
|
+
error=error,
|
30
|
+
source_ref=(
|
31
|
+
self._current_nodes_stack[-1].source_ref
|
32
|
+
if self._current_nodes_stack
|
33
|
+
else None
|
34
|
+
),
|
35
|
+
function=self.current_function,
|
36
|
+
)
|
26
37
|
)
|
27
|
-
self._errors.append(source_referenced_error)
|
28
38
|
|
29
|
-
def get_errors(self) -> List[
|
39
|
+
def get_errors(self) -> List[SourceReferencedError]:
|
30
40
|
return self._errors
|
31
41
|
|
32
42
|
def clear(self) -> None:
|
@@ -38,12 +48,28 @@ class ErrorManager:
|
|
38
48
|
|
39
49
|
def report_errors(self, error_type: Type[Exception]) -> None:
|
40
50
|
if self.has_errors():
|
41
|
-
errors = self.
|
51
|
+
errors = self.annotated_errors
|
42
52
|
self.clear()
|
43
53
|
raise error_type("\n\t" + "\n\t".join(errors))
|
44
54
|
|
55
|
+
@property
|
56
|
+
def current_function(self) -> Optional[str]:
|
57
|
+
return self._call_stack[-1] if self._call_stack else None
|
58
|
+
|
45
59
|
@contextmanager
|
46
60
|
def node_context(self, node: ASTNode) -> Iterator[None]:
|
47
61
|
self._current_nodes_stack.append(node)
|
48
62
|
yield
|
49
63
|
self._current_nodes_stack.pop()
|
64
|
+
|
65
|
+
@contextmanager
|
66
|
+
def call(self, func_name: str) -> Iterator[None]:
|
67
|
+
self._call_stack.append(func_name)
|
68
|
+
yield
|
69
|
+
self._call_stack.pop()
|
70
|
+
|
71
|
+
|
72
|
+
def append_error(node: ASTNode, message: str) -> None:
|
73
|
+
instance = ErrorManager()
|
74
|
+
with instance.node_context(node):
|
75
|
+
instance.add_error(message)
|