classiq 0.42.2__py3-none-any.whl → 0.43.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 +2 -6
- classiq/_internals/api_wrapper.py +6 -12
- classiq/_internals/authentication/token_manager.py +5 -2
- classiq/_internals/jobs.py +5 -10
- classiq/analyzer/rb.py +3 -3
- classiq/applications/chemistry/chemistry_model_constructor.py +12 -8
- classiq/applications/combinatorial_helpers/pauli_helpers/pauli_utils.py +2 -2
- classiq/applications/finance/finance_model_constructor.py +16 -13
- classiq/applications/qsvm/__init__.py +1 -3
- classiq/applications/qsvm/qsvm_model_constructor.py +7 -6
- classiq/exceptions.py +9 -4
- classiq/execution/execution_session.py +5 -2
- classiq/execution/qnn.py +1 -1
- classiq/executor.py +0 -2
- classiq/interface/_version.py +1 -1
- classiq/interface/chemistry/operator.py +19 -5
- classiq/interface/executor/constants.py +1 -0
- classiq/interface/finance/function_input.py +16 -10
- classiq/interface/generator/application_apis/chemistry_declarations.py +2 -2
- classiq/interface/generator/application_apis/qsvm_declarations.py +4 -2
- classiq/interface/generator/arith/argument_utils.py +20 -3
- classiq/interface/generator/arith/arithmetic_expression_validator.py +3 -26
- classiq/interface/generator/arith/binary_ops.py +8 -14
- classiq/interface/generator/arith/extremum_operations.py +30 -0
- classiq/interface/generator/arith/number_utils.py +1 -1
- classiq/interface/generator/arith/unary_ops.py +1 -3
- classiq/interface/generator/compiler_keywords.py +1 -1
- classiq/interface/generator/expressions/atomic_expression_functions.py +13 -3
- classiq/interface/generator/expressions/enums/__init__.py +0 -20
- classiq/interface/generator/expressions/enums/finance_functions.py +11 -18
- classiq/interface/generator/expressions/non_symbolic_expr.py +119 -0
- classiq/interface/generator/expressions/qmod_qarray_proxy.py +52 -37
- classiq/interface/generator/expressions/qmod_qscalar_proxy.py +16 -11
- classiq/interface/generator/expressions/qmod_sized_proxy.py +5 -5
- classiq/interface/generator/function_param_list_without_self_reference.py +0 -10
- classiq/interface/generator/function_params.py +0 -4
- classiq/interface/generator/functions/__init__.py +0 -20
- classiq/interface/generator/functions/builtins/core_library/exponentiation_functions.py +2 -2
- classiq/interface/generator/functions/builtins/open_lib_functions.py +530 -1
- classiq/interface/generator/functions/classical_type.py +22 -69
- classiq/interface/generator/functions/port_declaration.py +0 -11
- classiq/interface/generator/model/__init__.py +0 -1
- classiq/interface/generator/model/model.py +9 -185
- classiq/interface/generator/synthesis_metadata/synthesis_execution_data.py +3 -1
- classiq/interface/generator/types/builtin_enum_declarations.py +69 -0
- classiq/interface/generator/types/builtin_struct_declarations/pauli_struct_declarations.py +2 -2
- classiq/interface/generator/types/enum_declaration.py +57 -0
- classiq/interface/jobs.py +36 -65
- classiq/interface/model/bind_operation.py +3 -0
- classiq/interface/model/classical_parameter_declaration.py +3 -0
- classiq/interface/model/handle_binding.py +7 -0
- classiq/interface/model/inplace_binary_operation.py +13 -15
- classiq/interface/model/model.py +8 -20
- classiq/interface/model/native_function_definition.py +0 -17
- classiq/interface/model/quantum_function_call.py +63 -182
- classiq/interface/model/quantum_type.py +71 -10
- classiq/interface/server/routes.py +0 -6
- classiq/qmod/__init__.py +3 -3
- classiq/qmod/builtins/__init__.py +10 -1
- classiq/qmod/builtins/classical_execution_primitives.py +4 -2
- classiq/qmod/builtins/enums.py +177 -0
- classiq/qmod/builtins/functions.py +1 -2
- classiq/qmod/builtins/operations.py +2 -4
- classiq/qmod/builtins/structs.py +16 -17
- classiq/qmod/declaration_inferrer.py +23 -20
- classiq/qmod/model_state_container.py +2 -0
- classiq/qmod/native/pretty_printer.py +31 -13
- classiq/qmod/pretty_print/pretty_printer.py +52 -27
- classiq/qmod/qmod_constant.py +7 -3
- classiq/qmod/qmod_parameter.py +2 -1
- classiq/qmod/qmod_struct.py +9 -33
- classiq/qmod/qmod_variable.py +55 -22
- classiq/qmod/quantum_callable.py +6 -1
- classiq/qmod/quantum_expandable.py +29 -11
- classiq/qmod/quantum_function.py +8 -4
- classiq/qmod/semantics/annotation.py +38 -0
- classiq/qmod/semantics/error_manager.py +49 -0
- classiq/qmod/semantics/static_semantics_visitor.py +308 -0
- classiq/qmod/semantics/validation/func_call_validation.py +149 -0
- classiq/qmod/semantics/validation/types_validation.py +21 -0
- classiq/qmod/symbolic.py +6 -6
- classiq/qmod/symbolic_expr.py +26 -11
- classiq/qmod/utilities.py +23 -1
- {classiq-0.42.2.dist-info → classiq-0.43.1.dist-info}/METADATA +2 -2
- {classiq-0.42.2.dist-info → classiq-0.43.1.dist-info}/RECORD +88 -103
- classiq/_internals/_qfunc_ext.py +0 -6
- classiq/applications/libraries/ampltitude_estimation_library.py +0 -11
- classiq/interface/generator/credit_risk_example/linear_gci.py +0 -122
- classiq/interface/generator/credit_risk_example/weighted_adder.py +0 -69
- classiq/interface/generator/expressions/enums/chemistry.py +0 -28
- classiq/interface/generator/expressions/enums/classical_enum.py +0 -20
- classiq/interface/generator/expressions/enums/ladder_operator.py +0 -6
- classiq/interface/generator/expressions/enums/optimizers.py +0 -9
- classiq/interface/generator/expressions/enums/pauli.py +0 -8
- classiq/interface/generator/expressions/enums/qsvm_feature_map_entanglement.py +0 -9
- classiq/interface/generator/functions/foreign_function_definition.py +0 -114
- classiq/interface/generator/functions/function_implementation.py +0 -107
- classiq/interface/generator/functions/native_function_definition.py +0 -155
- classiq/interface/generator/functions/quantum_function_declaration.py +0 -69
- classiq/interface/generator/functions/register.py +0 -44
- classiq/interface/generator/functions/register_mapping_data.py +0 -106
- classiq/interface/generator/inequality_mixer.py +0 -51
- classiq/interface/generator/model/classical_main_validator.py +0 -106
- classiq/interface/generator/range_mixer.py +0 -56
- classiq/interface/generator/state_propagator.py +0 -74
- classiq/interface/model/resolvers/function_call_resolver.py +0 -64
- classiq/interface/model/validations/__init__.py +0 -0
- classiq/interface/model/validations/handle_validation_base.py +0 -55
- classiq/interface/model/validations/handles_validator.py +0 -153
- classiq/interface/model/validations/port_to_wire_name_generator.py +0 -12
- /classiq/{interface/generator/credit_risk_example → qmod/semantics}/__init__.py +0 -0
- /classiq/{interface/model/resolvers → qmod/semantics/validation}/__init__.py +0 -0
- {classiq-0.42.2.dist-info → classiq-0.43.1.dist-info}/WHEEL +0 -0
classiq/interface/jobs.py
CHANGED
@@ -1,16 +1,18 @@
|
|
1
|
-
from typing import Any, Dict, Generic,
|
1
|
+
from typing import Any, Dict, Generic, Optional, TypeVar, Union
|
2
2
|
|
3
3
|
import pydantic
|
4
|
-
from pydantic import BaseModel
|
4
|
+
from pydantic import BaseModel
|
5
5
|
from pydantic.generics import GenericModel
|
6
6
|
|
7
7
|
from classiq.interface.helpers.custom_encoders import CUSTOM_ENCODERS
|
8
8
|
|
9
9
|
from classiq._internals.enum_utils import StrEnum
|
10
|
+
from classiq.exceptions import ClassiqAPIError
|
10
11
|
|
11
12
|
JSONObject = Dict[str, Any]
|
12
13
|
T = TypeVar("T", bound=Union[pydantic.BaseModel, JSONObject])
|
13
14
|
AUTH_HEADER = "Classiq-BE-Auth"
|
15
|
+
INVALID_RESPONSE_ERROR_MSG = "Invalid response from Classiq API"
|
14
16
|
|
15
17
|
|
16
18
|
class JobID(BaseModel):
|
@@ -34,70 +36,39 @@ class JobStatus(StrEnum):
|
|
34
36
|
"""
|
35
37
|
A job can be in either of 3 states: ongoing, completed successfully or completed
|
36
38
|
unsuccessfully. Each job status belongs to one of the 3 states
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
so it's defined as a generic class
|
41
|
-
JobDescriptionFailure represents a job that was completed unsuccessfully. It contains
|
42
|
-
the failure details (i.e., error message) in the description field
|
43
|
-
JobDescriptionNonFinal represents a job that has not terminated yet. It does not contain
|
44
|
-
any additional information
|
45
|
-
JobDescriptionUnion is used to define a discriminator field between the 3 states. Since
|
46
|
-
JobDescriptionSuccess is generic, so is the union. This means it cannot be defined
|
47
|
-
as an annotated type alias (that is, we cannot define
|
48
|
-
JobDescriptionUnion = Annotated[Union[...], Field(discriminator="kind")])
|
39
|
+
For ongoing jobs, we expect both the failure_details and result to be None
|
40
|
+
For successful jobs, we expect failure_details to be None and result to be an instance of T
|
41
|
+
For unsuccessful jobs, we expect failure_details to be a string and result to be None
|
49
42
|
"""
|
50
43
|
|
51
|
-
SuccessStatus = Literal[JobStatus.COMPLETED]
|
52
|
-
FailureStatus = Union[
|
53
|
-
Literal[JobStatus.FAILED],
|
54
|
-
Literal[JobStatus.CANCELLED],
|
55
|
-
]
|
56
|
-
NonFinalStatus = Union[
|
57
|
-
Literal[JobStatus.QUEUED],
|
58
|
-
Literal[JobStatus.RUNNING],
|
59
|
-
Literal[JobStatus.READY],
|
60
|
-
Literal[JobStatus.CANCELLING],
|
61
|
-
Literal[JobStatus.UNKNOWN],
|
62
|
-
]
|
63
44
|
|
64
|
-
|
65
|
-
class FailureDetails(BaseModel):
|
66
|
-
details: str
|
67
|
-
|
68
|
-
|
69
|
-
class JobDescriptionBase(GenericModel, Generic[T], json_encoders=CUSTOM_ENCODERS):
|
70
|
-
kind: str
|
45
|
+
class JobDescription(GenericModel, Generic[T], json_encoders=CUSTOM_ENCODERS):
|
71
46
|
status: JobStatus
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
class JobDescriptionUnion(GenericModel, Generic[T], json_encoders=CUSTOM_ENCODERS):
|
101
|
-
__root__: Union[
|
102
|
-
JobDescriptionSuccess[T], JobDescriptionFailure, JobDescriptionNonFinal
|
103
|
-
] = Field(discriminator="kind")
|
47
|
+
failure_details: Optional[str]
|
48
|
+
result: Optional[T]
|
49
|
+
|
50
|
+
@pydantic.root_validator
|
51
|
+
def validate_status_and_fields(cls, values: Dict[str, Any]) -> Dict[str, Any]:
|
52
|
+
status: Optional[JobStatus] = values.get("status")
|
53
|
+
if status is None or "result" not in values or "failure_details" not in values:
|
54
|
+
# If any of the fields doesn't exist, then previous validations failed
|
55
|
+
# result and failure_details are Optional, so we explicitly check if they
|
56
|
+
# exist in the values dictionary
|
57
|
+
return values
|
58
|
+
|
59
|
+
result = values["result"]
|
60
|
+
failure_details = values["failure_details"]
|
61
|
+
|
62
|
+
if status is JobStatus.COMPLETED:
|
63
|
+
# Completed job must return result and not have an error
|
64
|
+
if result is None or failure_details is not None:
|
65
|
+
raise ClassiqAPIError(INVALID_RESPONSE_ERROR_MSG)
|
66
|
+
elif status in (JobStatus.FAILED, JobStatus.CANCELLED):
|
67
|
+
# Failed job must return error and not have result
|
68
|
+
if result is not None or failure_details is None:
|
69
|
+
raise ClassiqAPIError(INVALID_RESPONSE_ERROR_MSG)
|
70
|
+
elif result is not None or failure_details is not None:
|
71
|
+
# Pending job must have no result and no error
|
72
|
+
raise ClassiqAPIError(INVALID_RESPONSE_ERROR_MSG)
|
73
|
+
|
74
|
+
return values
|
@@ -1,3 +1,5 @@
|
|
1
|
+
from typing import Union
|
2
|
+
|
1
3
|
from pydantic import Extra
|
2
4
|
|
3
5
|
from classiq.interface.ast_node import ASTNode
|
@@ -45,3 +47,8 @@ class SlicedHandleBinding(HandleBinding):
|
|
45
47
|
|
46
48
|
def is_bindable(self) -> bool:
|
47
49
|
return False
|
50
|
+
|
51
|
+
|
52
|
+
ConcreteHandleBinding = Union[
|
53
|
+
HandleBinding, SubscriptHandleBinding, SlicedHandleBinding
|
54
|
+
]
|
@@ -1,13 +1,17 @@
|
|
1
1
|
from typing import Literal, Mapping
|
2
2
|
|
3
|
-
import
|
4
|
-
|
3
|
+
from classiq.interface.generator.functions.builtins.core_library import (
|
4
|
+
INTEGER_XOR_FUNCTION,
|
5
|
+
MODULAR_ADD_FUNCTION,
|
6
|
+
)
|
5
7
|
from classiq.interface.helpers.pydantic_model_helpers import nameables_to_dict
|
6
|
-
from classiq.interface.model.handle_binding import HandleBinding
|
8
|
+
from classiq.interface.model.handle_binding import ConcreteHandleBinding, HandleBinding
|
9
|
+
from classiq.interface.model.quantum_function_declaration import (
|
10
|
+
QuantumFunctionDeclaration,
|
11
|
+
)
|
7
12
|
from classiq.interface.model.quantum_statement import QuantumOperation
|
8
13
|
|
9
14
|
from classiq._internals.enum_utils import StrEnum
|
10
|
-
from classiq.exceptions import ClassiqValueError
|
11
15
|
|
12
16
|
|
13
17
|
class BinaryOperation(StrEnum):
|
@@ -15,26 +19,20 @@ class BinaryOperation(StrEnum):
|
|
15
19
|
Xor = "inplace_xor"
|
16
20
|
|
17
21
|
@property
|
18
|
-
def
|
22
|
+
def internal_function_declaration(self) -> QuantumFunctionDeclaration:
|
19
23
|
return {
|
20
|
-
BinaryOperation.Addition:
|
21
|
-
BinaryOperation.Xor:
|
24
|
+
BinaryOperation.Addition: MODULAR_ADD_FUNCTION,
|
25
|
+
BinaryOperation.Xor: INTEGER_XOR_FUNCTION,
|
22
26
|
}[self]
|
23
27
|
|
24
28
|
|
25
29
|
class InplaceBinaryOperation(QuantumOperation):
|
26
30
|
kind: Literal["InplaceBinaryOperation"]
|
27
31
|
|
28
|
-
target:
|
29
|
-
value:
|
32
|
+
target: ConcreteHandleBinding
|
33
|
+
value: ConcreteHandleBinding
|
30
34
|
operation: BinaryOperation
|
31
35
|
|
32
36
|
@property
|
33
37
|
def wiring_inouts(self) -> Mapping[str, HandleBinding]:
|
34
38
|
return nameables_to_dict([self.target, self.value])
|
35
|
-
|
36
|
-
@pydantic.validator("target", "value")
|
37
|
-
def validate_handle(cls, handle: HandleBinding) -> HandleBinding:
|
38
|
-
if not handle.is_bindable():
|
39
|
-
raise ClassiqValueError(f"Cannot bind '{handle!r}'")
|
40
|
-
return handle
|
classiq/interface/model/model.py
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
from collections import Counter
|
2
|
-
from typing import
|
2
|
+
from typing import Dict, List, Literal, NewType, Set
|
3
3
|
|
4
4
|
import pydantic
|
5
5
|
|
@@ -12,6 +12,7 @@ from classiq.interface.generator.functions.port_declaration import (
|
|
12
12
|
from classiq.interface.generator.model.constraints import Constraints
|
13
13
|
from classiq.interface.generator.model.preferences.preferences import Preferences
|
14
14
|
from classiq.interface.generator.quantum_function_call import SUFFIX_RANDOMIZER
|
15
|
+
from classiq.interface.generator.types.enum_declaration import EnumDeclaration
|
15
16
|
from classiq.interface.generator.types.struct_declaration import StructDeclaration
|
16
17
|
from classiq.interface.helpers.pydantic_model_helpers import nameables_to_dict
|
17
18
|
from classiq.interface.helpers.versioned_model import VersionedModel
|
@@ -19,9 +20,6 @@ from classiq.interface.model.native_function_definition import NativeFunctionDef
|
|
19
20
|
from classiq.interface.model.quantum_function_declaration import (
|
20
21
|
QuantumFunctionDeclaration,
|
21
22
|
)
|
22
|
-
from classiq.interface.model.resolvers.function_call_resolver import (
|
23
|
-
resolve_function_calls,
|
24
|
-
)
|
25
23
|
from classiq.interface.model.statement_block import StatementBlock
|
26
24
|
|
27
25
|
from classiq.exceptions import ClassiqValueError
|
@@ -65,9 +63,14 @@ class Model(VersionedModel, ASTNode):
|
|
65
63
|
description="The user-defined custom type library.",
|
66
64
|
)
|
67
65
|
|
66
|
+
enums: List[EnumDeclaration] = pydantic.Field(
|
67
|
+
default_factory=list,
|
68
|
+
description="user-defined enums",
|
69
|
+
)
|
70
|
+
|
68
71
|
types: List[StructDeclaration] = pydantic.Field(
|
69
72
|
default_factory=list,
|
70
|
-
description="
|
73
|
+
description="user-defined structs",
|
71
74
|
)
|
72
75
|
|
73
76
|
classical_execution_code: str = pydantic.Field(
|
@@ -118,21 +121,6 @@ class Model(VersionedModel, ASTNode):
|
|
118
121
|
functions.append(_create_empty_main_function())
|
119
122
|
return functions
|
120
123
|
|
121
|
-
@pydantic.root_validator()
|
122
|
-
def validate_static_correctness(cls, values: Dict[str, Any]) -> Dict[str, Any]:
|
123
|
-
functions: Optional[List[QuantumFunctionDeclaration]] = values.get("functions")
|
124
|
-
if functions is None:
|
125
|
-
return values
|
126
|
-
|
127
|
-
resolve_function_calls(
|
128
|
-
values,
|
129
|
-
nameables_to_dict(functions),
|
130
|
-
)
|
131
|
-
for func_def in functions:
|
132
|
-
if isinstance(func_def, NativeFunctionDefinition):
|
133
|
-
func_def.validate_body()
|
134
|
-
return values
|
135
|
-
|
136
124
|
@pydantic.validator("types")
|
137
125
|
def types_validator(cls, types: List[StructDeclaration]) -> List[StructDeclaration]:
|
138
126
|
user_defined_types: Set[str] = set()
|
@@ -4,12 +4,6 @@ from classiq.interface.model.quantum_function_declaration import (
|
|
4
4
|
QuantumFunctionDeclaration,
|
5
5
|
)
|
6
6
|
from classiq.interface.model.statement_block import StatementBlock
|
7
|
-
from classiq.interface.model.validations.handles_validator import HandleValidator
|
8
|
-
from classiq.interface.model.variable_declaration_statement import (
|
9
|
-
VariableDeclarationStatement,
|
10
|
-
)
|
11
|
-
|
12
|
-
from classiq.exceptions import ClassiqValueError
|
13
7
|
|
14
8
|
|
15
9
|
class NativeFunctionDefinition(QuantumFunctionDeclaration):
|
@@ -23,14 +17,3 @@ class NativeFunctionDefinition(QuantumFunctionDeclaration):
|
|
23
17
|
body: StatementBlock = pydantic.Field(
|
24
18
|
default_factory=list, description="List of function calls to perform."
|
25
19
|
)
|
26
|
-
|
27
|
-
def validate_body(self) -> None:
|
28
|
-
handle_validator = HandleValidator(self.port_declarations)
|
29
|
-
|
30
|
-
for statement in self.body:
|
31
|
-
if isinstance(statement, VariableDeclarationStatement):
|
32
|
-
handle_validator.handle_variable_declaration(statement)
|
33
|
-
else:
|
34
|
-
handle_validator.handle_operation(statement)
|
35
|
-
|
36
|
-
handle_validator.report_errored_handles(ClassiqValueError)
|
@@ -1,6 +1,5 @@
|
|
1
1
|
import itertools
|
2
|
-
import
|
3
|
-
from typing import Any, Dict, List, Literal, Mapping, Optional, Set, Type, Union
|
2
|
+
from typing import Any, Dict, List, Literal, Mapping, Optional, Type, Union
|
4
3
|
|
5
4
|
import pydantic
|
6
5
|
|
@@ -19,11 +18,8 @@ from classiq.interface.model.handle_binding import (
|
|
19
18
|
)
|
20
19
|
from classiq.interface.model.quantum_function_declaration import (
|
21
20
|
QuantumFunctionDeclaration,
|
22
|
-
QuantumOperandDeclaration,
|
23
21
|
)
|
24
22
|
from classiq.interface.model.quantum_lambda_function import (
|
25
|
-
QuantumCallable,
|
26
|
-
QuantumLambdaFunction,
|
27
23
|
QuantumOperand,
|
28
24
|
)
|
29
25
|
from classiq.interface.model.quantum_statement import QuantumOperation
|
@@ -117,22 +113,22 @@ class QuantumFunctionCall(QuantumOperation):
|
|
117
113
|
function: Union[str, OperandIdentifier] = pydantic.Field(
|
118
114
|
description="The function that is called"
|
119
115
|
)
|
120
|
-
|
121
|
-
|
116
|
+
designated_params: Dict[str, Expression] = pydantic.Field(default_factory=dict)
|
117
|
+
designated_inputs: Dict[str, HandleBinding] = pydantic.Field(
|
122
118
|
default_factory=dict,
|
123
119
|
description="A mapping from the input name to the wire it connects to",
|
124
120
|
)
|
125
|
-
|
121
|
+
designated_inouts: Dict[
|
126
122
|
str, Union[SlicedHandleBinding, SubscriptHandleBinding, HandleBinding]
|
127
123
|
] = pydantic.Field(
|
128
124
|
default_factory=dict,
|
129
125
|
description="A mapping from in/out name to the wires that connect to it",
|
130
126
|
)
|
131
|
-
|
127
|
+
designated_outputs: Dict[str, HandleBinding] = pydantic.Field(
|
132
128
|
default_factory=dict,
|
133
129
|
description="A mapping from the output name to the wire it connects to",
|
134
130
|
)
|
135
|
-
|
131
|
+
designated_operands: Dict[str, QuantumOperand] = pydantic.Field(
|
136
132
|
description="Function calls passed to the operator",
|
137
133
|
default_factory=dict,
|
138
134
|
)
|
@@ -181,15 +177,15 @@ class QuantumFunctionCall(QuantumOperation):
|
|
181
177
|
def get_positional_args(self) -> List[ArgValue]:
|
182
178
|
result: List[ArgValue] = self.positional_args
|
183
179
|
if not result:
|
184
|
-
result = list(self.
|
185
|
-
result.extend(self.
|
186
|
-
result.extend(self.
|
187
|
-
result.extend(self.
|
188
|
-
result.extend(self.
|
180
|
+
result = list(self.designated_params.values())
|
181
|
+
result.extend(self.designated_operands.values())
|
182
|
+
result.extend(self.designated_inputs.values())
|
183
|
+
result.extend(self.designated_inouts.values())
|
184
|
+
result.extend(self.designated_outputs.values())
|
189
185
|
return result
|
190
186
|
|
191
187
|
@property
|
192
|
-
def
|
188
|
+
def positional_params(self) -> Dict[str, Expression]:
|
193
189
|
return dict(
|
194
190
|
zip(
|
195
191
|
self.func_decl.param_decls.keys(),
|
@@ -202,7 +198,11 @@ class QuantumFunctionCall(QuantumOperation):
|
|
202
198
|
)
|
203
199
|
|
204
200
|
@property
|
205
|
-
def
|
201
|
+
def params(self) -> Dict[str, Expression]:
|
202
|
+
return self.positional_params or self.designated_params
|
203
|
+
|
204
|
+
@property
|
205
|
+
def positional_operands(self) -> Dict[str, "QuantumOperand"]:
|
206
206
|
return dict(
|
207
207
|
zip(
|
208
208
|
self.func_decl.operand_declarations.keys(),
|
@@ -214,6 +214,10 @@ class QuantumFunctionCall(QuantumOperation):
|
|
214
214
|
)
|
215
215
|
)
|
216
216
|
|
217
|
+
@property
|
218
|
+
def operands(self) -> Dict[str, "QuantumOperand"]:
|
219
|
+
return self.positional_operands or self.designated_operands
|
220
|
+
|
217
221
|
@property
|
218
222
|
def pos_port_args(self) -> Dict[str, HandleBinding]:
|
219
223
|
return dict(
|
@@ -227,181 +231,58 @@ class QuantumFunctionCall(QuantumOperation):
|
|
227
231
|
)
|
228
232
|
)
|
229
233
|
|
230
|
-
def
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
else:
|
237
|
-
self.inouts[name] = self.pos_port_args[name]
|
238
|
-
|
239
|
-
def _reduce_positional_args_to_keywords(self) -> None:
|
240
|
-
self.params.update(self.pos_param_args)
|
241
|
-
self.operands.update(self.pos_operand_args)
|
242
|
-
self._update_pos_port_params()
|
243
|
-
|
244
|
-
def resolve_function_decl(
|
245
|
-
self,
|
246
|
-
function_dict: Mapping[str, QuantumFunctionDeclaration],
|
247
|
-
check_operands: bool,
|
248
|
-
) -> None:
|
234
|
+
def _get_pos_port_args_by_direction(
|
235
|
+
self, direction: PortDeclarationDirection
|
236
|
+
) -> Dict[str, HandleBinding]:
|
237
|
+
# This is a hack for handles to wires reduction tests,
|
238
|
+
# that initialize function definitions or calls not in the scope of a model,
|
239
|
+
# so there is no function resolution annotation.
|
249
240
|
if self._func_decl is None:
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
)
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
241
|
+
return dict()
|
242
|
+
return {
|
243
|
+
port_decl.name: port
|
244
|
+
for port_decl, port in zip(
|
245
|
+
self.func_decl.port_declarations.values(),
|
246
|
+
(
|
247
|
+
param
|
248
|
+
for param in self.positional_args
|
249
|
+
if isinstance(param, HandleBinding)
|
250
|
+
),
|
251
|
+
)
|
252
|
+
if direction == port_decl.direction
|
253
|
+
}
|
254
|
+
|
255
|
+
@property
|
256
|
+
def inputs(self) -> Dict[str, HandleBinding]:
|
257
|
+
return (
|
258
|
+
self._get_pos_port_args_by_direction(PortDeclarationDirection.Input)
|
259
|
+
or self.designated_inputs
|
264
260
|
)
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
261
|
+
|
262
|
+
@property
|
263
|
+
def inouts(
|
264
|
+
self,
|
265
|
+
) -> Dict[str, Union[SlicedHandleBinding, SubscriptHandleBinding, HandleBinding]]:
|
266
|
+
return (
|
267
|
+
self._get_pos_port_args_by_direction(PortDeclarationDirection.Inout)
|
268
|
+
or self.designated_inouts
|
270
269
|
)
|
271
|
-
if check_operands:
|
272
|
-
_check_operands_against_declaration(self, self.func_decl, function_dict)
|
273
270
|
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
271
|
+
@property
|
272
|
+
def outputs(self) -> Dict[str, HandleBinding]:
|
273
|
+
return (
|
274
|
+
self._get_pos_port_args_by_direction(PortDeclarationDirection.Output)
|
275
|
+
or self.designated_outputs
|
276
|
+
)
|
279
277
|
|
280
278
|
@pydantic.root_validator()
|
281
279
|
def validate_handles(cls, values: Dict[str, Any]) -> Dict[str, Any]:
|
282
|
-
inputs = values.get("
|
283
|
-
outputs = values.get("
|
284
|
-
inouts = values.get("
|
280
|
+
inputs = values.get("designated_inputs", dict())
|
281
|
+
outputs = values.get("designated_outputs", dict())
|
282
|
+
inouts = values.get("designated_inouts", dict())
|
285
283
|
|
286
284
|
_validate_no_duplicated_ports(inputs, outputs, inouts)
|
287
285
|
_validate_no_duplicated_handles(inputs, outputs, inouts)
|
288
286
|
_validate_no_mixing_sliced_and_whole_handles(inouts)
|
289
287
|
|
290
288
|
return values
|
291
|
-
|
292
|
-
|
293
|
-
def get_lambda_defs(operand: QuantumOperand) -> List[QuantumCallable]:
|
294
|
-
if isinstance(operand, list):
|
295
|
-
return operand
|
296
|
-
return [operand]
|
297
|
-
|
298
|
-
|
299
|
-
def _check_ports_against_declaration(
|
300
|
-
call: QuantumFunctionCall, decl: QuantumFunctionDeclaration
|
301
|
-
) -> None:
|
302
|
-
call_input_names = set(call.inputs.keys())
|
303
|
-
|
304
|
-
_check_params_against_declaration(
|
305
|
-
call_input_names,
|
306
|
-
decl.ports_by_declaration_direction(PortDeclarationDirection.Input),
|
307
|
-
call.func_name,
|
308
|
-
)
|
309
|
-
|
310
|
-
call_output_names = set(call.outputs.keys())
|
311
|
-
|
312
|
-
_check_params_against_declaration(
|
313
|
-
call_output_names,
|
314
|
-
decl.ports_by_declaration_direction(PortDeclarationDirection.Output),
|
315
|
-
call.func_name,
|
316
|
-
)
|
317
|
-
|
318
|
-
inout_params = set(call.inouts.keys())
|
319
|
-
|
320
|
-
_check_params_against_declaration(
|
321
|
-
inout_params,
|
322
|
-
decl.ports_by_declaration_direction(PortDeclarationDirection.Inout),
|
323
|
-
call.func_name,
|
324
|
-
)
|
325
|
-
|
326
|
-
|
327
|
-
def _check_operand_against_declaration(
|
328
|
-
call: QuantumFunctionCall,
|
329
|
-
operand_decl: QuantumOperandDeclaration,
|
330
|
-
operand_argument: QuantumOperand,
|
331
|
-
function_dict: Mapping[str, QuantumFunctionDeclaration],
|
332
|
-
in_list: bool = False,
|
333
|
-
) -> None:
|
334
|
-
if isinstance(operand_argument, list):
|
335
|
-
if in_list:
|
336
|
-
raise ClassiqValueError(
|
337
|
-
f"{str(operand_argument)!r} argument to {call.func_decl.name!r} is not "
|
338
|
-
f"a valid operand. Nested operand lists are not permitted"
|
339
|
-
)
|
340
|
-
for arg in operand_argument:
|
341
|
-
_check_operand_against_declaration(
|
342
|
-
call, operand_decl, arg, function_dict, in_list=True
|
343
|
-
)
|
344
|
-
return
|
345
|
-
operand_arg_decl: QuantumFunctionDeclaration
|
346
|
-
if isinstance(operand_argument, str):
|
347
|
-
if operand_argument not in function_dict:
|
348
|
-
raise ClassiqValueError(
|
349
|
-
f"{operand_argument!r} argument to {call.func_decl.name!r} is not a "
|
350
|
-
f"registered function"
|
351
|
-
)
|
352
|
-
operand_arg_decl = function_dict[operand_argument]
|
353
|
-
elif isinstance(operand_argument, QuantumLambdaFunction):
|
354
|
-
if operand_argument.func_decl is None:
|
355
|
-
return
|
356
|
-
operand_arg_decl = operand_argument.func_decl
|
357
|
-
else:
|
358
|
-
raise ClassiqValueError(
|
359
|
-
f"{str(operand_argument)!r} argument to {call.func_decl.name!r} is not a "
|
360
|
-
f"valid operand"
|
361
|
-
)
|
362
|
-
num_arg_parameters = len(operand_arg_decl.get_positional_arg_decls())
|
363
|
-
num_decl_parameters = len(operand_decl.get_positional_arg_decls())
|
364
|
-
if num_arg_parameters != num_decl_parameters:
|
365
|
-
raise ClassiqValueError(
|
366
|
-
f"Signature of argument {operand_argument!r} to {call.func_decl.name!r} "
|
367
|
-
f"does not match the signature of parameter {operand_decl.name!r}. "
|
368
|
-
f"{operand_decl.name!r} accepts {num_decl_parameters} parameters but "
|
369
|
-
f"{operand_argument!r} accepts {num_arg_parameters} parameters"
|
370
|
-
)
|
371
|
-
|
372
|
-
|
373
|
-
def _check_operands_against_declaration(
|
374
|
-
call: QuantumFunctionCall,
|
375
|
-
decl: QuantumFunctionDeclaration,
|
376
|
-
function_dict: Mapping[str, QuantumFunctionDeclaration],
|
377
|
-
) -> None:
|
378
|
-
for operand_parameter, operand_argument in call.operands.items():
|
379
|
-
_check_operand_against_declaration(
|
380
|
-
call,
|
381
|
-
decl.operand_declarations[operand_parameter],
|
382
|
-
operand_argument,
|
383
|
-
function_dict,
|
384
|
-
)
|
385
|
-
|
386
|
-
|
387
|
-
def _check_params_against_declaration(
|
388
|
-
call_params: Set[str],
|
389
|
-
param_decls: Set[str],
|
390
|
-
callee_name: str,
|
391
|
-
) -> None:
|
392
|
-
unknown_params = call_params - param_decls
|
393
|
-
if any(re.match(r"arg\d+", param) for param in unknown_params):
|
394
|
-
error_msg = (
|
395
|
-
f"Unsupported passing of named function {callee_name!r} as an operand."
|
396
|
-
"\nSuggestion: replace the named function with lambda function."
|
397
|
-
)
|
398
|
-
else:
|
399
|
-
error_msg = f"Unknown parameters {unknown_params} in call to {callee_name!r}."
|
400
|
-
if unknown_params:
|
401
|
-
raise ClassiqValueError(error_msg)
|
402
|
-
|
403
|
-
missing_params = param_decls - call_params
|
404
|
-
if missing_params:
|
405
|
-
raise ClassiqValueError(
|
406
|
-
f"Missing parameters {missing_params} in call to {callee_name!r}."
|
407
|
-
)
|