classiq 0.37.0__py3-none-any.whl → 0.38.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 +2 -2
- classiq/_analyzer_extras/_ipywidgets_async_extension.py +1 -1
- classiq/_analyzer_extras/interactive_hardware.py +3 -3
- classiq/_internals/api_wrapper.py +24 -16
- classiq/_internals/async_utils.py +1 -74
- classiq/_internals/authentication/device.py +9 -4
- classiq/_internals/authentication/password_manager.py +25 -10
- classiq/_internals/authentication/token_manager.py +2 -2
- classiq/_internals/client.py +13 -5
- classiq/_internals/jobs.py +10 -7
- classiq/analyzer/analyzer.py +26 -28
- classiq/analyzer/analyzer_utilities.py +5 -5
- classiq/analyzer/rb.py +4 -5
- classiq/analyzer/show_interactive_hack.py +6 -6
- classiq/applications/benchmarking/mirror_benchmarking.py +9 -6
- classiq/applications/combinatorial_optimization/__init__.py +5 -0
- classiq/applications/qnn/circuit_utils.py +2 -2
- classiq/applications/qnn/gradients/quantum_gradient.py +2 -2
- classiq/applications/qnn/types.py +2 -2
- classiq/applications/qsvm/qsvm.py +4 -7
- classiq/applications/qsvm/qsvm_data_generation.py +2 -5
- classiq/applications_model_constructors/__init__.py +9 -1
- classiq/applications_model_constructors/chemistry_model_constructor.py +9 -16
- classiq/applications_model_constructors/combinatorial_helpers/__init__.py +0 -0
- classiq/applications_model_constructors/combinatorial_helpers/allowed_constraints.py +20 -0
- classiq/applications_model_constructors/combinatorial_helpers/arithmetic/__init__.py +0 -0
- classiq/applications_model_constructors/combinatorial_helpers/arithmetic/arithmetic_expression.py +35 -0
- classiq/applications_model_constructors/combinatorial_helpers/arithmetic/isolation.py +42 -0
- classiq/applications_model_constructors/combinatorial_helpers/combinatorial_problem_utils.py +130 -0
- classiq/applications_model_constructors/combinatorial_helpers/encoding_mapping.py +107 -0
- classiq/applications_model_constructors/combinatorial_helpers/encoding_utils.py +122 -0
- classiq/applications_model_constructors/combinatorial_helpers/memory.py +79 -0
- classiq/applications_model_constructors/combinatorial_helpers/multiple_comp_basis_sp.py +34 -0
- classiq/applications_model_constructors/combinatorial_helpers/optimization_model.py +166 -0
- classiq/applications_model_constructors/combinatorial_helpers/pauli_helpers/__init__.py +0 -0
- classiq/applications_model_constructors/combinatorial_helpers/pauli_helpers/pauli_sparsing.py +31 -0
- classiq/applications_model_constructors/combinatorial_helpers/pauli_helpers/pauli_utils.py +65 -0
- classiq/applications_model_constructors/combinatorial_helpers/py.typed +0 -0
- classiq/applications_model_constructors/combinatorial_helpers/pyomo_utils.py +243 -0
- classiq/applications_model_constructors/combinatorial_helpers/sympy_utils.py +22 -0
- classiq/applications_model_constructors/combinatorial_helpers/transformations/__init__.py +0 -0
- classiq/applications_model_constructors/combinatorial_helpers/transformations/encoding.py +194 -0
- classiq/applications_model_constructors/combinatorial_helpers/transformations/fixed_variables.py +144 -0
- classiq/applications_model_constructors/combinatorial_helpers/transformations/ising_converter.py +124 -0
- classiq/applications_model_constructors/combinatorial_helpers/transformations/penalty.py +32 -0
- classiq/applications_model_constructors/combinatorial_helpers/transformations/penalty_support.py +41 -0
- classiq/applications_model_constructors/combinatorial_helpers/transformations/sign_seperation.py +75 -0
- classiq/applications_model_constructors/combinatorial_helpers/transformations/slack_variables.py +90 -0
- classiq/applications_model_constructors/combinatorial_optimization_model_constructor.py +48 -91
- classiq/applications_model_constructors/finance_model_constructor.py +4 -17
- classiq/applications_model_constructors/grover_model_constructor.py +20 -91
- classiq/applications_model_constructors/libraries/qmci_library.py +17 -19
- classiq/builtin_functions/standard_gates.py +1 -1
- classiq/exceptions.py +43 -1
- classiq/executor.py +10 -9
- classiq/interface/_version.py +1 -1
- classiq/interface/analyzer/analysis_params.py +6 -3
- classiq/interface/analyzer/result.py +12 -4
- classiq/interface/applications/qsvm.py +13 -1
- classiq/interface/backend/backend_preferences.py +4 -2
- classiq/interface/backend/pydantic_backend.py +3 -1
- classiq/interface/backend/quantum_backend_providers.py +1 -0
- classiq/interface/chemistry/fermionic_operator.py +15 -13
- classiq/interface/chemistry/ground_state_problem.py +18 -3
- classiq/interface/chemistry/molecule.py +8 -6
- classiq/interface/chemistry/operator.py +20 -14
- classiq/interface/combinatorial_optimization/examples/ascending_sequence.py +1 -1
- classiq/interface/combinatorial_optimization/examples/greater_than_ilp.py +1 -1
- classiq/interface/combinatorial_optimization/examples/ilp.py +2 -1
- classiq/interface/combinatorial_optimization/examples/integer_portfolio_optimization.py +2 -2
- classiq/interface/combinatorial_optimization/examples/mds.py +2 -1
- classiq/interface/combinatorial_optimization/examples/mht.py +3 -3
- classiq/interface/combinatorial_optimization/examples/mis.py +4 -1
- classiq/interface/combinatorial_optimization/examples/mvc.py +2 -1
- classiq/interface/combinatorial_optimization/examples/set_cover.py +2 -1
- classiq/interface/combinatorial_optimization/examples/tsp.py +4 -3
- classiq/interface/combinatorial_optimization/examples/tsp_digraph.py +6 -2
- classiq/interface/combinatorial_optimization/mht_qaoa_input.py +9 -3
- classiq/interface/executor/aws_execution_cost.py +4 -3
- classiq/interface/executor/estimation.py +2 -2
- classiq/interface/executor/execution_preferences.py +5 -34
- classiq/interface/executor/execution_request.py +19 -17
- classiq/interface/executor/optimizer_preferences.py +22 -13
- classiq/interface/executor/{quantum_program.py → quantum_code.py} +21 -15
- classiq/interface/executor/quantum_instruction_set.py +2 -1
- classiq/interface/executor/register_initialization.py +1 -3
- classiq/interface/executor/result.py +41 -10
- classiq/interface/executor/vqe_result.py +1 -1
- classiq/interface/finance/function_input.py +17 -4
- classiq/interface/finance/gaussian_model_input.py +3 -1
- classiq/interface/finance/log_normal_model_input.py +3 -1
- classiq/interface/finance/model_input.py +2 -0
- classiq/interface/generator/amplitude_loading.py +6 -3
- classiq/interface/generator/application_apis/__init__.py +1 -0
- classiq/interface/generator/application_apis/arithmetic_declarations.py +14 -0
- classiq/interface/generator/arith/argument_utils.py +14 -4
- classiq/interface/generator/arith/arithmetic.py +3 -1
- classiq/interface/generator/arith/arithmetic_arg_type_validator.py +12 -13
- classiq/interface/generator/arith/arithmetic_expression_abc.py +4 -1
- classiq/interface/generator/arith/arithmetic_expression_parser.py +8 -2
- classiq/interface/generator/arith/arithmetic_expression_validator.py +16 -2
- classiq/interface/generator/arith/arithmetic_operations.py +5 -10
- classiq/interface/generator/arith/ast_node_rewrite.py +1 -1
- classiq/interface/generator/arith/binary_ops.py +202 -54
- classiq/interface/generator/arith/extremum_operations.py +5 -3
- classiq/interface/generator/arith/logical_ops.py +4 -2
- classiq/interface/generator/arith/machine_precision.py +3 -0
- classiq/interface/generator/arith/number_utils.py +34 -44
- classiq/interface/generator/arith/register_user_input.py +21 -1
- classiq/interface/generator/arith/unary_ops.py +16 -25
- classiq/interface/generator/chemistry_function_params.py +4 -4
- classiq/interface/generator/commuting_pauli_exponentiation.py +3 -1
- classiq/interface/generator/compiler_keywords.py +4 -0
- classiq/interface/generator/complex_type.py +3 -10
- classiq/interface/generator/control_state.py +5 -3
- classiq/interface/generator/credit_risk_example/linear_gci.py +10 -3
- classiq/interface/generator/credit_risk_example/weighted_adder.py +14 -4
- classiq/interface/generator/expressions/atomic_expression_functions.py +5 -3
- classiq/interface/generator/expressions/evaluated_expression.py +18 -4
- classiq/interface/generator/expressions/expression.py +1 -1
- classiq/interface/generator/expressions/qmod_qscalar_proxy.py +33 -0
- classiq/interface/generator/expressions/sympy_supported_expressions.py +2 -1
- classiq/interface/generator/finance.py +1 -1
- classiq/interface/generator/function_params.py +7 -6
- classiq/interface/generator/functions/__init__.py +1 -1
- classiq/interface/generator/functions/core_lib_declarations/quantum_functions/std_lib_functions.py +505 -138
- classiq/interface/generator/functions/core_lib_declarations/quantum_operators.py +25 -99
- classiq/interface/generator/functions/foreign_function_definition.py +12 -4
- classiq/interface/generator/functions/function_implementation.py +8 -4
- classiq/interface/generator/functions/native_function_definition.py +4 -2
- classiq/interface/generator/functions/register.py +4 -2
- classiq/interface/generator/functions/register_mapping_data.py +14 -10
- classiq/interface/generator/generated_circuit_data.py +2 -2
- classiq/interface/generator/grover_operator.py +5 -3
- classiq/interface/generator/hamiltonian_evolution/suzuki_trotter.py +5 -1
- classiq/interface/generator/hardware/hardware_data.py +6 -4
- classiq/interface/generator/hardware_efficient_ansatz.py +25 -8
- classiq/interface/generator/hartree_fock.py +3 -1
- classiq/interface/generator/linear_pauli_rotations.py +3 -1
- classiq/interface/generator/mcu.py +5 -3
- classiq/interface/generator/mcx.py +7 -5
- classiq/interface/generator/model/constraints.py +2 -1
- classiq/interface/generator/model/model.py +11 -19
- classiq/interface/generator/model/preferences/preferences.py +4 -3
- classiq/interface/generator/oracles/custom_oracle.py +4 -2
- classiq/interface/generator/oracles/oracle_abc.py +2 -2
- classiq/interface/generator/qpe.py +6 -4
- classiq/interface/generator/qsvm.py +5 -8
- classiq/interface/generator/quantum_function_call.py +21 -16
- classiq/interface/generator/{generated_circuit.py → quantum_program.py} +10 -14
- classiq/interface/generator/range_types.py +3 -1
- classiq/interface/generator/slice_parsing_utils.py +8 -3
- classiq/interface/generator/standard_gates/controlled_standard_gates.py +4 -2
- classiq/interface/generator/state_preparation/metrics.py +2 -1
- classiq/interface/generator/state_preparation/state_preparation.py +7 -5
- classiq/interface/generator/state_propagator.py +16 -5
- classiq/interface/generator/types/builtin_struct_declarations/__init__.py +0 -1
- classiq/interface/generator/types/struct_declaration.py +8 -3
- classiq/interface/generator/ucc.py +6 -4
- classiq/interface/generator/unitary_gate.py +7 -3
- classiq/interface/generator/validations/flow_graph.py +6 -4
- classiq/interface/generator/validations/validator_functions.py +6 -4
- classiq/interface/hardware.py +2 -2
- classiq/interface/helpers/custom_encoders.py +3 -0
- classiq/interface/helpers/pydantic_model_helpers.py +0 -6
- classiq/interface/helpers/validation_helpers.py +1 -1
- classiq/interface/helpers/versioned_model.py +4 -1
- classiq/interface/ide/show.py +2 -2
- classiq/interface/jobs.py +72 -3
- classiq/interface/model/bind_operation.py +18 -11
- classiq/interface/model/call_synthesis_data.py +68 -0
- classiq/interface/model/inplace_binary_operation.py +2 -2
- classiq/interface/model/model.py +27 -21
- classiq/interface/model/native_function_definition.py +3 -5
- classiq/interface/model/quantum_expressions/amplitude_loading_operation.py +9 -4
- classiq/interface/model/quantum_expressions/control_state.py +2 -2
- classiq/interface/model/quantum_function_call.py +25 -139
- classiq/interface/model/quantum_function_declaration.py +8 -0
- classiq/interface/model/quantum_if_operation.py +2 -3
- classiq/interface/model/quantum_lambda_function.py +64 -0
- classiq/interface/model/quantum_type.py +57 -56
- classiq/interface/model/quantum_variable_declaration.py +1 -1
- classiq/interface/model/statement_block.py +32 -0
- classiq/interface/model/validations/handles_validator.py +14 -12
- classiq/interface/model/within_apply_operation.py +11 -0
- classiq/interface/pyomo_extension/pyomo_sympy_bimap.py +4 -1
- classiq/interface/server/routes.py +5 -0
- classiq/model/function_handler.py +5 -9
- classiq/model/model.py +2 -19
- classiq/qmod/__init__.py +13 -6
- classiq/qmod/builtins/classical_execution_primitives.py +27 -36
- classiq/qmod/builtins/classical_functions.py +24 -14
- classiq/qmod/builtins/functions.py +162 -145
- classiq/qmod/builtins/operations.py +24 -35
- classiq/qmod/builtins/structs.py +15 -15
- classiq/qmod/cfunc.py +42 -0
- classiq/qmod/classical_function.py +6 -14
- classiq/qmod/declaration_inferrer.py +12 -21
- classiq/qmod/expression_query.py +23 -0
- classiq/qmod/model_state_container.py +2 -0
- classiq/qmod/native/__init__.py +0 -0
- classiq/qmod/native/expression_to_qmod.py +189 -0
- classiq/qmod/native/pretty_printer.py +311 -0
- classiq/qmod/qfunc.py +27 -0
- classiq/qmod/qmod_constant.py +76 -0
- classiq/qmod/qmod_parameter.py +34 -12
- classiq/qmod/qmod_struct.py +3 -3
- classiq/qmod/qmod_variable.py +102 -18
- classiq/qmod/quantum_expandable.py +16 -16
- classiq/qmod/quantum_function.py +37 -8
- classiq/qmod/symbolic.py +47 -4
- classiq/qmod/symbolic_expr.py +9 -0
- classiq/qmod/utilities.py +13 -0
- classiq/qmod/write_qmod.py +39 -0
- classiq/quantum_functions/__init__.py +2 -2
- classiq/quantum_functions/annotation_parser.py +9 -11
- classiq/quantum_functions/function_parser.py +1 -1
- classiq/quantum_functions/quantum_function.py +3 -3
- classiq/quantum_register.py +17 -9
- {classiq-0.37.0.dist-info → classiq-0.38.0.dist-info}/METADATA +2 -1
- {classiq-0.37.0.dist-info → classiq-0.38.0.dist-info}/RECORD +222 -186
- {classiq-0.37.0.dist-info → classiq-0.38.0.dist-info}/WHEEL +1 -1
- classiq/interface/generator/expressions/qmod_qnum_proxy.py +0 -22
- classiq/interface/generator/types/builtin_struct_declarations/qaoa_declarations.py +0 -23
- classiq/interface/generator/types/combinatorial_problem.py +0 -26
- classiq/interface/model/numeric_reinterpretation.py +0 -25
- classiq/interface/model/operator_synthesis_data.py +0 -48
- classiq/model/function_handler.pyi +0 -152
@@ -1,4 +1,4 @@
|
|
1
|
-
from typing import List
|
1
|
+
from typing import Any, Dict, List
|
2
2
|
|
3
3
|
import numpy as np
|
4
4
|
import pydantic
|
@@ -11,6 +11,8 @@ from classiq.interface.generator.function_params import (
|
|
11
11
|
DEFAULT_OUTPUT_NAME,
|
12
12
|
)
|
13
13
|
|
14
|
+
from classiq.exceptions import ClassiqValueError
|
15
|
+
|
14
16
|
|
15
17
|
class StatePropagator(function_params.FunctionParams):
|
16
18
|
"""
|
@@ -33,16 +35,25 @@ class StatePropagator(function_params.FunctionParams):
|
|
33
35
|
)
|
34
36
|
|
35
37
|
@pydantic.validator("start_state_vector", always=True)
|
36
|
-
def validate_start_state(
|
38
|
+
def validate_start_state(
|
39
|
+
cls, start_state_vector: List[Complex], values: Dict[str, Any]
|
40
|
+
) -> List[Complex]:
|
37
41
|
end_state_vector = values.get("end_state_vector")
|
42
|
+
if end_state_vector is None:
|
43
|
+
raise ClassiqValueError(
|
44
|
+
"Cannot validate start_start_vector without end_state_vector"
|
45
|
+
)
|
46
|
+
|
38
47
|
num_qubits = cls._num_qubits(end_state_vector)
|
39
48
|
if len(start_state_vector) == 0:
|
40
|
-
default_start_state_vector = [0.0 for _ in range(2**num_qubits)]
|
41
|
-
default_start_state_vector[0] = 1.0
|
49
|
+
default_start_state_vector = [Complex(0.0) for _ in range(2**num_qubits)]
|
50
|
+
default_start_state_vector[0] = Complex(1.0)
|
42
51
|
start_state_vector = default_start_state_vector
|
43
52
|
|
44
53
|
if len(start_state_vector) != len(end_state_vector):
|
45
|
-
raise
|
54
|
+
raise ClassiqValueError(
|
55
|
+
"Start and end state vectors are of non-equal length"
|
56
|
+
)
|
46
57
|
|
47
58
|
return start_state_vector
|
48
59
|
|
@@ -15,11 +15,16 @@ class StructDeclaration(HashablePydanticBaseModel):
|
|
15
15
|
|
16
16
|
variables: Dict[str, ConcreteClassicalType] = pydantic.Field(
|
17
17
|
default_factory=dict,
|
18
|
-
description="Dictionary of variable names and their classical
|
18
|
+
description="Dictionary of variable names and their classical types",
|
19
19
|
)
|
20
20
|
|
21
21
|
BUILTIN_STRUCT_DECLARATIONS: ClassVar[Dict[str, "StructDeclaration"]] = {}
|
22
22
|
|
23
23
|
def validate_fields(self, fields: Mapping[str, Any]) -> None:
|
24
|
-
|
25
|
-
|
24
|
+
expected_field_names = list(self.variables.keys())
|
25
|
+
received_field_names = list(fields.keys())
|
26
|
+
if set(expected_field_names) != set(received_field_names):
|
27
|
+
raise ClassiqValueError(
|
28
|
+
f"Invalid fields for {self.name} instance. Expected fields "
|
29
|
+
f"{expected_field_names}, got {received_field_names}"
|
30
|
+
)
|
@@ -10,6 +10,8 @@ from classiq.interface.generator.excitations import (
|
|
10
10
|
EXCITATIONS_TYPE_EXACT,
|
11
11
|
)
|
12
12
|
|
13
|
+
from classiq.exceptions import ClassiqValueError
|
14
|
+
|
13
15
|
_EXCITATIONS_DICT = {"s": 1, "d": 2, "t": 3, "q": 4}
|
14
16
|
|
15
17
|
DEFAULT_EXCITATIONS = [1, 2]
|
@@ -44,7 +46,7 @@ class UCC(ChemistryFunctionParams):
|
|
44
46
|
def _validate_excitations(cls, excitations: EXCITATIONS_TYPE) -> EXCITATIONS_TYPE:
|
45
47
|
if isinstance(excitations, int):
|
46
48
|
if excitations not in _EXCITATIONS_DICT.values():
|
47
|
-
raise
|
49
|
+
raise ClassiqValueError(
|
48
50
|
f"possible values of excitations are {list(_EXCITATIONS_DICT.values())}"
|
49
51
|
)
|
50
52
|
excitations = [excitations]
|
@@ -53,19 +55,19 @@ class UCC(ChemistryFunctionParams):
|
|
53
55
|
excitations = list(excitations) # type: ignore[assignment]
|
54
56
|
if all(isinstance(idx, int) for idx in excitations):
|
55
57
|
if any(idx not in _EXCITATIONS_DICT.values() for idx in excitations):
|
56
|
-
raise
|
58
|
+
raise ClassiqValueError(
|
57
59
|
f"possible values of excitations are {list(_EXCITATIONS_DICT.values())}"
|
58
60
|
)
|
59
61
|
|
60
62
|
elif all(isinstance(idx, str) for idx in excitations):
|
61
63
|
if any(idx not in _EXCITATIONS_DICT.keys() for idx in excitations):
|
62
|
-
raise
|
64
|
+
raise ClassiqValueError(
|
63
65
|
f"possible values of excitations are {list(_EXCITATIONS_DICT.keys())}"
|
64
66
|
)
|
65
67
|
excitations = sorted(_EXCITATIONS_DICT[idx] for idx in excitations) # type: ignore[index]
|
66
68
|
|
67
69
|
else:
|
68
|
-
raise
|
70
|
+
raise ClassiqValueError(
|
69
71
|
"excitations must be of the same type (all str or all int)"
|
70
72
|
)
|
71
73
|
return excitations
|
@@ -6,6 +6,8 @@ import pydantic
|
|
6
6
|
from classiq.interface.generator import complex_type, function_params
|
7
7
|
from classiq.interface.generator.arith.register_user_input import RegisterArithmeticInfo
|
8
8
|
|
9
|
+
from classiq.exceptions import ClassiqValueError
|
10
|
+
|
9
11
|
DataNumber = Union[complex_type.Complex, float, int]
|
10
12
|
DataArray = List[List[DataNumber]]
|
11
13
|
|
@@ -28,11 +30,13 @@ class UnitaryGate(function_params.FunctionParams):
|
|
28
30
|
def validate_data(cls, data: DataArray) -> DataArray:
|
29
31
|
data_np = np.array(data, dtype=object)
|
30
32
|
if data_np.ndim != 2:
|
31
|
-
raise
|
33
|
+
raise ClassiqValueError("Data must me two dimensional")
|
32
34
|
if data_np.shape[0] != data_np.shape[1]:
|
33
|
-
raise
|
35
|
+
raise ClassiqValueError("Matrix must be square")
|
34
36
|
if not np.mod(np.log2(data_np.shape[0]), 1) == 0:
|
35
|
-
raise
|
37
|
+
raise ClassiqValueError(
|
38
|
+
"Matrix dimensions must be an integer exponent of 2"
|
39
|
+
)
|
36
40
|
return data
|
37
41
|
|
38
42
|
@property
|
@@ -11,6 +11,8 @@ from classiq.interface.generator.quantum_function_call import (
|
|
11
11
|
)
|
12
12
|
from classiq.interface.helpers.custom_pydantic_types import PydanticNonEmptyString
|
13
13
|
|
14
|
+
from classiq.exceptions import ClassiqValueError
|
15
|
+
|
14
16
|
IO_MULTI_USE_ERROR_MSG = "Input and output names can only be used once"
|
15
17
|
UNCONNECTED_WIRES_ERROR_MSG = "Wires connected only on one end"
|
16
18
|
UNCONNECTED_FLOW_IO_ERROR_MSG = "Flow IOs not connected to inner calls"
|
@@ -40,7 +42,7 @@ def _parse_call_inputs(
|
|
40
42
|
wire = wires[wire_name]
|
41
43
|
|
42
44
|
if wire.end:
|
43
|
-
raise
|
45
|
+
raise ClassiqValueError(
|
44
46
|
IO_MULTI_USE_ERROR_MSG
|
45
47
|
+ f". The name {wire_name} is used multiple times."
|
46
48
|
)
|
@@ -62,7 +64,7 @@ def _parse_call_outputs(
|
|
62
64
|
wire = wires[wire_name]
|
63
65
|
|
64
66
|
if wire.start:
|
65
|
-
raise
|
67
|
+
raise ClassiqValueError(
|
66
68
|
IO_MULTI_USE_ERROR_MSG
|
67
69
|
+ f". The name {wire_name} is used multiple times."
|
68
70
|
)
|
@@ -148,7 +150,7 @@ def validate_legal_wiring(
|
|
148
150
|
if unconnected_wires:
|
149
151
|
error_messages.append(f"{UNCONNECTED_WIRES_ERROR_MSG}: {unconnected_wires}")
|
150
152
|
|
151
|
-
raise
|
153
|
+
raise ClassiqValueError(_join_errors(error_messages))
|
152
154
|
|
153
155
|
|
154
156
|
def _join_errors(error_messages: List[str]) -> str:
|
@@ -180,7 +182,7 @@ def validate_acyclic_logic_flow(
|
|
180
182
|
|
181
183
|
if not nx.algorithms.is_directed_acyclic_graph(graph):
|
182
184
|
cycles = list(nx.algorithms.simple_cycles(graph))
|
183
|
-
raise
|
185
|
+
raise ClassiqValueError(CYCLE_ERROR_MSG + ". Cycles are: " + str(cycles))
|
184
186
|
|
185
187
|
return graph
|
186
188
|
|
@@ -4,6 +4,8 @@ import numpy as np
|
|
4
4
|
|
5
5
|
from classiq.interface.helpers.custom_pydantic_types import PydanticProbabilityFloat
|
6
6
|
|
7
|
+
from classiq.exceptions import ClassiqValueError
|
8
|
+
|
7
9
|
NOT_SUM_TO_ONE_ERROR = "Probabilities do not sum to 1"
|
8
10
|
|
9
11
|
SUM_TO_ONE_SENSITIVITY = 8
|
@@ -26,9 +28,9 @@ def is_probabilities_sum_to_one(pro: Iterable[PydanticProbabilityFloat]) -> bool
|
|
26
28
|
|
27
29
|
def validate_amplitudes(amp: Amplitude) -> Amplitude:
|
28
30
|
if not is_amplitudes_sum_to_one(amp):
|
29
|
-
raise
|
31
|
+
raise ClassiqValueError("Amplitudes do not sum to 1")
|
30
32
|
if not _is_power_of_two(amp):
|
31
|
-
raise
|
33
|
+
raise ClassiqValueError("Amplitudes length must be power of 2")
|
32
34
|
return amp
|
33
35
|
|
34
36
|
|
@@ -36,7 +38,7 @@ def validate_probabilities(
|
|
36
38
|
cls: type, pmf: Sequence[PydanticProbabilityFloat]
|
37
39
|
) -> Sequence[PydanticProbabilityFloat]:
|
38
40
|
if not is_probabilities_sum_to_one(pmf):
|
39
|
-
raise
|
41
|
+
raise ClassiqValueError(NOT_SUM_TO_ONE_ERROR)
|
40
42
|
if not _is_power_of_two(pmf):
|
41
|
-
raise
|
43
|
+
raise ClassiqValueError("Probabilities length must be power of 2")
|
42
44
|
return pmf
|
classiq/interface/hardware.py
CHANGED
@@ -17,8 +17,8 @@ class Provider(StrEnum):
|
|
17
17
|
OQC = "OQC"
|
18
18
|
|
19
19
|
@property
|
20
|
-
def id(self):
|
21
|
-
return self.value.replace(" ", "-").lower()
|
20
|
+
def id(self) -> "ProviderIDEnum":
|
21
|
+
return self.value.replace(" ", "-").lower() # type: ignore[return-value]
|
22
22
|
|
23
23
|
|
24
24
|
ProviderIDEnum = StrEnum("ProviderIDEnum", {p.id: p.id for p in Provider}) # type: ignore[misc]
|
@@ -1,11 +1,5 @@
|
|
1
1
|
from typing import Any, Dict, Protocol, Sequence, TypeVar
|
2
2
|
|
3
|
-
import pydantic
|
4
|
-
|
5
|
-
|
6
|
-
def get_discriminator_field(default: str, **kwargs: Any) -> Any:
|
7
|
-
return pydantic.Field(default_factory=lambda: default, **kwargs)
|
8
|
-
|
9
3
|
|
10
4
|
def values_with_discriminator(
|
11
5
|
values: Dict[str, Any], discriminator: str, discriminator_value: Any
|
@@ -12,7 +12,7 @@ def is_list_unique(lst: List[Hashable]) -> bool:
|
|
12
12
|
def validate_nameables_mapping(
|
13
13
|
nameables_dict: Mapping[str, Nameable], declaration_type: str
|
14
14
|
) -> None:
|
15
|
-
if not all(
|
15
|
+
if not all(name == nameable.name for (name, nameable) in nameables_dict.items()):
|
16
16
|
raise ClassiqValueError(
|
17
17
|
f"{declaration_type} declaration names should match the keys of their names."
|
18
18
|
)
|
@@ -1,7 +1,10 @@
|
|
1
1
|
import pydantic
|
2
2
|
|
3
3
|
from classiq.interface._version import VERSION
|
4
|
+
from classiq.interface.helpers.custom_encoders import CUSTOM_ENCODERS
|
4
5
|
|
5
6
|
|
6
|
-
class VersionedModel(
|
7
|
+
class VersionedModel(
|
8
|
+
pydantic.BaseModel, extra=pydantic.Extra.forbid, json_encoders=CUSTOM_ENCODERS
|
9
|
+
):
|
7
10
|
version: str = pydantic.Field(default=VERSION)
|
classiq/interface/ide/show.py
CHANGED
@@ -4,7 +4,7 @@ import pydantic
|
|
4
4
|
|
5
5
|
from classiq.interface.analyzer.result import QasmCode
|
6
6
|
|
7
|
-
from classiq import
|
7
|
+
from classiq import QuantumProgram
|
8
8
|
from classiq._internals.api_wrapper import ApiWrapper
|
9
9
|
from classiq._internals.async_utils import syncify_function
|
10
10
|
from classiq.exceptions import ClassiqValueError
|
@@ -28,7 +28,7 @@ CANT_PARSE_QUANTUM_PROGRAM_MSG = (
|
|
28
28
|
|
29
29
|
def show(quantum_program: SerializedQuantumProgram) -> None:
|
30
30
|
try:
|
31
|
-
circuit =
|
31
|
+
circuit = QuantumProgram.parse_raw(quantum_program)
|
32
32
|
except pydantic.error_wrappers.ValidationError as exc:
|
33
33
|
raise ClassiqValueError(CANT_PARSE_QUANTUM_PROGRAM_MSG) from exc
|
34
34
|
circuit.show() # type: ignore[attr-defined]
|
classiq/interface/jobs.py
CHANGED
@@ -1,9 +1,11 @@
|
|
1
|
-
from typing import Any, Dict, Generic, TypeVar, Union
|
1
|
+
from typing import Any, Dict, Generic, Literal, TypeVar, Union
|
2
2
|
|
3
3
|
import pydantic
|
4
|
-
from pydantic import BaseModel
|
4
|
+
from pydantic import BaseModel, Field
|
5
5
|
from pydantic.generics import GenericModel
|
6
6
|
|
7
|
+
from classiq.interface.helpers.custom_encoders import CUSTOM_ENCODERS
|
8
|
+
|
7
9
|
from classiq._internals.enum_utils import StrEnum
|
8
10
|
|
9
11
|
JSONObject = Dict[str, Any]
|
@@ -29,6 +31,73 @@ class JobStatus(StrEnum):
|
|
29
31
|
return self in (self.COMPLETED, self.FAILED, self.CANCELLED)
|
30
32
|
|
31
33
|
|
32
|
-
|
34
|
+
"""
|
35
|
+
A job can be in either of 3 states: ongoing, completed successfully or completed
|
36
|
+
unsuccessfully. Each job status belongs to one of the 3 states
|
37
|
+
The class JobDescriptionBase represents a job description, regardless of its state
|
38
|
+
JobDescriptionSuccess represents a job that was completed successfully. It contains the
|
39
|
+
job result in the description field. The type of the result depends on the route, and
|
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")])
|
49
|
+
"""
|
50
|
+
|
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
|
+
|
64
|
+
|
65
|
+
class FailureDetails(BaseModel):
|
66
|
+
details: str
|
67
|
+
|
68
|
+
|
69
|
+
class JobDescriptionBase(GenericModel, Generic[T], json_encoders=CUSTOM_ENCODERS):
|
70
|
+
kind: str
|
33
71
|
status: JobStatus
|
72
|
+
description: Union[T, FailureDetails, Dict]
|
73
|
+
|
74
|
+
|
75
|
+
class JobDescriptionSuccess(JobDescriptionBase[T], Generic[T]):
|
76
|
+
kind: Literal["success"] = pydantic.Field(default="success")
|
77
|
+
status: SuccessStatus = Field(default=JobStatus.COMPLETED)
|
34
78
|
description: T
|
79
|
+
|
80
|
+
|
81
|
+
class JobDescriptionFailure(JobDescriptionBase[Any]):
|
82
|
+
kind: Literal["failure"] = pydantic.Field(default="failure")
|
83
|
+
status: FailureStatus
|
84
|
+
description: FailureDetails
|
85
|
+
|
86
|
+
|
87
|
+
class JobDescriptionNonFinal(JobDescriptionBase[Any]):
|
88
|
+
kind: Literal["non_final"] = pydantic.Field(default="non_final")
|
89
|
+
status: NonFinalStatus
|
90
|
+
description: Dict = Field(default_factory=dict)
|
91
|
+
|
92
|
+
@pydantic.validator("description")
|
93
|
+
def validate_empty_description(cls, description: Dict) -> Dict:
|
94
|
+
if description:
|
95
|
+
raise ValueError("Non-final job description must be empty")
|
96
|
+
|
97
|
+
return description
|
98
|
+
|
99
|
+
|
100
|
+
class JobDescriptionUnion(GenericModel, Generic[T], json_encoders=CUSTOM_ENCODERS):
|
101
|
+
__root__: Union[
|
102
|
+
JobDescriptionSuccess[T], JobDescriptionFailure, JobDescriptionNonFinal
|
103
|
+
] = Field(discriminator="kind")
|
@@ -1,4 +1,4 @@
|
|
1
|
-
from typing import Mapping
|
1
|
+
from typing import List, Mapping
|
2
2
|
|
3
3
|
import pydantic
|
4
4
|
|
@@ -12,19 +12,26 @@ BIND_OUTPUT_NAME = "bind_output"
|
|
12
12
|
|
13
13
|
|
14
14
|
class BindOperation(QuantumOperation):
|
15
|
-
|
16
|
-
|
15
|
+
in_handles: List[HandleBinding]
|
16
|
+
out_handles: List[HandleBinding]
|
17
17
|
|
18
18
|
@property
|
19
19
|
def wiring_inputs(self) -> Mapping[str, HandleBinding]:
|
20
|
-
return {
|
20
|
+
return {
|
21
|
+
f"{BIND_INPUT_NAME}_{i}": handle for i, handle in enumerate(self.in_handles)
|
22
|
+
}
|
21
23
|
|
22
24
|
@property
|
23
25
|
def wiring_outputs(self) -> Mapping[str, HandleBinding]:
|
24
|
-
return {
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
26
|
+
return {
|
27
|
+
f"{BIND_OUTPUT_NAME}_{i}": handle
|
28
|
+
for i, handle in enumerate(self.out_handles)
|
29
|
+
}
|
30
|
+
|
31
|
+
@pydantic.validator("in_handles", "out_handles")
|
32
|
+
def validate_handle(cls, handles: List[HandleBinding]) -> List[HandleBinding]:
|
33
|
+
for handle in handles:
|
34
|
+
if not handle.is_bindable():
|
35
|
+
raise ClassiqValueError(f"Cannot bind '{handle}'")
|
36
|
+
|
37
|
+
return handles
|
@@ -0,0 +1,68 @@
|
|
1
|
+
import dataclasses
|
2
|
+
from typing import Any, List, Mapping
|
3
|
+
|
4
|
+
from sympy import sympify
|
5
|
+
|
6
|
+
from classiq.interface.generator.control_state import ControlState
|
7
|
+
from classiq.interface.generator.synthesis_execution_parameter import PydanticPowerType
|
8
|
+
|
9
|
+
from classiq.exceptions import ClassiqValueError
|
10
|
+
|
11
|
+
ILLEGAL_NESTED_POWER_ERROR = "Nested power calls with a parametric power and an integer power are unsupported: {a}, {b}"
|
12
|
+
|
13
|
+
|
14
|
+
def _merge_power(a: PydanticPowerType, b: PydanticPowerType) -> PydanticPowerType:
|
15
|
+
symbolic_res = sympify(a) * sympify(b)
|
16
|
+
if symbolic_res.is_Integer:
|
17
|
+
return int(symbolic_res)
|
18
|
+
elif symbolic_res.is_symbol:
|
19
|
+
return str(symbolic_res)
|
20
|
+
else:
|
21
|
+
raise ClassiqValueError(ILLEGAL_NESTED_POWER_ERROR.format(a=a, b=b))
|
22
|
+
|
23
|
+
|
24
|
+
@dataclasses.dataclass
|
25
|
+
class CallSynthesisData(Mapping):
|
26
|
+
power: PydanticPowerType = 1
|
27
|
+
is_inverse: bool = False
|
28
|
+
control_states: List[ControlState] = dataclasses.field(default_factory=list)
|
29
|
+
should_control: bool = True
|
30
|
+
|
31
|
+
def merge(self, other: "CallSynthesisData") -> "CallSynthesisData":
|
32
|
+
return CallSynthesisData(
|
33
|
+
power=_merge_power(self.power, other.power),
|
34
|
+
is_inverse=self.is_inverse != other.is_inverse,
|
35
|
+
control_states=self.control_states + other.control_states,
|
36
|
+
should_control=self.should_control and other.should_control,
|
37
|
+
)
|
38
|
+
|
39
|
+
def set_control(self, ctrl_name: str, ctrl_size: int) -> None:
|
40
|
+
self.control_states = [
|
41
|
+
ControlState(
|
42
|
+
name=ctrl_name,
|
43
|
+
num_ctrl_qubits=ctrl_size,
|
44
|
+
)
|
45
|
+
]
|
46
|
+
|
47
|
+
def update_control_state(self, ctrl_size: int, ctrl_state: str) -> None:
|
48
|
+
prev_ctrl_state = self.control_states.pop()
|
49
|
+
self.control_states.append(
|
50
|
+
ControlState(
|
51
|
+
name=prev_ctrl_state.name,
|
52
|
+
num_ctrl_qubits=ctrl_size,
|
53
|
+
ctrl_state=ctrl_state,
|
54
|
+
)
|
55
|
+
)
|
56
|
+
|
57
|
+
@property
|
58
|
+
def has_control(self) -> bool:
|
59
|
+
return bool(self.control_states)
|
60
|
+
|
61
|
+
def __getitem__(self, key: str) -> Any:
|
62
|
+
return dataclasses.asdict(self)[key]
|
63
|
+
|
64
|
+
def __iter__(self):
|
65
|
+
return iter(dataclasses.asdict(self))
|
66
|
+
|
67
|
+
def __len__(self):
|
68
|
+
return len(dataclasses.asdict(self))
|
classiq/interface/model/model.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
from
|
1
|
+
from collections import Counter
|
2
|
+
from typing import Any, Dict, List, Literal, NewType, Optional, Set
|
2
3
|
|
3
4
|
import pydantic
|
4
5
|
|
@@ -10,23 +11,17 @@ from classiq.interface.generator.functions.port_declaration import (
|
|
10
11
|
from classiq.interface.generator.model.constraints import Constraints
|
11
12
|
from classiq.interface.generator.model.preferences.preferences import Preferences
|
12
13
|
from classiq.interface.generator.quantum_function_call import SUFFIX_RANDOMIZER
|
13
|
-
from classiq.interface.generator.types.combinatorial_problem import (
|
14
|
-
CombinatorialOptimizationStructDeclaration,
|
15
|
-
)
|
16
14
|
from classiq.interface.generator.types.struct_declaration import StructDeclaration
|
17
|
-
from classiq.interface.helpers.pydantic_model_helpers import
|
18
|
-
get_discriminator_field,
|
19
|
-
nameables_to_dict,
|
20
|
-
)
|
15
|
+
from classiq.interface.helpers.pydantic_model_helpers import nameables_to_dict
|
21
16
|
from classiq.interface.helpers.versioned_model import VersionedModel
|
22
17
|
from classiq.interface.model.native_function_definition import NativeFunctionDefinition
|
23
|
-
from classiq.interface.model.quantum_function_call import ConcreteQuantumStatement
|
24
18
|
from classiq.interface.model.quantum_function_declaration import (
|
25
19
|
QuantumFunctionDeclaration,
|
26
20
|
)
|
27
21
|
from classiq.interface.model.resolvers.function_call_resolver import (
|
28
22
|
resolve_function_calls,
|
29
23
|
)
|
24
|
+
from classiq.interface.model.statement_block import StatementBlock
|
30
25
|
|
31
26
|
from classiq.exceptions import ClassiqValueError
|
32
27
|
|
@@ -39,10 +34,6 @@ DEFAULT_PORT_SIZE = 1
|
|
39
34
|
|
40
35
|
SerializedModel = NewType("SerializedModel", str)
|
41
36
|
|
42
|
-
ConcreteStructDeclaration = Union[
|
43
|
-
CombinatorialOptimizationStructDeclaration, StructDeclaration
|
44
|
-
]
|
45
|
-
|
46
37
|
TYPE_NAME_CONFLICT_BUILTIN = (
|
47
38
|
"Type '{name}' conflicts with a builtin type with the same name"
|
48
39
|
)
|
@@ -65,7 +56,7 @@ class Model(VersionedModel):
|
|
65
56
|
All the relevant data for generating quantum circuit in one place.
|
66
57
|
"""
|
67
58
|
|
68
|
-
kind: Literal["user"] =
|
59
|
+
kind: Literal["user"] = pydantic.Field(default=USER_MODEL_MARKER)
|
69
60
|
|
70
61
|
# Must be validated before logic_flow
|
71
62
|
functions: List[NativeFunctionDefinition] = pydantic.Field(
|
@@ -73,7 +64,7 @@ class Model(VersionedModel):
|
|
73
64
|
description="The user-defined custom type library.",
|
74
65
|
)
|
75
66
|
|
76
|
-
types: List[
|
67
|
+
types: List[StructDeclaration] = pydantic.Field(
|
77
68
|
default_factory=list,
|
78
69
|
description="The user-defined custom function library.",
|
79
70
|
)
|
@@ -98,7 +89,7 @@ class Model(VersionedModel):
|
|
98
89
|
return self.function_dict[MAIN_FUNCTION_NAME] # type:ignore[return-value]
|
99
90
|
|
100
91
|
@property
|
101
|
-
def body(self) ->
|
92
|
+
def body(self) -> StatementBlock:
|
102
93
|
return self.main_func.body
|
103
94
|
|
104
95
|
@pydantic.validator("preferences", always=True)
|
@@ -142,15 +133,15 @@ class Model(VersionedModel):
|
|
142
133
|
return values
|
143
134
|
|
144
135
|
@pydantic.validator("types")
|
145
|
-
def types_validator(
|
146
|
-
cls, types: List[ConcreteStructDeclaration]
|
147
|
-
) -> List[ConcreteStructDeclaration]:
|
136
|
+
def types_validator(cls, types: List[StructDeclaration]) -> List[StructDeclaration]:
|
148
137
|
user_defined_types: Set[str] = set()
|
149
138
|
for ctype in types:
|
150
139
|
if ctype.name in StructDeclaration.BUILTIN_STRUCT_DECLARATIONS:
|
151
|
-
raise
|
140
|
+
raise ClassiqValueError(
|
141
|
+
TYPE_NAME_CONFLICT_BUILTIN.format(name=ctype.name)
|
142
|
+
)
|
152
143
|
if ctype.name in user_defined_types:
|
153
|
-
raise
|
144
|
+
raise ClassiqValueError(TYPE_NAME_CONFLICT_USER.format(name=ctype.name))
|
154
145
|
user_defined_types.add(ctype.name)
|
155
146
|
|
156
147
|
return types
|
@@ -174,3 +165,18 @@ class Model(VersionedModel):
|
|
174
165
|
raise ClassiqValueError("Function 'main' cannot declare quantum inputs")
|
175
166
|
|
176
167
|
return functions
|
168
|
+
|
169
|
+
@pydantic.validator("constants")
|
170
|
+
def _validate_constants(cls, constants: List[Constant]) -> List[Constant]:
|
171
|
+
constant_definition_counts = Counter(
|
172
|
+
[constant.name for constant in constants]
|
173
|
+
).items()
|
174
|
+
multiply_defined_constants = {
|
175
|
+
constant for constant, count in constant_definition_counts if count > 1
|
176
|
+
}
|
177
|
+
if len(multiply_defined_constants) > 0:
|
178
|
+
raise ClassiqValueError(
|
179
|
+
f"The following constants were defined more than once: "
|
180
|
+
f"{multiply_defined_constants}"
|
181
|
+
)
|
182
|
+
return constants
|
@@ -1,11 +1,9 @@
|
|
1
|
-
from typing import List
|
2
|
-
|
3
1
|
import pydantic
|
4
2
|
|
5
|
-
from classiq.interface.model.quantum_function_call import ConcreteQuantumStatement
|
6
3
|
from classiq.interface.model.quantum_function_declaration import (
|
7
4
|
QuantumFunctionDeclaration,
|
8
5
|
)
|
6
|
+
from classiq.interface.model.statement_block import StatementBlock
|
9
7
|
from classiq.interface.model.validations.handles_validator import HandleValidator
|
10
8
|
from classiq.interface.model.variable_declaration_statement import (
|
11
9
|
VariableDeclarationStatement,
|
@@ -22,7 +20,7 @@ class NativeFunctionDefinition(QuantumFunctionDeclaration):
|
|
22
20
|
objects from other classes.
|
23
21
|
"""
|
24
22
|
|
25
|
-
body:
|
23
|
+
body: StatementBlock = pydantic.Field(
|
26
24
|
default_factory=list, description="List of function calls to perform."
|
27
25
|
)
|
28
26
|
|
@@ -33,6 +31,6 @@ class NativeFunctionDefinition(QuantumFunctionDeclaration):
|
|
33
31
|
if isinstance(statement, VariableDeclarationStatement):
|
34
32
|
handle_validator.handle_variable_declaration(statement)
|
35
33
|
else:
|
36
|
-
handle_validator.
|
34
|
+
handle_validator.handle_operation(statement)
|
37
35
|
|
38
36
|
handle_validator.report_errored_handles(ClassiqValueError)
|
@@ -36,9 +36,14 @@ class AmplitudeLoadingOperation(QuantumAssignmentOperation):
|
|
36
36
|
) -> Mapping[
|
37
37
|
str, Union[SlicedHandleBinding, SubscriptHandleBinding, HandleBinding]
|
38
38
|
]:
|
39
|
-
|
40
|
-
|
41
|
-
|
39
|
+
inouts = {self.result_name(): self.result_var}
|
40
|
+
if len(self.var_handles) == 1:
|
41
|
+
inouts[AMPLITUDE_IO_NAME] = self.var_handles[0]
|
42
|
+
return inouts
|
43
|
+
|
44
|
+
@property
|
45
|
+
def wiring_outputs(self) -> Mapping[str, HandleBinding]:
|
46
|
+
return {}
|
42
47
|
|
43
48
|
def initialize_var_types(
|
44
49
|
self,
|
@@ -50,7 +55,7 @@ class AmplitudeLoadingOperation(QuantumAssignmentOperation):
|
|
50
55
|
var_type = var_types[self.var_handles[0].name]
|
51
56
|
if not (
|
52
57
|
isinstance(var_type, QuantumNumeric)
|
53
|
-
and var_type.
|
58
|
+
and var_type.fraction_digits_value == var_type.size_in_bits
|
54
59
|
):
|
55
60
|
raise ClassiqValueError(VAR_DOMAIN_ILLEGAL)
|
56
61
|
super().initialize_var_types(var_types, machine_precision)
|