classiq 0.36.1__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 +0 -1
- classiq/applications_model_constructors/grover_model_constructor.py +27 -18
- 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/generator/ansatz_library.py +3 -3
- classiq/interface/generator/arith/binary_ops.py +1 -3
- classiq/interface/generator/expressions/atomic_expression_functions.py +2 -0
- 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/std_lib_functions.py +140 -14
- classiq/interface/generator/functions/core_lib_declarations/quantum_operators.py +3 -20
- 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 +10 -8
- 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/visitor.py +1 -2
- classiq/interface/hardware.py +1 -1
- classiq/interface/model/native_function_definition.py +2 -24
- classiq/interface/model/quantum_expressions/amplitude_loading_operation.py +2 -2
- classiq/interface/model/quantum_expressions/arithmetic_operation.py +2 -2
- classiq/interface/model/quantum_expressions/control_state.py +38 -0
- classiq/interface/model/quantum_expressions/quantum_expression.py +12 -9
- classiq/interface/model/quantum_function_call.py +3 -0
- classiq/interface/model/quantum_function_declaration.py +3 -3
- classiq/interface/model/quantum_if_operation.py +95 -0
- classiq/interface/model/validations/handles_validator.py +7 -15
- classiq/interface/server/routes.py +10 -6
- classiq/model/function_handler.pyi +85 -85
- classiq/model/model.py +1 -0
- classiq/qmod/__init__.py +4 -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 +47 -21
- classiq/qmod/builtins/operations.py +15 -29
- classiq/qmod/classical_function.py +40 -0
- classiq/qmod/declaration_inferrer.py +5 -2
- classiq/qmod/qmod_variable.py +15 -3
- classiq/qmod/quantum_callable.py +24 -3
- classiq/qmod/quantum_expandable.py +99 -17
- classiq/qmod/quantum_function.py +12 -2
- classiq/qmod/symbolic.py +109 -107
- classiq/qmod/symbolic_expr.py +1 -4
- 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.1.dist-info → classiq-0.37.0.dist-info}/METADATA +1 -1
- {classiq-0.36.1.dist-info → classiq-0.37.0.dist-info}/RECORD +69 -61
- classiq/interface/model/local_variable_declaration.py +0 -7
- {classiq-0.36.1.dist-info → classiq-0.37.0.dist-info}/WHEEL +0 -0
@@ -4,7 +4,7 @@ from typing import List, Literal
|
|
4
4
|
|
5
5
|
from classiq.qmod.qmod_parameter import QParam
|
6
6
|
from classiq.qmod.qmod_variable import Input, Output, QArray, QBit, QNum
|
7
|
-
from classiq.qmod.quantum_callable import QCallable
|
7
|
+
from classiq.qmod.quantum_callable import QCallable, QCallableList
|
8
8
|
from classiq.qmod.quantum_function import ExternalQFunc
|
9
9
|
|
10
10
|
from .structs import *
|
@@ -256,6 +256,22 @@ def add(
|
|
256
256
|
pass
|
257
257
|
|
258
258
|
|
259
|
+
@ExternalQFunc
|
260
|
+
def modular_add(
|
261
|
+
left: QArray[QBit],
|
262
|
+
right: QArray[QBit],
|
263
|
+
) -> None:
|
264
|
+
pass
|
265
|
+
|
266
|
+
|
267
|
+
@ExternalQFunc
|
268
|
+
def integer_xor(
|
269
|
+
left: QArray[QBit],
|
270
|
+
right: QArray[QBit],
|
271
|
+
) -> None:
|
272
|
+
pass
|
273
|
+
|
274
|
+
|
259
275
|
@ExternalQFunc
|
260
276
|
def U(
|
261
277
|
theta: QParam[float],
|
@@ -371,19 +387,26 @@ def qft(
|
|
371
387
|
|
372
388
|
|
373
389
|
@ExternalQFunc
|
374
|
-
def
|
375
|
-
|
376
|
-
|
377
|
-
phase: QArray[QBit, Literal["precision"]],
|
390
|
+
def qpe_flexible(
|
391
|
+
unitary_with_power: QCallable[QParam[int]],
|
392
|
+
phase: QNum,
|
378
393
|
) -> None:
|
379
394
|
pass
|
380
395
|
|
381
396
|
|
382
397
|
@ExternalQFunc
|
383
398
|
def qpe(
|
399
|
+
unitary: QCallable,
|
400
|
+
phase: QNum,
|
401
|
+
) -> None:
|
402
|
+
pass
|
403
|
+
|
404
|
+
|
405
|
+
@ExternalQFunc
|
406
|
+
def standard_qpe(
|
384
407
|
precision: QParam[int],
|
385
408
|
unitary: QCallable,
|
386
|
-
phase:
|
409
|
+
phase: QArray[QBit, Literal["precision"]],
|
387
410
|
) -> None:
|
388
411
|
pass
|
389
412
|
|
@@ -545,13 +568,22 @@ def full_hea(
|
|
545
568
|
angle_params: QParam[List[float]],
|
546
569
|
connectivity_map: QParam[List[List[int]]],
|
547
570
|
reps: QParam[int],
|
548
|
-
operands_1qubit:
|
549
|
-
operands_2qubit:
|
571
|
+
operands_1qubit: QCallableList[QParam[float], QBit],
|
572
|
+
operands_2qubit: QCallableList[QParam[float], QBit, QBit],
|
550
573
|
x: QArray[QBit, Literal["num_qubits"]],
|
551
574
|
) -> None:
|
552
575
|
pass
|
553
576
|
|
554
577
|
|
578
|
+
@ExternalQFunc
|
579
|
+
def swap_test(
|
580
|
+
state1: QArray[QBit],
|
581
|
+
state2: QArray[QBit],
|
582
|
+
test: Output[QArray[QBit]],
|
583
|
+
) -> None:
|
584
|
+
pass
|
585
|
+
|
586
|
+
|
555
587
|
@ExternalQFunc
|
556
588
|
def repeat(
|
557
589
|
count: QParam[int],
|
@@ -587,7 +619,7 @@ def if_(
|
|
587
619
|
@ExternalQFunc
|
588
620
|
def switch(
|
589
621
|
selector: QParam[int],
|
590
|
-
cases:
|
622
|
+
cases: QCallableList,
|
591
623
|
) -> None:
|
592
624
|
pass
|
593
625
|
|
@@ -614,7 +646,7 @@ def split(
|
|
614
646
|
|
615
647
|
@ExternalQFunc
|
616
648
|
def permute(
|
617
|
-
functions:
|
649
|
+
functions: QCallableList,
|
618
650
|
) -> None:
|
619
651
|
pass
|
620
652
|
|
@@ -627,15 +659,6 @@ def power(
|
|
627
659
|
pass
|
628
660
|
|
629
661
|
|
630
|
-
@ExternalQFunc
|
631
|
-
def control_with_value(
|
632
|
-
ctrl_val: QParam[int],
|
633
|
-
operand: QCallable,
|
634
|
-
ctrl: QNum,
|
635
|
-
) -> None:
|
636
|
-
pass
|
637
|
-
|
638
|
-
|
639
662
|
@ExternalQFunc
|
640
663
|
def apply(
|
641
664
|
operand: QCallable,
|
@@ -812,6 +835,8 @@ __all__ = [
|
|
812
835
|
"prepare_amplitudes",
|
813
836
|
"unitary",
|
814
837
|
"add",
|
838
|
+
"modular_add",
|
839
|
+
"integer_xor",
|
815
840
|
"U",
|
816
841
|
"CCX",
|
817
842
|
"allocate",
|
@@ -825,8 +850,9 @@ __all__ = [
|
|
825
850
|
"exponentiation_with_depth_constraint",
|
826
851
|
"qft_step",
|
827
852
|
"qft",
|
828
|
-
"
|
853
|
+
"qpe_flexible",
|
829
854
|
"qpe",
|
855
|
+
"standard_qpe",
|
830
856
|
"single_pauli",
|
831
857
|
"linear_pauli_rotations",
|
832
858
|
"amplitude_estimation",
|
@@ -844,6 +870,7 @@ __all__ = [
|
|
844
870
|
"qaoa_init",
|
845
871
|
"qaoa_penalty",
|
846
872
|
"full_hea",
|
873
|
+
"swap_test",
|
847
874
|
"repeat",
|
848
875
|
"invert",
|
849
876
|
"control",
|
@@ -853,7 +880,6 @@ __all__ = [
|
|
853
880
|
"split",
|
854
881
|
"permute",
|
855
882
|
"power",
|
856
|
-
"control_with_value",
|
857
883
|
"apply",
|
858
884
|
"compute",
|
859
885
|
"uncompute",
|
@@ -1,6 +1,9 @@
|
|
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
8
|
from classiq.interface.model.inplace_binary_operation import (
|
6
9
|
BinaryOperation,
|
@@ -9,18 +12,21 @@ from classiq.interface.model.inplace_binary_operation import (
|
|
9
12
|
from classiq.interface.model.numeric_reinterpretation import (
|
10
13
|
NumericReinterpretationOperation,
|
11
14
|
)
|
15
|
+
from classiq.interface.model.quantum_function_declaration import (
|
16
|
+
QuantumOperandDeclaration,
|
17
|
+
)
|
18
|
+
from classiq.interface.model.quantum_if_operation import QuantumIfOperation
|
12
19
|
|
13
|
-
from classiq.exceptions import ClassiqValueError
|
14
20
|
from classiq.qmod.builtins.functions import (
|
15
21
|
apply,
|
16
22
|
compute as compute_operator,
|
17
|
-
control_with_value,
|
18
23
|
uncompute,
|
19
24
|
)
|
20
25
|
from classiq.qmod.qmod_parameter import QParam
|
21
26
|
from classiq.qmod.qmod_variable import Input, Output, QNum, QVar
|
22
27
|
from classiq.qmod.quantum_callable import QCallable
|
23
|
-
from classiq.qmod.
|
28
|
+
from classiq.qmod.quantum_expandable import prepare_arg
|
29
|
+
from classiq.qmod.symbolic_expr import SymbolicExpr
|
24
30
|
|
25
31
|
|
26
32
|
def bind(source: Input[QVar], destination: Output[QVar]) -> None:
|
@@ -33,36 +39,16 @@ def bind(source: Input[QVar], destination: Output[QVar]) -> None:
|
|
33
39
|
)
|
34
40
|
|
35
41
|
|
36
|
-
QUANTUM_IF_CONDITION_ARG_ERROR_MESSAGE_PREFIX = (
|
37
|
-
"quantum_if condition must be of the form '<quantum-variable> == "
|
38
|
-
"<classical-integer-expression>', but "
|
39
|
-
)
|
40
|
-
|
41
|
-
|
42
42
|
def quantum_if(
|
43
43
|
condition: SymbolicExpr, then: Union[QCallable, Callable[[], None]]
|
44
44
|
) -> None:
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
if isinstance(ctrl, (int, SymbolicExpr)) and isinstance(ctrl_val, QNum):
|
51
|
-
ctrl, ctrl_val = ctrl_val, ctrl
|
52
|
-
|
53
|
-
if not isinstance(ctrl, QNum):
|
54
|
-
raise ClassiqValueError(
|
55
|
-
QUANTUM_IF_CONDITION_ARG_ERROR_MESSAGE_PREFIX
|
56
|
-
+ f"condition's left-hand side was {ctrl!r} of type "
|
57
|
-
f"{type(ctrl)}"
|
58
|
-
)
|
59
|
-
if not isinstance(ctrl_val, (int, SymbolicExpr)):
|
60
|
-
raise ClassiqValueError(
|
61
|
-
QUANTUM_IF_CONDITION_ARG_ERROR_MESSAGE_PREFIX
|
62
|
-
+ f"condition's right-hand side was {ctrl_val!r} of type "
|
63
|
-
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),
|
64
50
|
)
|
65
|
-
|
51
|
+
)
|
66
52
|
|
67
53
|
|
68
54
|
def reinterpret_num(
|
@@ -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
@@ -40,7 +40,8 @@ from classiq.interface.model.quantum_type import (
|
|
40
40
|
from classiq.exceptions import ClassiqValueError
|
41
41
|
from classiq.qmod.qmod_parameter import ArrayBase, QParam, QParamScalar
|
42
42
|
from classiq.qmod.quantum_callable import QCallable
|
43
|
-
from classiq.qmod.symbolic_expr import SymbolicExpr
|
43
|
+
from classiq.qmod.symbolic_expr import SymbolicExpr
|
44
|
+
from classiq.qmod.symbolic_type import SymbolicTypes
|
44
45
|
|
45
46
|
ILLEGAL_SLICING_STEP_MSG = "Slicing with a step of a quantum variable is not supported"
|
46
47
|
SLICE_OUT_OF_BOUNDS_MSG = "Slice end index out of bounds"
|
@@ -199,6 +200,18 @@ class QNum(Generic[_T], QScalar):
|
|
199
200
|
def get_qmod_type(self) -> QuantumType:
|
200
201
|
return self.QMOD_TYPE()
|
201
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
|
+
|
202
215
|
|
203
216
|
_P = ParamSpec("_P")
|
204
217
|
|
@@ -240,8 +253,7 @@ class QArray(ArrayBase[_P], QVar):
|
|
240
253
|
|
241
254
|
if TYPE_CHECKING:
|
242
255
|
|
243
|
-
def len(self) -> int:
|
244
|
-
...
|
256
|
+
def len(self) -> int: ...
|
245
257
|
|
246
258
|
else:
|
247
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,
|
@@ -27,7 +41,7 @@ from classiq.interface.model.variable_declaration_statement import (
|
|
27
41
|
|
28
42
|
from classiq.exceptions import ClassiqValueError
|
29
43
|
from classiq.qmod.model_state_container import QMODULE, ModelStateContainer
|
30
|
-
from classiq.qmod.qmod_parameter import QParam, create_param
|
44
|
+
from classiq.qmod.qmod_parameter import QParam, QParamScalar, create_param
|
31
45
|
from classiq.qmod.qmod_variable import QVar, create_qvar_for_port_decl
|
32
46
|
from classiq.qmod.quantum_callable import QCallable, QExpandableInterface
|
33
47
|
from classiq.qmod.utilities import mangle_keyword
|
@@ -98,7 +112,7 @@ class QExpandable(QCallable, QExpandableInterface, ABC):
|
|
98
112
|
def create_quantum_function_call(
|
99
113
|
self, *args: Any, **kwargs: Any
|
100
114
|
) -> QuantumFunctionCall:
|
101
|
-
return _create_quantum_function_call(self.func_decl, *args, **kwargs)
|
115
|
+
return _create_quantum_function_call(self.func_decl, None, *args, **kwargs)
|
102
116
|
|
103
117
|
|
104
118
|
class QLambdaFunction(QExpandable):
|
@@ -127,8 +141,42 @@ class QLambdaFunction(QExpandable):
|
|
127
141
|
|
128
142
|
|
129
143
|
class QTerminalCallable(QCallable):
|
130
|
-
def __init__(
|
144
|
+
def __init__(
|
145
|
+
self,
|
146
|
+
decl: QuantumFunctionDeclaration,
|
147
|
+
index_: Optional[Union[int, QParamScalar]] = None,
|
148
|
+
) -> None:
|
131
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})")
|
132
180
|
|
133
181
|
@property
|
134
182
|
def func_decl(self) -> QuantumFunctionDeclaration:
|
@@ -137,15 +185,39 @@ class QTerminalCallable(QCallable):
|
|
137
185
|
def create_quantum_function_call(
|
138
186
|
self, *args: Any, **kwargs: Any
|
139
187
|
) -> QuantumFunctionCall:
|
140
|
-
|
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
|
+
|
196
|
+
|
197
|
+
@overload
|
198
|
+
def prepare_arg(
|
199
|
+
arg_decl: PositionalArg, val: Union[QCallable, Callable[[Any], None]]
|
200
|
+
) -> QuantumOperand: ...
|
201
|
+
|
141
202
|
|
203
|
+
@overload
|
204
|
+
def prepare_arg(arg_decl: PositionalArg, val: Any) -> ArgValue: ...
|
142
205
|
|
143
|
-
|
206
|
+
|
207
|
+
def prepare_arg(arg_decl: PositionalArg, val: Any) -> ArgValue:
|
144
208
|
if isinstance(arg_decl, ClassicalParameterDeclaration):
|
145
209
|
return Expression(expr=str(val))
|
146
210
|
elif isinstance(arg_decl, PortDeclaration):
|
147
211
|
return val.get_handle_binding()
|
148
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
|
+
|
149
221
|
if not isinstance(val, QCallable):
|
150
222
|
val = QLambdaFunction(arg_decl, val)
|
151
223
|
|
@@ -163,9 +235,11 @@ def _get_operand_hint_args(
|
|
163
235
|
) -> str:
|
164
236
|
return ", ".join(
|
165
237
|
[
|
166
|
-
|
167
|
-
|
168
|
-
|
238
|
+
(
|
239
|
+
f"{decl.name}={param_value}"
|
240
|
+
if decl.name == param.name
|
241
|
+
else f"{decl.name}=..."
|
242
|
+
)
|
169
243
|
for decl in func.get_positional_arg_decls()
|
170
244
|
]
|
171
245
|
)
|
@@ -198,31 +272,39 @@ def _prepare_args(
|
|
198
272
|
if isinstance(arg_decl, QuantumOperandDeclaration):
|
199
273
|
error_message += _get_operand_hint(decl, arg_decl)
|
200
274
|
raise ClassiqValueError(error_message)
|
201
|
-
result.append(
|
275
|
+
result.append(prepare_arg(arg_decl, arg))
|
202
276
|
|
203
277
|
return result
|
204
278
|
|
205
279
|
|
206
280
|
def _create_quantum_function_call(
|
207
|
-
|
281
|
+
decl_: QuantumFunctionDeclaration,
|
282
|
+
index_: Optional[Union[QParamScalar, int]] = None,
|
283
|
+
*args: Any,
|
284
|
+
**kwargs: Any,
|
208
285
|
) -> QuantumFunctionCall:
|
209
|
-
arg_decls =
|
286
|
+
arg_decls = decl_.get_positional_arg_decls()
|
210
287
|
arg_list = list(args)
|
211
|
-
prepared_args = _prepare_args(
|
288
|
+
prepared_args = _prepare_args(decl_, arg_list, kwargs)
|
212
289
|
|
213
290
|
if kwargs:
|
214
291
|
bad_kwarg = next(iter(kwargs))
|
215
292
|
if not all(arg_decl.name == bad_kwarg for arg_decl in arg_decls):
|
216
293
|
raise ClassiqValueError(
|
217
|
-
f"{
|
294
|
+
f"{decl_.name}() got an unexpected keyword argument {bad_kwarg!r}"
|
218
295
|
)
|
219
296
|
else:
|
220
297
|
raise ClassiqValueError(
|
221
|
-
f"{
|
298
|
+
f"{decl_.name}() got multiple values for argument {bad_kwarg!r}"
|
222
299
|
)
|
223
300
|
if arg_list:
|
224
301
|
raise ClassiqValueError(
|
225
|
-
f"{
|
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
|
226
308
|
)
|
227
309
|
|
228
|
-
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
|
}
|