classiq 0.36.0__py3-none-any.whl → 0.37.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 +1 -0
- classiq/_internals/api_wrapper.py +24 -6
- classiq/_internals/authentication/device.py +6 -3
- classiq/_internals/authentication/token_manager.py +21 -5
- classiq/_internals/client.py +7 -2
- classiq/_internals/config.py +12 -0
- classiq/_internals/host_checker.py +1 -1
- classiq/_internals/jobs.py +3 -1
- classiq/_internals/type_validation.py +3 -6
- classiq/analyzer/analyzer.py +1 -0
- classiq/analyzer/rb.py +3 -5
- classiq/applications_model_constructors/chemistry_model_constructor.py +42 -67
- classiq/applications_model_constructors/grover_model_constructor.py +27 -18
- classiq/exceptions.py +5 -0
- classiq/execution/jobs.py +13 -4
- classiq/executor.py +3 -2
- classiq/interface/_version.py +1 -1
- classiq/interface/analyzer/analysis_params.py +0 -6
- classiq/interface/analyzer/result.py +0 -4
- classiq/interface/backend/backend_preferences.py +2 -2
- classiq/interface/backend/quantum_backend_providers.py +1 -1
- classiq/interface/execution/resource_estimator.py +7 -0
- classiq/interface/execution/result.py +5 -0
- classiq/interface/executor/register_initialization.py +3 -1
- classiq/interface/executor/vqe_result.py +1 -0
- classiq/interface/generator/ansatz_library.py +3 -3
- classiq/interface/generator/arith/argument_utils.py +4 -4
- classiq/interface/generator/arith/arithmetic.py +4 -2
- classiq/interface/generator/arith/arithmetic_arg_type_validator.py +11 -5
- classiq/interface/generator/arith/arithmetic_expression_parser.py +8 -7
- classiq/interface/generator/arith/arithmetic_operations.py +7 -0
- classiq/interface/generator/arith/arithmetic_param_getters.py +97 -16
- classiq/interface/generator/arith/arithmetic_result_builder.py +13 -3
- classiq/interface/generator/arith/binary_ops.py +8 -10
- classiq/interface/generator/arith/extremum_operations.py +2 -2
- classiq/interface/generator/arith/number_utils.py +20 -23
- classiq/interface/generator/arith/register_user_input.py +3 -1
- classiq/interface/generator/arith/unary_ops.py +9 -13
- classiq/interface/generator/expressions/atomic_expression_functions.py +2 -0
- classiq/interface/generator/expressions/expression.py +7 -2
- classiq/interface/generator/expressions/qmod_qnum_proxy.py +22 -0
- classiq/interface/generator/expressions/qmod_sized_proxy.py +2 -12
- classiq/interface/generator/functions/core_lib_declarations/quantum_functions/atomic_quantum_functions.py +63 -3
- classiq/interface/generator/functions/core_lib_declarations/quantum_functions/std_lib_functions.py +143 -17
- classiq/interface/generator/functions/core_lib_declarations/quantum_operators.py +41 -16
- classiq/interface/generator/functions/native_function_definition.py +3 -3
- classiq/interface/generator/model/constraints.py +3 -3
- classiq/interface/generator/model/preferences/preferences.py +13 -9
- classiq/interface/generator/noise_properties.py +5 -5
- classiq/interface/generator/qpe.py +5 -5
- classiq/interface/generator/quantum_function_call.py +5 -3
- classiq/interface/generator/randomized_benchmarking.py +5 -3
- classiq/interface/generator/visitor.py +1 -2
- classiq/interface/hardware.py +1 -1
- classiq/interface/helpers/custom_pydantic_types.py +6 -0
- classiq/interface/model/{modular_addition_operation.py → inplace_binary_operation.py} +16 -2
- classiq/interface/model/native_function_definition.py +2 -24
- classiq/interface/model/operator_synthesis_data.py +6 -0
- classiq/interface/model/quantum_expressions/amplitude_loading_operation.py +8 -4
- classiq/interface/model/quantum_expressions/arithmetic_operation.py +9 -5
- classiq/interface/model/quantum_expressions/control_state.py +38 -0
- classiq/interface/model/quantum_expressions/quantum_expression.py +21 -11
- classiq/interface/model/quantum_function_call.py +81 -6
- classiq/interface/model/quantum_function_declaration.py +3 -3
- classiq/interface/model/quantum_if_operation.py +95 -0
- classiq/interface/model/resolvers/function_call_resolver.py +1 -1
- classiq/interface/model/validations/handles_validator.py +42 -15
- classiq/interface/server/routes.py +10 -6
- classiq/model/function_handler.pyi +86 -86
- classiq/model/model.py +1 -0
- classiq/qmod/__init__.py +6 -1
- classiq/qmod/builtins/__init__.py +13 -1
- classiq/qmod/builtins/classical_execution_primitives.py +109 -0
- classiq/qmod/builtins/classical_functions.py +68 -0
- classiq/qmod/builtins/functions.py +88 -18
- classiq/qmod/builtins/operations.py +60 -35
- classiq/qmod/classical_function.py +40 -0
- classiq/qmod/declaration_inferrer.py +5 -2
- classiq/qmod/qmod_variable.py +17 -10
- classiq/qmod/quantum_callable.py +24 -3
- classiq/qmod/quantum_expandable.py +131 -21
- classiq/qmod/quantum_function.py +12 -2
- classiq/qmod/symbolic.py +182 -107
- classiq/qmod/symbolic_expr.py +11 -10
- classiq/qmod/symbolic_type.py +8 -0
- classiq/quantum_functions/decorators.py +2 -4
- classiq/quantum_functions/function_library.py +1 -0
- {classiq-0.36.0.dist-info → classiq-0.37.0.dist-info}/METADATA +1 -1
- {classiq-0.36.0.dist-info → classiq-0.37.0.dist-info}/RECORD +90 -82
- classiq/interface/model/local_variable_declaration.py +0 -7
- {classiq-0.36.0.dist-info → classiq-0.37.0.dist-info}/WHEEL +0 -0
@@ -1,18 +1,32 @@
|
|
1
1
|
from typing import Callable, List, Union
|
2
2
|
|
3
3
|
from classiq.interface.generator.expressions.expression import Expression
|
4
|
+
from classiq.interface.generator.functions.core_lib_declarations.quantum_operators import (
|
5
|
+
OPERAND_FIELD_NAME,
|
6
|
+
)
|
4
7
|
from classiq.interface.model.bind_operation import BindOperation
|
5
|
-
from classiq.interface.model.
|
8
|
+
from classiq.interface.model.inplace_binary_operation import (
|
9
|
+
BinaryOperation,
|
10
|
+
InplaceBinaryOperation,
|
11
|
+
)
|
6
12
|
from classiq.interface.model.numeric_reinterpretation import (
|
7
13
|
NumericReinterpretationOperation,
|
8
14
|
)
|
15
|
+
from classiq.interface.model.quantum_function_declaration import (
|
16
|
+
QuantumOperandDeclaration,
|
17
|
+
)
|
18
|
+
from classiq.interface.model.quantum_if_operation import QuantumIfOperation
|
9
19
|
|
10
|
-
from classiq.
|
11
|
-
|
20
|
+
from classiq.qmod.builtins.functions import (
|
21
|
+
apply,
|
22
|
+
compute as compute_operator,
|
23
|
+
uncompute,
|
24
|
+
)
|
12
25
|
from classiq.qmod.qmod_parameter import QParam
|
13
26
|
from classiq.qmod.qmod_variable import Input, Output, QNum, QVar
|
14
27
|
from classiq.qmod.quantum_callable import QCallable
|
15
|
-
from classiq.qmod.
|
28
|
+
from classiq.qmod.quantum_expandable import prepare_arg
|
29
|
+
from classiq.qmod.symbolic_expr import SymbolicExpr
|
16
30
|
|
17
31
|
|
18
32
|
def bind(source: Input[QVar], destination: Output[QVar]) -> None:
|
@@ -25,42 +39,22 @@ def bind(source: Input[QVar], destination: Output[QVar]) -> None:
|
|
25
39
|
)
|
26
40
|
|
27
41
|
|
28
|
-
QUANTUM_IF_CONDITION_ARG_ERROR_MESSAGE_PREFIX = (
|
29
|
-
"quantum_if condition must be of the form '<quantum-variable> == "
|
30
|
-
"<classical-integer-expression>', but "
|
31
|
-
)
|
32
|
-
|
33
|
-
|
34
42
|
def quantum_if(
|
35
43
|
condition: SymbolicExpr, then: Union[QCallable, Callable[[], None]]
|
36
44
|
) -> None:
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
if isinstance(ctrl, (int, SymbolicExpr)) and isinstance(ctrl_val, QNum):
|
43
|
-
ctrl, ctrl_val = ctrl_val, ctrl
|
44
|
-
|
45
|
-
if not isinstance(ctrl, QNum):
|
46
|
-
raise ClassiqValueError(
|
47
|
-
QUANTUM_IF_CONDITION_ARG_ERROR_MESSAGE_PREFIX
|
48
|
-
+ f"condition's left-hand side was {ctrl!r} of type "
|
49
|
-
f"{type(ctrl)}"
|
50
|
-
)
|
51
|
-
if not isinstance(ctrl_val, (int, SymbolicExpr)):
|
52
|
-
raise ClassiqValueError(
|
53
|
-
QUANTUM_IF_CONDITION_ARG_ERROR_MESSAGE_PREFIX
|
54
|
-
+ f"condition's right-hand side was {ctrl_val!r} of type "
|
55
|
-
f"{type(ctrl_val)}"
|
45
|
+
assert QCallable.CURRENT_EXPANDABLE is not None
|
46
|
+
QCallable.CURRENT_EXPANDABLE.append_statement_to_body(
|
47
|
+
QuantumIfOperation(
|
48
|
+
expression=Expression(expr=str(condition)),
|
49
|
+
then=prepare_arg(QuantumOperandDeclaration(name=OPERAND_FIELD_NAME), then),
|
56
50
|
)
|
57
|
-
|
51
|
+
)
|
58
52
|
|
59
53
|
|
60
54
|
def reinterpret_num(
|
55
|
+
is_signed: Union[QParam[bool], bool],
|
56
|
+
fraction_digits: Union[QParam[int], int],
|
61
57
|
target: QNum,
|
62
|
-
is_signed: Union[QParam[bool], bool] = True,
|
63
|
-
fraction_digits: Union[QParam[int], int] = 0,
|
64
58
|
) -> None:
|
65
59
|
assert QCallable.CURRENT_EXPANDABLE is not None
|
66
60
|
QCallable.CURRENT_EXPANDABLE.append_statement_to_body(
|
@@ -72,20 +66,51 @@ def reinterpret_num(
|
|
72
66
|
)
|
73
67
|
|
74
68
|
|
75
|
-
def
|
69
|
+
def inplace_add(
|
76
70
|
value: QNum,
|
77
71
|
target: QNum,
|
78
72
|
) -> None:
|
79
73
|
assert QCallable.CURRENT_EXPANDABLE is not None
|
80
74
|
QCallable.CURRENT_EXPANDABLE.append_statement_to_body(
|
81
|
-
|
75
|
+
InplaceBinaryOperation(
|
82
76
|
target=target.get_handle_binding(),
|
83
77
|
value=value.get_handle_binding(),
|
78
|
+
operation=BinaryOperation.Addition,
|
84
79
|
)
|
85
80
|
)
|
86
81
|
|
87
82
|
|
88
|
-
|
83
|
+
def inplace_xor(
|
84
|
+
value: QNum,
|
85
|
+
target: QNum,
|
86
|
+
) -> None:
|
87
|
+
assert QCallable.CURRENT_EXPANDABLE is not None
|
88
|
+
QCallable.CURRENT_EXPANDABLE.append_statement_to_body(
|
89
|
+
InplaceBinaryOperation(
|
90
|
+
target=target.get_handle_binding(),
|
91
|
+
value=value.get_handle_binding(),
|
92
|
+
operation=BinaryOperation.Xor,
|
93
|
+
)
|
94
|
+
)
|
95
|
+
|
96
|
+
|
97
|
+
def within_apply(
|
98
|
+
compute: Callable[[], None],
|
99
|
+
action: Callable[[], None],
|
100
|
+
) -> None:
|
101
|
+
compute_operator(compute)
|
102
|
+
apply(action)
|
103
|
+
uncompute(compute)
|
104
|
+
|
105
|
+
|
106
|
+
__all__ = [
|
107
|
+
"bind",
|
108
|
+
"quantum_if",
|
109
|
+
"reinterpret_num",
|
110
|
+
"inplace_add",
|
111
|
+
"inplace_xor",
|
112
|
+
"within_apply",
|
113
|
+
]
|
89
114
|
|
90
115
|
|
91
116
|
def __dir__() -> List[str]:
|
@@ -0,0 +1,40 @@
|
|
1
|
+
import ast
|
2
|
+
import inspect
|
3
|
+
import sys
|
4
|
+
from textwrap import dedent
|
5
|
+
from typing import Callable
|
6
|
+
|
7
|
+
from classiq.exceptions import ClassiqValueError
|
8
|
+
from classiq.qmod.builtins.classical_execution_primitives import sample, save
|
9
|
+
|
10
|
+
|
11
|
+
def _unparse_function_body(code: str, func: ast.FunctionDef) -> str:
|
12
|
+
first_statement = func.body[0]
|
13
|
+
body_lines = list(code.split("\n"))[first_statement.lineno - 1 :]
|
14
|
+
body_lines[0] = body_lines[0][first_statement.col_offset :]
|
15
|
+
if len(body_lines) > 1:
|
16
|
+
body_lines = [body_lines[0], dedent("\n".join(body_lines[1:]))]
|
17
|
+
return "\n".join(body_lines).strip()
|
18
|
+
|
19
|
+
|
20
|
+
class CFunc:
|
21
|
+
@staticmethod
|
22
|
+
def default_cmain() -> "CFunc":
|
23
|
+
@CFunc
|
24
|
+
def cmain() -> None:
|
25
|
+
result = sample()
|
26
|
+
save({"result": result})
|
27
|
+
|
28
|
+
return cmain
|
29
|
+
|
30
|
+
def __init__(self, py_callable: Callable[[], None]):
|
31
|
+
code = dedent(inspect.getsource(py_callable))
|
32
|
+
func = ast.parse(code).body[0]
|
33
|
+
if not isinstance(func, ast.FunctionDef):
|
34
|
+
raise ClassiqValueError(f"Use @{CFunc.__name__} to decorate a function")
|
35
|
+
if len(func.args.args) > 0:
|
36
|
+
raise ClassiqValueError(f"A @{CFunc.__name__} must receive no arguments")
|
37
|
+
if sys.version_info >= (3, 9):
|
38
|
+
self.code = "\n".join([ast.unparse(statement) for statement in func.body])
|
39
|
+
else:
|
40
|
+
self.code = _unparse_function_body(code, func)
|
@@ -29,7 +29,7 @@ from classiq import StructDeclaration
|
|
29
29
|
from classiq.qmod.model_state_container import ModelStateContainer
|
30
30
|
from classiq.qmod.qmod_parameter import Array, QParam
|
31
31
|
from classiq.qmod.qmod_variable import QVar, get_type_hint_expr
|
32
|
-
from classiq.qmod.quantum_callable import QCallable
|
32
|
+
from classiq.qmod.quantum_callable import QCallable, QCallableList
|
33
33
|
from classiq.qmod.utilities import unmangle_keyword
|
34
34
|
|
35
35
|
OPERAND_ARG_NAME = "arg{i}"
|
@@ -125,6 +125,7 @@ def _extract_operand_decl(
|
|
125
125
|
return QuantumOperandDeclaration(
|
126
126
|
name=name,
|
127
127
|
positional_arg_declarations=_extract_positional_args(arg_dict, qmodule=qmodule),
|
128
|
+
is_list=(get_origin(py_type) or py_type) is QCallableList,
|
128
129
|
)
|
129
130
|
|
130
131
|
|
@@ -141,7 +142,9 @@ def _extract_positional_args(
|
|
141
142
|
elif QVar.from_type_hint(py_type) is not None:
|
142
143
|
result.append(_extract_port_decl(name, py_type))
|
143
144
|
else:
|
144
|
-
assert get_origin(py_type) or py_type is QCallable
|
145
|
+
assert (get_origin(py_type) or py_type) is QCallable or (
|
146
|
+
get_origin(py_type) or py_type
|
147
|
+
) is QCallableList
|
145
148
|
result.append(_extract_operand_decl(name, py_type, qmodule=qmodule))
|
146
149
|
return result
|
147
150
|
|
classiq/qmod/qmod_variable.py
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
import abc
|
2
|
-
import ast
|
3
2
|
from contextlib import contextmanager
|
4
3
|
from typing import (
|
5
4
|
TYPE_CHECKING,
|
@@ -41,16 +40,13 @@ from classiq.interface.model.quantum_type import (
|
|
41
40
|
from classiq.exceptions import ClassiqValueError
|
42
41
|
from classiq.qmod.qmod_parameter import ArrayBase, QParam, QParamScalar
|
43
42
|
from classiq.qmod.quantum_callable import QCallable
|
44
|
-
from classiq.qmod.symbolic_expr import SymbolicExpr
|
43
|
+
from classiq.qmod.symbolic_expr import SymbolicExpr
|
44
|
+
from classiq.qmod.symbolic_type import SymbolicTypes
|
45
45
|
|
46
46
|
ILLEGAL_SLICING_STEP_MSG = "Slicing with a step of a quantum variable is not supported"
|
47
47
|
SLICE_OUT_OF_BOUNDS_MSG = "Slice end index out of bounds"
|
48
48
|
|
49
49
|
|
50
|
-
def _python_expr(expr: SymbolicTypes) -> str:
|
51
|
-
return ast.unparse(ast.parse(str(expr)))
|
52
|
-
|
53
|
-
|
54
50
|
def _is_input_output_typehint(type_hint: Any) -> bool:
|
55
51
|
return isinstance(type_hint, _AnnotatedAlias) and isinstance(
|
56
52
|
type_hint.__metadata__[0], PortDeclarationDirection
|
@@ -131,7 +127,7 @@ class QScalar(QVar, SymbolicExpr):
|
|
131
127
|
assert QCallable.CURRENT_EXPANDABLE is not None
|
132
128
|
QCallable.CURRENT_EXPANDABLE.append_statement_to_body(
|
133
129
|
ArithmeticOperation(
|
134
|
-
expression=Expression(expr=
|
130
|
+
expression=Expression(expr=str(expr)),
|
135
131
|
result_var=self.get_handle_binding(),
|
136
132
|
inplace_result=inplace,
|
137
133
|
)
|
@@ -142,7 +138,7 @@ class QScalar(QVar, SymbolicExpr):
|
|
142
138
|
assert QCallable.CURRENT_EXPANDABLE is not None
|
143
139
|
QCallable.CURRENT_EXPANDABLE.append_statement_to_body(
|
144
140
|
AmplitudeLoadingOperation(
|
145
|
-
expression=Expression(expr=
|
141
|
+
expression=Expression(expr=str(expr)),
|
146
142
|
result_var=self.get_handle_binding(),
|
147
143
|
)
|
148
144
|
)
|
@@ -204,6 +200,18 @@ class QNum(Generic[_T], QScalar):
|
|
204
200
|
def get_qmod_type(self) -> QuantumType:
|
205
201
|
return self.QMOD_TYPE()
|
206
202
|
|
203
|
+
@property
|
204
|
+
def size(self) -> QParamScalar:
|
205
|
+
return QParamScalar(f"len({self._name})")
|
206
|
+
|
207
|
+
@property
|
208
|
+
def fraction_digits(self) -> QParamScalar:
|
209
|
+
return QParamScalar(f"fraction_digits({self._name})")
|
210
|
+
|
211
|
+
@property
|
212
|
+
def is_signed(self) -> QParamScalar:
|
213
|
+
return QParamScalar(f"is_signed({self._name})")
|
214
|
+
|
207
215
|
|
208
216
|
_P = ParamSpec("_P")
|
209
217
|
|
@@ -245,8 +253,7 @@ class QArray(ArrayBase[_P], QVar):
|
|
245
253
|
|
246
254
|
if TYPE_CHECKING:
|
247
255
|
|
248
|
-
def len(self) -> int:
|
249
|
-
...
|
256
|
+
def len(self) -> int: ...
|
250
257
|
|
251
258
|
else:
|
252
259
|
|
classiq/qmod/quantum_callable.py
CHANGED
@@ -1,7 +1,14 @@
|
|
1
1
|
import sys
|
2
2
|
from abc import ABC, abstractmethod
|
3
|
-
from typing import
|
4
|
-
|
3
|
+
from typing import ( # type: ignore[attr-defined]
|
4
|
+
TYPE_CHECKING,
|
5
|
+
Any,
|
6
|
+
ClassVar,
|
7
|
+
Generic,
|
8
|
+
Optional,
|
9
|
+
Union,
|
10
|
+
_GenericAlias,
|
11
|
+
)
|
5
12
|
|
6
13
|
from typing_extensions import ParamSpec
|
7
14
|
|
@@ -12,6 +19,11 @@ from classiq.interface.model.quantum_function_declaration import (
|
|
12
19
|
from classiq.interface.model.quantum_statement import QuantumStatement
|
13
20
|
from classiq.interface.model.quantum_type import QuantumType
|
14
21
|
|
22
|
+
from classiq.qmod.qmod_parameter import QParam
|
23
|
+
|
24
|
+
if TYPE_CHECKING:
|
25
|
+
from classiq.qmod.quantum_expandable import QTerminalCallable
|
26
|
+
|
15
27
|
P = ParamSpec("P")
|
16
28
|
|
17
29
|
|
@@ -25,7 +37,7 @@ class QExpandableInterface(ABC):
|
|
25
37
|
raise NotImplementedError()
|
26
38
|
|
27
39
|
|
28
|
-
class QCallable(Generic[P]):
|
40
|
+
class QCallable(Generic[P], ABC):
|
29
41
|
CURRENT_EXPANDABLE: ClassVar[Optional[QExpandableInterface]] = None
|
30
42
|
|
31
43
|
def __call__(self, *args: Any, **kwargs: Any) -> None:
|
@@ -51,3 +63,12 @@ class QCallable(Generic[P]):
|
|
51
63
|
self, *args: Any, **kwargs: Any
|
52
64
|
) -> QuantumFunctionCall:
|
53
65
|
raise NotImplementedError()
|
66
|
+
|
67
|
+
|
68
|
+
class QCallableList(QCallable, Generic[P], ABC):
|
69
|
+
if TYPE_CHECKING:
|
70
|
+
|
71
|
+
def len(self) -> int: ...
|
72
|
+
|
73
|
+
def __getitem__(self, key: Union[slice, int, QParam]) -> "QTerminalCallable":
|
74
|
+
raise NotImplementedError()
|
@@ -1,6 +1,18 @@
|
|
1
1
|
from abc import ABC
|
2
2
|
from types import TracebackType
|
3
|
-
from typing import
|
3
|
+
from typing import (
|
4
|
+
TYPE_CHECKING,
|
5
|
+
Any,
|
6
|
+
Callable,
|
7
|
+
ClassVar,
|
8
|
+
Dict,
|
9
|
+
List,
|
10
|
+
Optional,
|
11
|
+
Type,
|
12
|
+
Union,
|
13
|
+
cast,
|
14
|
+
overload,
|
15
|
+
)
|
4
16
|
|
5
17
|
from typing_extensions import Self
|
6
18
|
|
@@ -11,8 +23,10 @@ from classiq.interface.model.classical_parameter_declaration import (
|
|
11
23
|
from classiq.interface.model.port_declaration import PortDeclaration
|
12
24
|
from classiq.interface.model.quantum_function_call import (
|
13
25
|
ArgValue,
|
26
|
+
OperandIdentifier,
|
14
27
|
QuantumFunctionCall,
|
15
28
|
QuantumLambdaFunction,
|
29
|
+
QuantumOperand,
|
16
30
|
)
|
17
31
|
from classiq.interface.model.quantum_function_declaration import (
|
18
32
|
PositionalArg,
|
@@ -25,8 +39,9 @@ from classiq.interface.model.variable_declaration_statement import (
|
|
25
39
|
VariableDeclarationStatement,
|
26
40
|
)
|
27
41
|
|
42
|
+
from classiq.exceptions import ClassiqValueError
|
28
43
|
from classiq.qmod.model_state_container import QMODULE, ModelStateContainer
|
29
|
-
from classiq.qmod.qmod_parameter import QParam, create_param
|
44
|
+
from classiq.qmod.qmod_parameter import QParam, QParamScalar, create_param
|
30
45
|
from classiq.qmod.qmod_variable import QVar, create_qvar_for_port_decl
|
31
46
|
from classiq.qmod.quantum_callable import QCallable, QExpandableInterface
|
32
47
|
from classiq.qmod.utilities import mangle_keyword
|
@@ -97,7 +112,7 @@ class QExpandable(QCallable, QExpandableInterface, ABC):
|
|
97
112
|
def create_quantum_function_call(
|
98
113
|
self, *args: Any, **kwargs: Any
|
99
114
|
) -> QuantumFunctionCall:
|
100
|
-
return _create_quantum_function_call(self.func_decl, *args, **kwargs)
|
115
|
+
return _create_quantum_function_call(self.func_decl, None, *args, **kwargs)
|
101
116
|
|
102
117
|
|
103
118
|
class QLambdaFunction(QExpandable):
|
@@ -126,8 +141,42 @@ class QLambdaFunction(QExpandable):
|
|
126
141
|
|
127
142
|
|
128
143
|
class QTerminalCallable(QCallable):
|
129
|
-
def __init__(
|
144
|
+
def __init__(
|
145
|
+
self,
|
146
|
+
decl: QuantumFunctionDeclaration,
|
147
|
+
index_: Optional[Union[int, QParamScalar]] = None,
|
148
|
+
) -> None:
|
130
149
|
self._decl = decl
|
150
|
+
self._index = index_
|
151
|
+
|
152
|
+
@property
|
153
|
+
def is_list(self) -> bool:
|
154
|
+
return isinstance(self._decl, QuantumOperandDeclaration) and self._decl.is_list
|
155
|
+
|
156
|
+
def __getitem__(self, key: Union[slice, int, QParam]) -> "QTerminalCallable":
|
157
|
+
if not self.is_list:
|
158
|
+
raise ClassiqValueError("Cannot index a non-list operand")
|
159
|
+
if isinstance(key, slice):
|
160
|
+
raise NotImplementedError("Operand lists don't support slicing")
|
161
|
+
if isinstance(key, QParam) and not isinstance(key, QParamScalar):
|
162
|
+
raise ClassiqValueError("Non-classical parameter for slicing")
|
163
|
+
return QTerminalCallable(self._decl, key)
|
164
|
+
|
165
|
+
def __len__(self) -> int:
|
166
|
+
raise ValueError(
|
167
|
+
"len(<func>) is not supported for quantum callables - use <func>.len() instead (Only if it is an operand list)"
|
168
|
+
)
|
169
|
+
|
170
|
+
if TYPE_CHECKING:
|
171
|
+
|
172
|
+
def len(self) -> int: ...
|
173
|
+
|
174
|
+
else:
|
175
|
+
|
176
|
+
def len(self) -> QParamScalar:
|
177
|
+
if not self.is_list:
|
178
|
+
raise ClassiqValueError("Cannot get length of a non-list operand")
|
179
|
+
return QParamScalar(f"len({self.func_decl.name})")
|
131
180
|
|
132
181
|
@property
|
133
182
|
def func_decl(self) -> QuantumFunctionDeclaration:
|
@@ -136,15 +185,39 @@ class QTerminalCallable(QCallable):
|
|
136
185
|
def create_quantum_function_call(
|
137
186
|
self, *args: Any, **kwargs: Any
|
138
187
|
) -> QuantumFunctionCall:
|
139
|
-
|
188
|
+
if self.is_list and self._index is None:
|
189
|
+
raise ClassiqValueError(
|
190
|
+
f"Quantum operand {self.func_decl.name!r} is a list and must be indexed"
|
191
|
+
)
|
192
|
+
return _create_quantum_function_call(
|
193
|
+
self.func_decl, self._index, *args, **kwargs
|
194
|
+
)
|
195
|
+
|
140
196
|
|
197
|
+
@overload
|
198
|
+
def prepare_arg(
|
199
|
+
arg_decl: PositionalArg, val: Union[QCallable, Callable[[Any], None]]
|
200
|
+
) -> QuantumOperand: ...
|
141
201
|
|
142
|
-
|
202
|
+
|
203
|
+
@overload
|
204
|
+
def prepare_arg(arg_decl: PositionalArg, val: Any) -> ArgValue: ...
|
205
|
+
|
206
|
+
|
207
|
+
def prepare_arg(arg_decl: PositionalArg, val: Any) -> ArgValue:
|
143
208
|
if isinstance(arg_decl, ClassicalParameterDeclaration):
|
144
209
|
return Expression(expr=str(val))
|
145
210
|
elif isinstance(arg_decl, PortDeclaration):
|
146
211
|
return val.get_handle_binding()
|
147
212
|
else:
|
213
|
+
if isinstance(val, list):
|
214
|
+
if not all(isinstance(v, QCallable) or callable(v) for v in val):
|
215
|
+
raise ClassiqValueError(
|
216
|
+
f"Quantum operand {arg_decl.name!r} cannot be initialized with a list of non-callables"
|
217
|
+
)
|
218
|
+
val = cast(List[Union[QCallable, Callable[[Any], None]]], val)
|
219
|
+
return [prepare_arg(arg_decl, v) for v in val]
|
220
|
+
|
148
221
|
if not isinstance(val, QCallable):
|
149
222
|
val = QLambdaFunction(arg_decl, val)
|
150
223
|
|
@@ -157,6 +230,32 @@ def _prepare_arg(arg_decl: PositionalArg, val: Any) -> ArgValue:
|
|
157
230
|
)
|
158
231
|
|
159
232
|
|
233
|
+
def _get_operand_hint_args(
|
234
|
+
func: QuantumFunctionDeclaration, param: PositionalArg, param_value: str
|
235
|
+
) -> str:
|
236
|
+
return ", ".join(
|
237
|
+
[
|
238
|
+
(
|
239
|
+
f"{decl.name}={param_value}"
|
240
|
+
if decl.name == param.name
|
241
|
+
else f"{decl.name}=..."
|
242
|
+
)
|
243
|
+
for decl in func.get_positional_arg_decls()
|
244
|
+
]
|
245
|
+
)
|
246
|
+
|
247
|
+
|
248
|
+
def _get_operand_hint(func: QuantumFunctionDeclaration, param: PositionalArg) -> str:
|
249
|
+
return (
|
250
|
+
f"\nHint: To create an operand, do not call quantum gates directly "
|
251
|
+
f"`{func.name}({_get_operand_hint_args(func, param, 'H(q)')})`. "
|
252
|
+
f"Instead, use a lambda function "
|
253
|
+
f"`{func.name}({_get_operand_hint_args(func, param, 'lambda: H(q)')})` "
|
254
|
+
f"or a quantum function "
|
255
|
+
f"`{func.name}({_get_operand_hint_args(func, param, 'my_func')})`"
|
256
|
+
)
|
257
|
+
|
258
|
+
|
160
259
|
def _prepare_args(
|
161
260
|
decl: QuantumFunctionDeclaration, arg_list: List[Any], kwargs: Dict[str, Any]
|
162
261
|
) -> List[ArgValue]:
|
@@ -166,35 +265,46 @@ def _prepare_args(
|
|
166
265
|
arg = arg_list.pop(0)
|
167
266
|
else:
|
168
267
|
arg = kwargs.pop(mangle_keyword(arg_decl.name), None)
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
268
|
+
if arg is None:
|
269
|
+
error_message = (
|
270
|
+
f"{decl.name!r} is missing required argument for {arg_decl.name!r}"
|
271
|
+
)
|
272
|
+
if isinstance(arg_decl, QuantumOperandDeclaration):
|
273
|
+
error_message += _get_operand_hint(decl, arg_decl)
|
274
|
+
raise ClassiqValueError(error_message)
|
275
|
+
result.append(prepare_arg(arg_decl, arg))
|
174
276
|
|
175
277
|
return result
|
176
278
|
|
177
279
|
|
178
280
|
def _create_quantum_function_call(
|
179
|
-
|
281
|
+
decl_: QuantumFunctionDeclaration,
|
282
|
+
index_: Optional[Union[QParamScalar, int]] = None,
|
283
|
+
*args: Any,
|
284
|
+
**kwargs: Any,
|
180
285
|
) -> QuantumFunctionCall:
|
181
|
-
arg_decls =
|
286
|
+
arg_decls = decl_.get_positional_arg_decls()
|
182
287
|
arg_list = list(args)
|
183
|
-
prepared_args = _prepare_args(
|
288
|
+
prepared_args = _prepare_args(decl_, arg_list, kwargs)
|
184
289
|
|
185
290
|
if kwargs:
|
186
291
|
bad_kwarg = next(iter(kwargs))
|
187
292
|
if not all(arg_decl.name == bad_kwarg for arg_decl in arg_decls):
|
188
|
-
raise
|
189
|
-
f"{
|
293
|
+
raise ClassiqValueError(
|
294
|
+
f"{decl_.name}() got an unexpected keyword argument {bad_kwarg!r}"
|
190
295
|
)
|
191
296
|
else:
|
192
|
-
raise
|
193
|
-
f"{
|
297
|
+
raise ClassiqValueError(
|
298
|
+
f"{decl_.name}() got multiple values for argument {bad_kwarg!r}"
|
194
299
|
)
|
195
300
|
if arg_list:
|
196
|
-
raise
|
197
|
-
f"{
|
301
|
+
raise ClassiqValueError(
|
302
|
+
f"{decl_.name}() takes {len(arg_decls)} arguments but {len(args)} were given"
|
303
|
+
)
|
304
|
+
function_ident: Union[str, OperandIdentifier] = decl_.name
|
305
|
+
if index_ is not None:
|
306
|
+
function_ident = OperandIdentifier(
|
307
|
+
index=Expression(expr=str(index_)), name=function_ident
|
198
308
|
)
|
199
309
|
|
200
|
-
return QuantumFunctionCall(function=
|
310
|
+
return QuantumFunctionCall(function=function_ident, positional_args=prepared_args)
|
classiq/qmod/quantum_function.py
CHANGED
@@ -11,10 +11,11 @@ from classiq.interface.model.quantum_function_declaration import (
|
|
11
11
|
)
|
12
12
|
|
13
13
|
from classiq.exceptions import ClassiqError
|
14
|
+
from classiq.qmod.classical_function import CFunc
|
14
15
|
from classiq.qmod.declaration_inferrer import infer_func_decl
|
15
16
|
from classiq.qmod.qmod_parameter import QParam
|
16
17
|
from classiq.qmod.qmod_variable import QVar
|
17
|
-
from classiq.qmod.quantum_callable import QCallable
|
18
|
+
from classiq.qmod.quantum_callable import QCallable, QCallableList
|
18
19
|
from classiq.qmod.quantum_expandable import QExpandable, QTerminalCallable
|
19
20
|
from classiq.qmod.utilities import mangle_keyword, unmangle_keyword
|
20
21
|
|
@@ -29,9 +30,10 @@ def create_model(
|
|
29
30
|
constraints: Optional[Constraints] = None,
|
30
31
|
execution_preferences: Optional[ExecutionPreferences] = None,
|
31
32
|
preferences: Optional[Preferences] = None,
|
33
|
+
classical_execution_function: Optional[CFunc] = None,
|
32
34
|
) -> SerializedModel:
|
33
35
|
return entry_point.create_model(
|
34
|
-
constraints, execution_preferences, preferences
|
36
|
+
constraints, execution_preferences, preferences, classical_execution_function
|
35
37
|
).get_model()
|
36
38
|
|
37
39
|
|
@@ -57,6 +59,7 @@ class QFunc(QExpandable):
|
|
57
59
|
constraints: Optional[Constraints] = None,
|
58
60
|
execution_preferences: Optional[ExecutionPreferences] = None,
|
59
61
|
preferences: Optional[Preferences] = None,
|
62
|
+
classical_execution_function: Optional[CFunc] = None,
|
60
63
|
) -> Model:
|
61
64
|
self._qmodule.type_decls = dict()
|
62
65
|
self._qmodule.native_defs = dict()
|
@@ -66,9 +69,15 @@ class QFunc(QExpandable):
|
|
66
69
|
("execution_preferences", execution_preferences),
|
67
70
|
("preferences", preferences),
|
68
71
|
]
|
72
|
+
classical_execution_function = (
|
73
|
+
CFunc.default_cmain()
|
74
|
+
if classical_execution_function is None
|
75
|
+
else classical_execution_function
|
76
|
+
)
|
69
77
|
return Model(
|
70
78
|
functions=list(self._qmodule.native_defs.values()),
|
71
79
|
types=list(self._qmodule.type_decls.values()),
|
80
|
+
classical_execution_code=classical_execution_function.code,
|
72
81
|
**{key: value for key, value in model_extra_settings if value},
|
73
82
|
)
|
74
83
|
|
@@ -116,6 +125,7 @@ def _validate_no_gen_params(annotations: Dict[str, Any]) -> None:
|
|
116
125
|
name == "return"
|
117
126
|
or get_origin(annotation) is QParam
|
118
127
|
or (get_origin(annotation) or annotation) is QCallable
|
128
|
+
or (get_origin(annotation) or annotation) is QCallableList
|
119
129
|
or QVar.from_type_hint(annotation) is not None
|
120
130
|
)
|
121
131
|
}
|