classiq 0.37.1__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.1.dist-info → classiq-0.38.0.dist-info}/METADATA +2 -1
- {classiq-0.37.1.dist-info → classiq-0.38.0.dist-info}/RECORD +222 -186
- {classiq-0.37.1.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,5 +1,6 @@
|
|
1
1
|
import ast
|
2
2
|
import builtins
|
3
|
+
import re
|
3
4
|
from _ast import AST
|
4
5
|
from typing import Any, Optional, Set, Tuple, Type, Union
|
5
6
|
|
@@ -15,6 +16,7 @@ from classiq.exceptions import ClassiqArithmeticError, ClassiqValueError
|
|
15
16
|
DEFAULT_SUPPORTED_FUNC_NAMES: Set[str] = {"CLShift", "CRShift", "min", "max"}
|
16
17
|
|
17
18
|
DEFAULT_EXPRESSION_TYPE = "arithmetic"
|
19
|
+
IDENITIFIER_REGEX = re.compile(r"[a-zA-Z_][a-zA-Z0-9_]*")
|
18
20
|
|
19
21
|
_REPEATED_VARIABLES_ERROR_MESSAGE: str = (
|
20
22
|
"Repeated variables in the beginning of an arithmetic expression are not allowed."
|
@@ -75,17 +77,25 @@ class ExpressionValidator(ast.NodeVisitor):
|
|
75
77
|
|
76
78
|
def validate(self, expression: str) -> None:
|
77
79
|
try:
|
78
|
-
|
80
|
+
adjusted_expression = self._get_adjusted_expression(expression)
|
81
|
+
ast_expr = ast.parse(adjusted_expression, filename="", mode=self._mode)
|
79
82
|
except SyntaxError as e:
|
80
83
|
raise ClassiqValueError(f"Failed to parse expression {expression!r}") from e
|
81
84
|
try:
|
82
|
-
self._ast_obj =
|
85
|
+
self._ast_obj = self.rewrite_ast(ast_expr)
|
83
86
|
self.visit(self._ast_obj)
|
84
87
|
except RecursionError as e:
|
85
88
|
raise ClassiqValueError(
|
86
89
|
f"Failed to parse expression since it is too long: {expression}"
|
87
90
|
) from e
|
88
91
|
|
92
|
+
@staticmethod
|
93
|
+
def _get_adjusted_expression(expression: str) -> str:
|
94
|
+
# This works around the simplification of the trivial expressions such as a + 0, 1 * a, etc.
|
95
|
+
if IDENITIFIER_REGEX.fullmatch(expression):
|
96
|
+
return f"0 + {expression}"
|
97
|
+
return expression
|
98
|
+
|
89
99
|
@property
|
90
100
|
def ast_obj(self) -> ast.AST:
|
91
101
|
if not self._ast_obj:
|
@@ -182,6 +192,10 @@ class ExpressionValidator(ast.NodeVisitor):
|
|
182
192
|
self._supported_functions.add(node.name)
|
183
193
|
self.generic_visit(node)
|
184
194
|
|
195
|
+
@classmethod
|
196
|
+
def rewrite_ast(cls, expression_ast: AST) -> AST:
|
197
|
+
return expression_ast
|
198
|
+
|
185
199
|
|
186
200
|
def validate_expression(
|
187
201
|
expression: str,
|
@@ -1,11 +1,11 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
1
|
import abc
|
4
2
|
from typing import ClassVar, Iterable, Optional, Tuple
|
5
3
|
|
6
4
|
import pydantic
|
7
5
|
|
8
|
-
from classiq.interface.generator.arith import
|
6
|
+
from classiq.interface.generator.arith.machine_precision import (
|
7
|
+
DEFAULT_MACHINE_PRECISION,
|
8
|
+
)
|
9
9
|
from classiq.interface.generator.arith.register_user_input import RegisterArithmeticInfo
|
10
10
|
from classiq.interface.generator.function_params import FunctionParams
|
11
11
|
|
@@ -14,7 +14,7 @@ DEFAULT_GARBAGE_OUT_NAME: str = "extra_qubits"
|
|
14
14
|
|
15
15
|
class ArithmeticOperationParams(FunctionParams):
|
16
16
|
output_size: Optional[pydantic.PositiveInt]
|
17
|
-
machine_precision: pydantic.PositiveInt =
|
17
|
+
machine_precision: pydantic.PositiveInt = DEFAULT_MACHINE_PRECISION
|
18
18
|
output_name: ClassVar[str]
|
19
19
|
garbage_output_name: ClassVar[str] = DEFAULT_GARBAGE_OUT_NAME
|
20
20
|
_result_register: Optional[RegisterArithmeticInfo] = pydantic.PrivateAttr(
|
@@ -46,11 +46,6 @@ class ArithmeticOperationParams(FunctionParams):
|
|
46
46
|
return suggested_bounds
|
47
47
|
return None
|
48
48
|
|
49
|
-
def _compute_fraction_places(self, argument: argument_utils.RegisterOrConst) -> int:
|
50
|
-
return argument_utils.fraction_places(
|
51
|
-
argument, machine_precision=self.machine_precision
|
52
|
-
)
|
53
|
-
|
54
49
|
@abc.abstractmethod
|
55
|
-
def get_params_inplace_options(self) -> Iterable[ArithmeticOperationParams]:
|
50
|
+
def get_params_inplace_options(self) -> Iterable["ArithmeticOperationParams"]:
|
56
51
|
pass
|
@@ -30,7 +30,7 @@ class AstNodeRewrite(ast.NodeTransformer):
|
|
30
30
|
elif hasattr(node, "op"):
|
31
31
|
return type(node.op).__name__ + next(self.count_str_gen)
|
32
32
|
elif hasattr(node, "func"):
|
33
|
-
return node.func
|
33
|
+
return self.extract_node_id(node.func)
|
34
34
|
elif hasattr(node, "value"):
|
35
35
|
return node.value
|
36
36
|
elif hasattr(node, "ops"):
|
@@ -8,6 +8,7 @@ from typing import (
|
|
8
8
|
Literal,
|
9
9
|
Optional,
|
10
10
|
Tuple,
|
11
|
+
Type,
|
11
12
|
TypeVar,
|
12
13
|
Union,
|
13
14
|
)
|
@@ -28,6 +29,7 @@ from classiq.interface.generator.arith.unary_ops import Negation
|
|
28
29
|
from classiq.interface.generator.function_params import get_zero_input_name
|
29
30
|
|
30
31
|
from classiq._internals.enum_utils import StrEnum
|
32
|
+
from classiq.exceptions import ClassiqValueError
|
31
33
|
|
32
34
|
LeftDataT = TypeVar("LeftDataT")
|
33
35
|
RightDataT = TypeVar("RightDataT")
|
@@ -61,23 +63,36 @@ class BinaryOpParams(
|
|
61
63
|
left_arg = values.get("left_arg")
|
62
64
|
right_arg = values.get("right_arg")
|
63
65
|
if isinstance(left_arg, Numeric) and isinstance(right_arg, Numeric):
|
64
|
-
raise
|
66
|
+
raise ClassiqValueError("One argument must be a register")
|
65
67
|
if left_arg is right_arg and isinstance(left_arg, pydantic.BaseModel):
|
66
68
|
# In case both arguments refer to the same object, copy it.
|
67
69
|
# This prevents changes performed on one argument to affect the other.
|
68
70
|
values["right_arg"] = left_arg.copy(deep=True)
|
69
71
|
return values
|
70
72
|
|
73
|
+
def garbage_output_size(self) -> pydantic.NonNegativeInt:
|
74
|
+
return 0
|
75
|
+
|
71
76
|
def _create_ios(self) -> None:
|
72
77
|
self._inputs = dict()
|
73
78
|
if isinstance(self.left_arg, RegisterArithmeticInfo):
|
74
79
|
self._inputs[self.left_arg_name] = self.left_arg
|
75
80
|
if isinstance(self.right_arg, RegisterArithmeticInfo):
|
76
81
|
self._inputs[self.right_arg_name] = self.right_arg
|
77
|
-
zero_input_name = get_zero_input_name(self.output_name)
|
78
|
-
self._zero_inputs = {zero_input_name: self.result_register}
|
79
82
|
self._outputs = {**self._inputs, self.output_name: self.result_register}
|
80
83
|
|
84
|
+
garbage_size = self.garbage_output_size()
|
85
|
+
if garbage_size > 0:
|
86
|
+
self._outputs[self.garbage_output_name] = RegisterArithmeticInfo(
|
87
|
+
size=garbage_size
|
88
|
+
)
|
89
|
+
|
90
|
+
zero_input_name = get_zero_input_name(self.output_name)
|
91
|
+
zero_input_size = self.result_register.size + garbage_size
|
92
|
+
self._zero_inputs = {
|
93
|
+
zero_input_name: RegisterArithmeticInfo(size=zero_input_size)
|
94
|
+
}
|
95
|
+
|
81
96
|
def is_inplaced(self) -> bool:
|
82
97
|
return False
|
83
98
|
|
@@ -96,18 +111,17 @@ class InplacableBinaryOpParams(
|
|
96
111
|
right_arg = values.get("right_arg")
|
97
112
|
inplace_arg: Optional[ArgToInplace] = values.get("inplace_arg")
|
98
113
|
if inplace_arg == ArgToInplace.RIGHT and isinstance(right_arg, Numeric):
|
99
|
-
raise
|
114
|
+
raise ClassiqValueError(
|
115
|
+
_NumericArgumentInplaceErrorMessage.format(right_arg)
|
116
|
+
)
|
100
117
|
elif inplace_arg == ArgToInplace.LEFT and isinstance(left_arg, Numeric):
|
101
|
-
raise
|
118
|
+
raise ClassiqValueError(
|
119
|
+
_NumericArgumentInplaceErrorMessage.format(left_arg)
|
120
|
+
)
|
102
121
|
return values
|
103
122
|
|
104
123
|
def _create_ios(self) -> None:
|
105
124
|
BinaryOpParams._create_ios(self)
|
106
|
-
garbage_size = self.garbage_output_size()
|
107
|
-
if garbage_size > 0:
|
108
|
-
self._outputs[self.garbage_output_name] = RegisterArithmeticInfo(
|
109
|
-
size=garbage_size
|
110
|
-
)
|
111
125
|
if self.inplace_arg is None:
|
112
126
|
return
|
113
127
|
inplace_arg_name = (
|
@@ -117,7 +131,7 @@ class InplacableBinaryOpParams(
|
|
117
131
|
)
|
118
132
|
self._outputs.pop(inplace_arg_name)
|
119
133
|
|
120
|
-
self._set_inplace_zero_inputs(inplace_arg_name,
|
134
|
+
self._set_inplace_zero_inputs(inplace_arg_name, self.garbage_output_size())
|
121
135
|
|
122
136
|
def _set_inplace_zero_inputs(
|
123
137
|
self, inplace_arg_name: str, garbage_size: int
|
@@ -193,7 +207,7 @@ class BinaryOpWithIntInputs(BinaryOpParams[RegisterOrInt, RegisterOrInt]):
|
|
193
207
|
and right_arg.fraction_places > 0
|
194
208
|
)
|
195
209
|
if is_left_arg_float_register or is_right_arg_float_register:
|
196
|
-
raise
|
210
|
+
raise ClassiqValueError(BOOLEAN_OP_WITH_FRACTIONS_ERROR)
|
197
211
|
return values
|
198
212
|
|
199
213
|
@staticmethod
|
@@ -251,19 +265,28 @@ class Adder(InplacableBinaryOpParams[RegisterOrConst, RegisterOrConst]):
|
|
251
265
|
ub = argument_utils.upper_bound(self.left_arg) + argument_utils.upper_bound(
|
252
266
|
self.right_arg
|
253
267
|
)
|
254
|
-
integer_part_size = number_utils.bounds_to_integer_part_size(lb, ub)
|
255
268
|
fraction_places = max(
|
256
|
-
|
257
|
-
|
269
|
+
argument_utils.fraction_places(self.left_arg),
|
270
|
+
argument_utils.fraction_places(self.right_arg),
|
258
271
|
)
|
259
|
-
size_needed = integer_part_size + fraction_places
|
260
272
|
return RegisterArithmeticInfo(
|
261
|
-
size=self.output_size or
|
273
|
+
size=self.output_size or self._get_output_size(ub, lb, fraction_places),
|
262
274
|
fraction_places=fraction_places,
|
263
275
|
is_signed=self._include_sign and lb < 0,
|
264
276
|
bounds=(lb, ub) if self._include_sign else None,
|
265
277
|
)
|
266
278
|
|
279
|
+
def _get_output_size(self, ub: float, lb: float, fraction_places: int) -> int:
|
280
|
+
if isinstance(self.left_arg, float) and self.left_arg == 0.0:
|
281
|
+
assert isinstance(self.right_arg, RegisterArithmeticInfo)
|
282
|
+
return self.right_arg.size
|
283
|
+
elif isinstance(self.right_arg, float) and self.right_arg == 0.0:
|
284
|
+
assert isinstance(self.left_arg, RegisterArithmeticInfo)
|
285
|
+
return self.left_arg.size
|
286
|
+
|
287
|
+
integer_part_size = number_utils.bounds_to_integer_part_size(lb, ub)
|
288
|
+
return integer_part_size + fraction_places
|
289
|
+
|
267
290
|
|
268
291
|
class Subtractor(InplacableBinaryOpParams[RegisterOrConst, RegisterOrConst]):
|
269
292
|
output_name = "difference"
|
@@ -275,19 +298,28 @@ class Subtractor(InplacableBinaryOpParams[RegisterOrConst, RegisterOrConst]):
|
|
275
298
|
argument_utils.upper_bound(self.left_arg)
|
276
299
|
- argument_utils.lower_bound(self.right_arg),
|
277
300
|
)
|
278
|
-
integer_part_size = number_utils.bounds_to_integer_part_size(*bounds)
|
279
301
|
fraction_places = max(
|
280
|
-
|
281
|
-
|
302
|
+
argument_utils.fraction_places(self.left_arg),
|
303
|
+
argument_utils.fraction_places(self.right_arg),
|
282
304
|
)
|
283
|
-
|
305
|
+
|
284
306
|
return RegisterArithmeticInfo(
|
285
|
-
size=self.output_size or
|
307
|
+
size=self.output_size or self._get_output_size(bounds, fraction_places),
|
286
308
|
fraction_places=fraction_places,
|
287
309
|
is_signed=self._include_sign and min(bounds) < 0,
|
288
310
|
bounds=self._legal_bounds(bounds),
|
289
311
|
)
|
290
312
|
|
313
|
+
def _get_output_size(
|
314
|
+
self, bounds: Tuple[float, float], fraction_places: int
|
315
|
+
) -> int:
|
316
|
+
if isinstance(self.right_arg, float) and self.right_arg == 0:
|
317
|
+
assert isinstance(self.left_arg, RegisterArithmeticInfo)
|
318
|
+
return self.left_arg.size
|
319
|
+
integer_part_size = number_utils.bounds_to_integer_part_size(*bounds)
|
320
|
+
size_needed = integer_part_size + fraction_places
|
321
|
+
return size_needed
|
322
|
+
|
291
323
|
def garbage_output_size(self) -> pydantic.NonNegativeInt:
|
292
324
|
if not isinstance(self.right_arg, RegisterArithmeticInfo):
|
293
325
|
adder_params = Adder(
|
@@ -327,7 +359,7 @@ class Subtractor(InplacableBinaryOpParams[RegisterOrConst, RegisterOrConst]):
|
|
327
359
|
return self.inplace_arg == ArgToInplace.LEFT
|
328
360
|
|
329
361
|
def _expected_negation_output_size(self) -> int:
|
330
|
-
return
|
362
|
+
return argument_utils.fraction_places(self.right_arg) + min(
|
331
363
|
self.result_register.integer_part_size,
|
332
364
|
number_utils.bounds_to_integer_part_size(
|
333
365
|
*(-bound for bound in argument_utils.bounds(self.right_arg))
|
@@ -356,31 +388,80 @@ class Subtractor(InplacableBinaryOpParams[RegisterOrConst, RegisterOrConst]):
|
|
356
388
|
class Multiplier(BinaryOpWithFloatInputs):
|
357
389
|
output_name = "product"
|
358
390
|
|
359
|
-
def
|
360
|
-
|
361
|
-
|
362
|
-
|
391
|
+
def expected_fraction_places(self) -> int:
|
392
|
+
return argument_utils.fraction_places(
|
393
|
+
argument_utils.limit_fraction_places(
|
394
|
+
self.left_arg, machine_precision=self.machine_precision
|
395
|
+
)
|
396
|
+
) + argument_utils.fraction_places(
|
397
|
+
argument_utils.limit_fraction_places(
|
398
|
+
self.right_arg, machine_precision=self.machine_precision
|
399
|
+
)
|
400
|
+
)
|
401
|
+
|
402
|
+
@staticmethod
|
403
|
+
def _get_bounds(
|
404
|
+
args: Tuple[RegisterOrConst, RegisterOrConst], machine_precision: int
|
405
|
+
) -> Tuple[float, float]:
|
363
406
|
extremal_values = [
|
364
407
|
left * right
|
365
|
-
for left in argument_utils.bounds(
|
366
|
-
for right in argument_utils.bounds(
|
408
|
+
for left in argument_utils.bounds(args[0])
|
409
|
+
for right in argument_utils.bounds(args[1])
|
367
410
|
]
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
411
|
+
return (
|
412
|
+
number_utils.limit_fraction_places(
|
413
|
+
min(extremal_values), machine_precision=machine_precision
|
414
|
+
),
|
415
|
+
number_utils.limit_fraction_places(
|
416
|
+
max(extremal_values), machine_precision=machine_precision
|
417
|
+
),
|
418
|
+
)
|
419
|
+
|
420
|
+
def _get_result_register(self) -> RegisterArithmeticInfo:
|
421
|
+
fraction_places = min(self.machine_precision, self.expected_fraction_places())
|
422
|
+
left_arg = argument_utils.limit_fraction_places(
|
423
|
+
self.left_arg, machine_precision=self.machine_precision
|
375
424
|
)
|
425
|
+
right_arg = argument_utils.limit_fraction_places(
|
426
|
+
self.right_arg, machine_precision=self.machine_precision
|
427
|
+
)
|
428
|
+
bounds = self._get_bounds((left_arg, right_arg), self.machine_precision)
|
429
|
+
|
376
430
|
return RegisterArithmeticInfo(
|
377
431
|
size=self.output_size
|
378
|
-
or
|
432
|
+
or self._get_output_size(bounds, fraction_places, left_arg, right_arg),
|
379
433
|
fraction_places=fraction_places,
|
380
434
|
is_signed=self._include_sign and min(bounds) < 0,
|
381
435
|
bounds=self._legal_bounds(bounds),
|
382
436
|
)
|
383
437
|
|
438
|
+
@staticmethod
|
439
|
+
def _get_output_size(
|
440
|
+
bounds: Tuple[float, float],
|
441
|
+
fraction_places: int,
|
442
|
+
left_arg: Union[RegisterArithmeticInfo, float],
|
443
|
+
right_arg: Union[RegisterArithmeticInfo, float],
|
444
|
+
) -> int:
|
445
|
+
if isinstance(left_arg, float) and left_arg == 1.0:
|
446
|
+
assert isinstance(right_arg, RegisterArithmeticInfo)
|
447
|
+
return right_arg.size
|
448
|
+
elif isinstance(right_arg, float) and right_arg == 1.0:
|
449
|
+
assert isinstance(left_arg, RegisterArithmeticInfo)
|
450
|
+
return left_arg.size
|
451
|
+
largest_bound = max(bounds, key=abs)
|
452
|
+
integer_places = int(largest_bound).bit_length() + int(largest_bound < 0)
|
453
|
+
extra_sign_bit = int(
|
454
|
+
argument_utils.is_signed(left_arg)
|
455
|
+
and argument_utils.is_signed(right_arg)
|
456
|
+
and largest_bound > 0
|
457
|
+
)
|
458
|
+
return max(1, integer_places + fraction_places + extra_sign_bit)
|
459
|
+
|
460
|
+
def garbage_output_size(self) -> pydantic.NonNegativeInt:
|
461
|
+
return max(
|
462
|
+
0, self.expected_fraction_places() - self.result_register.fraction_places
|
463
|
+
)
|
464
|
+
|
384
465
|
|
385
466
|
class Comparator(BinaryOpWithFloatInputs):
|
386
467
|
output_size: Literal[1] = 1
|
@@ -419,26 +500,53 @@ class Power(BinaryOpParams[RegisterArithmeticInfo, pydantic.PositiveInt]):
|
|
419
500
|
@pydantic.validator("right_arg", pre=True)
|
420
501
|
def _validate_legal_power(cls, right_arg: Any) -> pydantic.PositiveInt:
|
421
502
|
if not float(right_arg).is_integer():
|
422
|
-
raise
|
503
|
+
raise ClassiqValueError("Power must be an integer")
|
423
504
|
if right_arg <= 0:
|
424
|
-
raise
|
505
|
+
raise ClassiqValueError("Power must be greater than one")
|
425
506
|
return int(right_arg)
|
426
507
|
|
508
|
+
def expected_fraction_places(self) -> int:
|
509
|
+
return (
|
510
|
+
argument_utils.fraction_places(
|
511
|
+
argument_utils.limit_fraction_places(
|
512
|
+
self.left_arg, machine_precision=self.machine_precision
|
513
|
+
)
|
514
|
+
)
|
515
|
+
* self.right_arg
|
516
|
+
)
|
517
|
+
|
427
518
|
def _get_result_bounds(self) -> Tuple[float, float]:
|
428
|
-
|
519
|
+
bounds = [
|
520
|
+
number_utils.limit_fraction_places(
|
521
|
+
bound, machine_precision=self.machine_precision
|
522
|
+
)
|
523
|
+
for bound in self.left_arg.bounds
|
524
|
+
]
|
525
|
+
if (self.right_arg % 2) or min(bounds) >= 0:
|
429
526
|
return (
|
430
|
-
|
431
|
-
|
527
|
+
number_utils.limit_fraction_places(
|
528
|
+
bounds[0] ** self.right_arg,
|
529
|
+
machine_precision=self.machine_precision,
|
530
|
+
),
|
531
|
+
number_utils.limit_fraction_places(
|
532
|
+
bounds[1] ** self.right_arg,
|
533
|
+
machine_precision=self.machine_precision,
|
534
|
+
),
|
432
535
|
)
|
433
|
-
return 0.0,
|
536
|
+
return 0.0, number_utils.limit_fraction_places(
|
537
|
+
max(abs(bound) for bound in bounds) ** self.right_arg,
|
538
|
+
machine_precision=self.machine_precision,
|
539
|
+
)
|
434
540
|
|
435
541
|
def _get_result_register(self) -> RegisterArithmeticInfo:
|
436
542
|
if self.output_size:
|
437
543
|
return RegisterArithmeticInfo(size=self.output_size)
|
438
544
|
|
439
|
-
fraction_places
|
545
|
+
fraction_places = min(self.machine_precision, self.expected_fraction_places())
|
440
546
|
bounds = self._get_result_bounds()
|
441
547
|
size = number_utils.bounds_to_integer_part_size(*bounds) + fraction_places
|
548
|
+
if bounds[0] == bounds[1]:
|
549
|
+
size = 1
|
442
550
|
return RegisterArithmeticInfo(
|
443
551
|
size=size,
|
444
552
|
is_signed=self.left_arg.is_signed and (self.right_arg % 2 == 1),
|
@@ -446,6 +554,50 @@ class Power(BinaryOpParams[RegisterArithmeticInfo, pydantic.PositiveInt]):
|
|
446
554
|
bounds=bounds,
|
447
555
|
)
|
448
556
|
|
557
|
+
def _get_inner_action_garbage_size(
|
558
|
+
self,
|
559
|
+
action_type: Union[Type["Power"], Type[Multiplier]],
|
560
|
+
*,
|
561
|
+
arg: RegisterArithmeticInfo,
|
562
|
+
action_right_arg: RegisterOrConst,
|
563
|
+
compute_power: int
|
564
|
+
) -> pydantic.NonNegativeInt:
|
565
|
+
inner_compute_power_params = Power(
|
566
|
+
left_arg=arg,
|
567
|
+
right_arg=compute_power,
|
568
|
+
output_size=self.output_size,
|
569
|
+
machine_precision=self.machine_precision,
|
570
|
+
)
|
571
|
+
return action_type(
|
572
|
+
left_arg=inner_compute_power_params.result_register,
|
573
|
+
right_arg=action_right_arg,
|
574
|
+
output_size=self.output_size,
|
575
|
+
machine_precision=self.machine_precision,
|
576
|
+
).garbage_output_size()
|
577
|
+
|
578
|
+
def garbage_output_size(self) -> pydantic.NonNegativeInt:
|
579
|
+
arg = self.left_arg
|
580
|
+
power = self.right_arg
|
581
|
+
if power == 1:
|
582
|
+
return 0
|
583
|
+
if (
|
584
|
+
power == 2
|
585
|
+
or (arg.size == 1 and arg.fraction_places == 0)
|
586
|
+
or self.output_size == 1
|
587
|
+
):
|
588
|
+
return max(
|
589
|
+
0,
|
590
|
+
self.expected_fraction_places() - self.result_register.fraction_places,
|
591
|
+
)
|
592
|
+
|
593
|
+
if power % 2 == 0:
|
594
|
+
return self._get_inner_action_garbage_size(
|
595
|
+
Power, arg=arg, action_right_arg=power // 2, compute_power=2
|
596
|
+
)
|
597
|
+
return self._get_inner_action_garbage_size(
|
598
|
+
Multiplier, arg=arg, action_right_arg=arg, compute_power=power - 1
|
599
|
+
)
|
600
|
+
|
449
601
|
|
450
602
|
class EffectiveUnaryOpParams(
|
451
603
|
InplacableBinaryOpParams[RegisterArithmeticInfo, RightDataT], Generic[RightDataT]
|
@@ -465,9 +617,9 @@ class LShift(EffectiveUnaryOpParams[pydantic.NonNegativeInt]):
|
|
465
617
|
arg = values.get("left_arg")
|
466
618
|
shift = values.get("right_arg")
|
467
619
|
if not isinstance(arg, RegisterArithmeticInfo):
|
468
|
-
raise
|
620
|
+
raise ClassiqValueError("left arg must be a RegisterArithmeticInfo")
|
469
621
|
if not isinstance(shift, int):
|
470
|
-
raise
|
622
|
+
raise ClassiqValueError("Shift must be an integer")
|
471
623
|
assert arg.fraction_places - shift <= 0, _FLOATING_POINT_MODULO_ERROR_MESSAGE
|
472
624
|
return values
|
473
625
|
|
@@ -506,9 +658,9 @@ class RShift(EffectiveUnaryOpParams[pydantic.NonNegativeInt]):
|
|
506
658
|
arg = values.get("left_arg")
|
507
659
|
shift = values.get("right_arg")
|
508
660
|
if not isinstance(arg, RegisterArithmeticInfo):
|
509
|
-
raise
|
661
|
+
raise ClassiqValueError("left arg must be a RegisterArithmeticInfo")
|
510
662
|
if not isinstance(shift, int):
|
511
|
-
raise
|
663
|
+
raise ClassiqValueError("Shift must be an integer")
|
512
664
|
assert (
|
513
665
|
cls._shifted_fraction_places(arg=arg, shift=shift) == 0
|
514
666
|
), _FLOATING_POINT_MODULO_ERROR_MESSAGE
|
@@ -549,7 +701,7 @@ class CyclicShift(EffectiveUnaryOpParams[int]):
|
|
549
701
|
return values
|
550
702
|
arg = values.get("left_arg")
|
551
703
|
if not isinstance(arg, RegisterArithmeticInfo):
|
552
|
-
raise
|
704
|
+
raise ClassiqValueError("left arg must be a RegisterArithmeticInfo")
|
553
705
|
assert arg.fraction_places == 0, _FLOATING_POINT_MODULO_ERROR_MESSAGE
|
554
706
|
return values
|
555
707
|
|
@@ -590,11 +742,7 @@ class Modulo(EffectiveUnaryOpParams[int]):
|
|
590
742
|
values["output_size"] = None
|
591
743
|
return 2 ** (repr_qubits)
|
592
744
|
|
593
|
-
@property
|
594
|
-
def result_size(self) -> int:
|
595
|
-
return round(math.log2(self.right_arg))
|
596
|
-
|
597
745
|
def _get_result_register(self) -> RegisterArithmeticInfo:
|
598
746
|
return RegisterArithmeticInfo(
|
599
|
-
size=self.
|
747
|
+
size=round(math.log2(self.right_arg)), is_signed=False, fraction_places=0
|
600
748
|
)
|
@@ -15,6 +15,8 @@ from classiq.interface.generator.arith.binary_ops import (
|
|
15
15
|
from classiq.interface.generator.arith.register_user_input import RegisterArithmeticInfo
|
16
16
|
from classiq.interface.generator.function_params import get_zero_input_name
|
17
17
|
|
18
|
+
from classiq.exceptions import ClassiqValueError
|
19
|
+
|
18
20
|
Numeric = (float, int)
|
19
21
|
|
20
22
|
|
@@ -27,7 +29,7 @@ class Extremum(ArithmeticOperationParams):
|
|
27
29
|
left_arg = values.get("left_arg")
|
28
30
|
right_arg = values.get("right_arg")
|
29
31
|
if isinstance(left_arg, Numeric) and isinstance(right_arg, Numeric):
|
30
|
-
raise
|
32
|
+
raise ClassiqValueError("One argument must be a register")
|
31
33
|
if left_arg is right_arg and isinstance(left_arg, pydantic.BaseModel):
|
32
34
|
# In case both arguments refer to the same object, copy it.
|
33
35
|
# This prevents changes performed on one argument from affecting the other.
|
@@ -61,8 +63,8 @@ class Extremum(ArithmeticOperationParams):
|
|
61
63
|
argument_utils.integer_part_size(self.right_arg),
|
62
64
|
)
|
63
65
|
fraction_places = max(
|
64
|
-
|
65
|
-
|
66
|
+
argument_utils.fraction_places(self.left_arg),
|
67
|
+
argument_utils.fraction_places(self.right_arg),
|
66
68
|
)
|
67
69
|
required_size = integer_part_size + fraction_places
|
68
70
|
bounds = (
|
@@ -9,6 +9,8 @@ from classiq.interface.generator.arith.arithmetic_operations import (
|
|
9
9
|
from classiq.interface.generator.arith.register_user_input import RegisterArithmeticInfo
|
10
10
|
from classiq.interface.generator.function_params import get_zero_input_name
|
11
11
|
|
12
|
+
from classiq.exceptions import ClassiqValueError
|
13
|
+
|
12
14
|
|
13
15
|
def get_arg_name(idx: int) -> str:
|
14
16
|
return f"arg_{idx}"
|
@@ -25,7 +27,7 @@ class LogicalOps(ArithmeticOperationParams):
|
|
25
27
|
@pydantic.validator("output_size")
|
26
28
|
def _validate_output_size(cls, output_size: Optional[int]) -> int:
|
27
29
|
if output_size is not None and output_size != 1:
|
28
|
-
raise
|
30
|
+
raise ClassiqValueError("logical operation output size must be 1")
|
29
31
|
return 1
|
30
32
|
|
31
33
|
@pydantic.validator("args")
|
@@ -34,7 +36,7 @@ class LogicalOps(ArithmeticOperationParams):
|
|
34
36
|
) -> List[RegisterOrConst]:
|
35
37
|
for arg_idx, arg in enumerate(arguments):
|
36
38
|
if isinstance(arg, RegisterArithmeticInfo) and not arg.is_boolean_register:
|
37
|
-
raise
|
39
|
+
raise ClassiqValueError(
|
38
40
|
f"All inputs to logical and must be of size 1 (at argument #{arg_idx})"
|
39
41
|
)
|
40
42
|
return arguments
|