classiq 0.51.1__py3-none-any.whl → 0.52.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/_internals/api_wrapper.py +41 -15
- classiq/_internals/authentication/auth0.py +20 -4
- classiq/_internals/authentication/password_manager.py +16 -4
- classiq/_internals/client.py +2 -2
- classiq/_internals/host_checker.py +5 -3
- classiq/_internals/jobs.py +3 -3
- classiq/analyzer/analyzer_utilities.py +1 -1
- classiq/applications/chemistry/ground_state_problem.py +1 -1
- classiq/applications/combinatorial_helpers/pyomo_utils.py +3 -1
- classiq/applications/qnn/gradients/quantum_gradient.py +1 -1
- classiq/applications/qnn/qlayer.py +2 -2
- classiq/execution/__init__.py +3 -0
- classiq/execution/execution_session.py +2 -2
- classiq/execution/iqcc.py +63 -0
- classiq/execution/jobs.py +2 -2
- classiq/executor.py +2 -2
- classiq/interface/_version.py +1 -1
- classiq/interface/analyzer/analysis_params.py +19 -9
- classiq/interface/analyzer/cytoscape_graph.py +10 -3
- classiq/interface/analyzer/result.py +6 -5
- classiq/interface/applications/qsvm.py +13 -12
- classiq/interface/backend/backend_preferences.py +78 -105
- classiq/interface/backend/ionq/ionq_quantum_program.py +12 -19
- classiq/interface/backend/pydantic_backend.py +24 -12
- classiq/interface/backend/quantum_backend_providers.py +2 -0
- classiq/interface/chemistry/fermionic_operator.py +7 -7
- classiq/interface/chemistry/ground_state_problem.py +23 -18
- classiq/interface/chemistry/molecule.py +10 -5
- classiq/interface/chemistry/operator.py +71 -44
- classiq/interface/combinatorial_optimization/mht_qaoa_input.py +2 -1
- classiq/interface/debug_info/debug_info.py +3 -4
- classiq/interface/execution/iqcc.py +21 -0
- classiq/interface/execution/jobs.py +10 -10
- classiq/interface/executor/aws_execution_cost.py +37 -20
- classiq/interface/executor/execution_preferences.py +1 -2
- classiq/interface/executor/execution_request.py +2 -2
- classiq/interface/executor/execution_result.py +4 -2
- classiq/interface/executor/iqae_result.py +1 -1
- classiq/interface/executor/optimizer_preferences.py +14 -10
- classiq/interface/executor/quantum_code.py +21 -16
- classiq/interface/executor/register_initialization.py +10 -10
- classiq/interface/executor/result.py +19 -16
- classiq/interface/executor/vqe_result.py +1 -1
- classiq/interface/finance/function_input.py +27 -18
- classiq/interface/finance/log_normal_model_input.py +2 -2
- classiq/interface/finance/model_input.py +3 -2
- classiq/interface/generator/amplitude_loading.py +8 -6
- classiq/interface/generator/arith/argument_utils.py +24 -0
- classiq/interface/generator/arith/arithmetic.py +5 -3
- classiq/interface/generator/arith/arithmetic_expression_abc.py +36 -14
- classiq/interface/generator/arith/arithmetic_operations.py +6 -3
- classiq/interface/generator/arith/binary_ops.py +88 -63
- classiq/interface/generator/arith/extremum_operations.py +22 -13
- classiq/interface/generator/arith/logical_ops.py +6 -4
- classiq/interface/generator/arith/number_utils.py +3 -3
- classiq/interface/generator/arith/register_user_input.py +32 -17
- classiq/interface/generator/arith/unary_ops.py +5 -4
- classiq/interface/generator/chemistry_function_params.py +2 -1
- classiq/interface/generator/circuit_code/circuit_code.py +2 -1
- classiq/interface/generator/commuting_pauli_exponentiation.py +6 -5
- classiq/interface/generator/complex_type.py +14 -18
- classiq/interface/generator/control_state.py +32 -26
- classiq/interface/generator/expressions/expression.py +6 -5
- classiq/interface/generator/function_params.py +22 -39
- classiq/interface/generator/functions/classical_function_declaration.py +1 -1
- classiq/interface/generator/functions/classical_type.py +32 -23
- classiq/interface/generator/functions/concrete_types.py +8 -7
- classiq/interface/generator/functions/function_declaration.py +4 -5
- classiq/interface/generator/functions/type_name.py +5 -4
- classiq/interface/generator/generated_circuit_data.py +9 -6
- classiq/interface/generator/grover_diffuser.py +26 -18
- classiq/interface/generator/grover_operator.py +32 -22
- classiq/interface/generator/hamiltonian_evolution/exponentiation.py +3 -4
- classiq/interface/generator/hamiltonian_evolution/qdrift.py +4 -4
- classiq/interface/generator/hamiltonian_evolution/suzuki_trotter.py +8 -7
- classiq/interface/generator/hardware/hardware_data.py +27 -26
- classiq/interface/generator/hardware_efficient_ansatz.py +11 -6
- classiq/interface/generator/hartree_fock.py +2 -1
- classiq/interface/generator/identity.py +7 -2
- classiq/interface/generator/linear_pauli_rotations.py +27 -14
- classiq/interface/generator/mcu.py +15 -12
- classiq/interface/generator/mcx.py +18 -10
- classiq/interface/generator/model/constraints.py +4 -2
- classiq/interface/generator/model/model.py +2 -1
- classiq/interface/generator/model/preferences/preferences.py +30 -32
- classiq/interface/generator/oracles/custom_oracle.py +13 -10
- classiq/interface/generator/piecewise_linear_amplitude_loading.py +37 -21
- classiq/interface/generator/qpe.py +38 -26
- classiq/interface/generator/qsvm.py +4 -4
- classiq/interface/generator/quantum_function_call.py +57 -44
- classiq/interface/generator/quantum_program.py +8 -6
- classiq/interface/generator/range_types.py +10 -11
- classiq/interface/generator/standard_gates/controlled_standard_gates.py +9 -5
- classiq/interface/generator/standard_gates/standard_angle_metaclass.py +2 -6
- classiq/interface/generator/standard_gates/u_gate.py +7 -10
- classiq/interface/generator/state_preparation/computational_basis_state_preparation.py +2 -1
- classiq/interface/generator/state_preparation/distributions.py +12 -12
- classiq/interface/generator/state_preparation/state_preparation.py +22 -16
- classiq/interface/generator/types/enum_declaration.py +2 -1
- classiq/interface/generator/ucc.py +2 -1
- classiq/interface/generator/unitary_gate.py +2 -1
- classiq/interface/generator/user_defined_function_params.py +3 -0
- classiq/interface/generator/visitor.py +1 -1
- classiq/interface/hardware.py +18 -3
- classiq/interface/helpers/custom_pydantic_types.py +38 -47
- classiq/interface/helpers/pydantic_model_helpers.py +3 -2
- classiq/interface/helpers/versioned_model.py +1 -4
- classiq/interface/ide/ide_data.py +5 -5
- classiq/interface/ide/visual_model.py +5 -5
- classiq/interface/interface_version.py +1 -1
- classiq/interface/jobs.py +12 -22
- classiq/interface/model/bind_operation.py +2 -1
- classiq/interface/model/classical_parameter_declaration.py +10 -4
- classiq/interface/model/handle_binding.py +20 -24
- classiq/interface/model/inplace_binary_operation.py +16 -9
- classiq/interface/model/model.py +21 -11
- classiq/interface/model/port_declaration.py +10 -7
- classiq/interface/model/quantum_expressions/arithmetic_operation.py +6 -4
- classiq/interface/model/quantum_function_declaration.py +22 -11
- classiq/interface/model/quantum_statement.py +6 -7
- classiq/interface/model/quantum_type.py +22 -19
- classiq/interface/model/statement_block.py +9 -9
- classiq/interface/server/global_versions.py +4 -5
- classiq/interface/server/routes.py +8 -0
- classiq/model_expansions/evaluators/parameter_types.py +3 -3
- classiq/model_expansions/expression_renamer.py +1 -1
- classiq/model_expansions/quantum_operations/control.py +11 -12
- classiq/model_expansions/quantum_operations/emitter.py +22 -0
- classiq/model_expansions/quantum_operations/expression_operation.py +2 -20
- classiq/model_expansions/quantum_operations/inplace_binary_operation.py +38 -9
- classiq/model_expansions/quantum_operations/invert.py +1 -1
- classiq/model_expansions/quantum_operations/phase.py +4 -5
- classiq/model_expansions/quantum_operations/power.py +1 -1
- classiq/model_expansions/quantum_operations/quantum_assignment_operation.py +50 -9
- classiq/model_expansions/quantum_operations/variable_decleration.py +2 -2
- classiq/model_expansions/quantum_operations/within_apply.py +1 -1
- classiq/qmod/builtins/__init__.py +1 -3
- classiq/qmod/builtins/functions/__init__.py +4 -0
- classiq/qmod/builtins/functions/arithmetic.py +10 -0
- classiq/qmod/create_model_function.py +4 -4
- classiq/qmod/quantum_expandable.py +22 -9
- classiq/qmod/quantum_function.py +1 -1
- classiq/qmod/semantics/static_semantics_visitor.py +3 -1
- classiq/qmod/type_attribute_remover.py +1 -1
- classiq/qmod/write_qmod.py +2 -4
- classiq/synthesis.py +11 -13
- {classiq-0.51.1.dist-info → classiq-0.52.0.dist-info}/METADATA +3 -2
- {classiq-0.51.1.dist-info → classiq-0.52.0.dist-info}/RECORD +149 -147
- {classiq-0.51.1.dist-info → classiq-0.52.0.dist-info}/WHEEL +0 -0
@@ -1,6 +1,7 @@
|
|
1
|
-
from typing import
|
1
|
+
from typing import List
|
2
2
|
|
3
3
|
import pydantic
|
4
|
+
from typing_extensions import Self
|
4
5
|
|
5
6
|
from classiq.interface.exceptions import ClassiqStateInitializationError
|
6
7
|
from classiq.interface.generator.arith import number_utils
|
@@ -15,19 +16,18 @@ class RegisterInitialization(pydantic.BaseModel):
|
|
15
16
|
qubits: List[int]
|
16
17
|
initial_condition: pydantic.NonNegativeInt
|
17
18
|
|
18
|
-
@pydantic.
|
19
|
+
@pydantic.field_validator("initial_condition", mode="before")
|
20
|
+
@classmethod
|
19
21
|
def _validate_initial_condition(cls, value: int) -> int:
|
20
22
|
if not isinstance(value, int) or value < 0:
|
21
23
|
raise ClassiqStateInitializationError(_NON_INTEGER_INITIALIZATION_ERROR_MSG)
|
22
24
|
return value
|
23
25
|
|
24
|
-
@pydantic.
|
25
|
-
def _validate_register_initialization(
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
initial_condition: int = values.get("initial_condition", 0)
|
30
|
-
name: str = values.get("name", "")
|
26
|
+
@pydantic.model_validator(mode="after")
|
27
|
+
def _validate_register_initialization(self) -> Self:
|
28
|
+
qubits: List[int] = self.qubits or []
|
29
|
+
initial_condition: int = self.initial_condition or 0
|
30
|
+
name: str = self.name or ""
|
31
31
|
|
32
32
|
initial_condition_length = number_utils.size(initial_condition)
|
33
33
|
register_length = len(qubits)
|
@@ -35,4 +35,4 @@ class RegisterInitialization(pydantic.BaseModel):
|
|
35
35
|
raise ClassiqStateInitializationError(
|
36
36
|
f"Register {name} has {register_length} qubits, which is not enough to represent the number {initial_condition}."
|
37
37
|
)
|
38
|
-
return
|
38
|
+
return self
|
@@ -16,7 +16,7 @@ from typing import (
|
|
16
16
|
|
17
17
|
import pydantic
|
18
18
|
from pydantic import BaseModel
|
19
|
-
from typing_extensions import TypeAlias
|
19
|
+
from typing_extensions import Self, TypeAlias
|
20
20
|
|
21
21
|
from classiq.interface.exceptions import ClassiqError
|
22
22
|
from classiq.interface.executor.quantum_code import OutputQubitsMap, Qubits
|
@@ -37,7 +37,7 @@ MeasuredShots: TypeAlias = pydantic.NonNegativeInt
|
|
37
37
|
ParsedState: TypeAlias = Mapping[Name, RegisterValue]
|
38
38
|
ParsedStates: TypeAlias = Mapping[State, ParsedState]
|
39
39
|
Counts: TypeAlias = Dict[State, MeasuredShots]
|
40
|
-
StateVector: TypeAlias = Optional[Dict[str,
|
40
|
+
StateVector: TypeAlias = Optional[Dict[str, Complex]]
|
41
41
|
|
42
42
|
|
43
43
|
class SampledState(BaseModel):
|
@@ -60,7 +60,7 @@ class SimulatedState(BaseModel):
|
|
60
60
|
return self.state[item]
|
61
61
|
|
62
62
|
|
63
|
-
SimulatedState.
|
63
|
+
SimulatedState.model_rebuild()
|
64
64
|
ParsedStateVector: TypeAlias = List[SimulatedState]
|
65
65
|
|
66
66
|
|
@@ -170,7 +170,7 @@ class ExecutionDetails(BaseModel, QmodPyObject):
|
|
170
170
|
default=None,
|
171
171
|
description="The state vector when executed on a simulator, with LSB right qubit order",
|
172
172
|
)
|
173
|
-
parsed_state_vector_states: ParsedStates = pydantic.Field(
|
173
|
+
parsed_state_vector_states: Optional[ParsedStates] = pydantic.Field(
|
174
174
|
default=None,
|
175
175
|
description="A mapping between the raw states of the state vector (bitstrings) to their parsed states (registers' values)",
|
176
176
|
)
|
@@ -182,22 +182,23 @@ class ExecutionDetails(BaseModel, QmodPyObject):
|
|
182
182
|
default=None, description="The total number of shots the circuit was executed"
|
183
183
|
)
|
184
184
|
|
185
|
-
@pydantic.
|
185
|
+
@pydantic.field_validator("counts", mode="after")
|
186
|
+
@classmethod
|
186
187
|
def _clean_spaces_from_counts_keys(cls, v: Counts) -> Counts:
|
187
188
|
if not v or " " not in list(v.keys())[0]:
|
188
189
|
return v
|
189
190
|
return {state.replace(" ", ""): v[state] for state in v}
|
190
191
|
|
191
|
-
@pydantic.
|
192
|
-
def _validate_num_shots(
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
return
|
192
|
+
@pydantic.model_validator(mode="after")
|
193
|
+
def _validate_num_shots(self) -> Self:
|
194
|
+
if isinstance(self, ExecutionDetails):
|
195
|
+
if self.num_shots is not None:
|
196
|
+
return self
|
197
|
+
counts = self.counts
|
198
|
+
if not counts:
|
199
|
+
return self
|
200
|
+
self.num_shots = sum(shots for _, shots in counts.items())
|
201
|
+
return self
|
201
202
|
|
202
203
|
@property
|
203
204
|
def parsed_counts(self) -> ParsedCounts:
|
@@ -205,6 +206,8 @@ class ExecutionDetails(BaseModel, QmodPyObject):
|
|
205
206
|
|
206
207
|
@property
|
207
208
|
def parsed_state_vector(self) -> Optional[ParsedStateVector]:
|
209
|
+
if TYPE_CHECKING:
|
210
|
+
assert self.parsed_state_vector_states is not None
|
208
211
|
return prepare_parsed_state_vector(
|
209
212
|
self.state_vector, self.parsed_state_vector_states
|
210
213
|
)
|
@@ -297,7 +300,7 @@ class MultipleExecutionDetails(VersionedModel):
|
|
297
300
|
return self.details[index]
|
298
301
|
|
299
302
|
|
300
|
-
class EstimationMetadata(BaseModel, extra=
|
303
|
+
class EstimationMetadata(BaseModel, extra="allow"):
|
301
304
|
shots: Optional[pydantic.NonNegativeInt] = None
|
302
305
|
remapped_qubits: bool = False
|
303
306
|
input_qubit_map: Optional[List[PydanticNonNegIntTuple]] = None
|
@@ -1,6 +1,8 @@
|
|
1
1
|
from typing import Any, Dict, Optional, Union
|
2
2
|
|
3
3
|
import pydantic
|
4
|
+
from pydantic import ConfigDict
|
5
|
+
from pydantic_core.core_schema import ValidationInfo
|
4
6
|
|
5
7
|
from classiq.interface.exceptions import ClassiqValueError
|
6
8
|
from classiq.interface.generator.expressions.enums.finance_functions import (
|
@@ -20,9 +22,7 @@ class FunctionCondition(pydantic.BaseModel):
|
|
20
22
|
default=False,
|
21
23
|
description="When true, function is set when input is larger to threshold and otherwise 0. Default is False.",
|
22
24
|
)
|
23
|
-
|
24
|
-
class Config:
|
25
|
-
frozen = True
|
25
|
+
model_config = ConfigDict(frozen=True)
|
26
26
|
|
27
27
|
|
28
28
|
class FinanceFunctionInput(pydantic.BaseModel):
|
@@ -50,24 +50,33 @@ class FinanceFunctionInput(pydantic.BaseModel):
|
|
50
50
|
description="The required probability on the tail of the distribution (1 - percentile)",
|
51
51
|
)
|
52
52
|
|
53
|
-
@pydantic.
|
54
|
-
|
55
|
-
|
56
|
-
if
|
57
|
-
|
58
|
-
|
53
|
+
@pydantic.model_validator(mode="before")
|
54
|
+
@classmethod
|
55
|
+
def _convert_f_if_str(cls, values: Any, info: ValidationInfo) -> Dict[str, Any]:
|
56
|
+
if isinstance(values, dict):
|
57
|
+
f = values.get("f")
|
58
|
+
elif isinstance(values, FinanceFunctionInput):
|
59
|
+
f = values.f
|
60
|
+
values = values.model_dump()
|
61
|
+
else:
|
62
|
+
f = info.data.get("f")
|
63
|
+
if isinstance(f, str) and f in get_finance_function_dict():
|
64
|
+
values["f"] = get_finance_function_dict()[f]
|
65
|
+
return values
|
59
66
|
|
60
|
-
@pydantic.
|
67
|
+
@pydantic.field_validator("use_chebyshev_polynomial_approximation", mode="before")
|
68
|
+
@classmethod
|
61
69
|
def _validate_polynomial_flag(
|
62
|
-
cls, use_chebyshev_flag: bool,
|
70
|
+
cls, use_chebyshev_flag: bool, info: ValidationInfo
|
63
71
|
) -> bool:
|
64
|
-
if use_chebyshev_flag ^ (
|
72
|
+
if use_chebyshev_flag ^ (info.data.get("polynomial_degree") is None):
|
65
73
|
return use_chebyshev_flag
|
66
74
|
raise ClassiqValueError(
|
67
75
|
"Degree must be positive and use_chebyshev_polynomial_approximation set to True"
|
68
76
|
)
|
69
77
|
|
70
|
-
@pydantic.
|
78
|
+
@pydantic.field_validator("f", mode="before")
|
79
|
+
@classmethod
|
71
80
|
def _validate_finance_function(
|
72
81
|
cls, f: Union[int, str, "FinanceFunctionType"]
|
73
82
|
) -> FinanceFunctionType:
|
@@ -77,17 +86,17 @@ class FinanceFunctionInput(pydantic.BaseModel):
|
|
77
86
|
return FinanceFunctionType(f)
|
78
87
|
return get_finance_function_dict()[f]
|
79
88
|
|
80
|
-
@pydantic.
|
89
|
+
@pydantic.field_validator("tail_probability", mode="before")
|
90
|
+
@classmethod
|
81
91
|
def _validate_tail_probability_assignment_for_shortfall(
|
82
92
|
cls,
|
83
93
|
tail_probability: Optional[PydanticNonZeroProbabilityFloat],
|
84
|
-
|
94
|
+
info: ValidationInfo,
|
85
95
|
) -> Optional[PydanticNonZeroProbabilityFloat]:
|
86
|
-
if
|
96
|
+
if info.data.get("f") == FinanceFunctionType.SHORTFALL and not tail_probability:
|
87
97
|
raise ClassiqValueError(
|
88
98
|
"Tail probability must be set for expected shortfall"
|
89
99
|
)
|
90
100
|
return tail_probability
|
91
101
|
|
92
|
-
|
93
|
-
frozen = True
|
102
|
+
model_config = ConfigDict(frozen=True)
|
@@ -2,6 +2,7 @@ from typing import Literal, Tuple
|
|
2
2
|
|
3
3
|
import numpy as np
|
4
4
|
import pydantic
|
5
|
+
from pydantic import ConfigDict
|
5
6
|
|
6
7
|
from classiq.interface.finance.model_input import FinanceModelInput
|
7
8
|
|
@@ -36,5 +37,4 @@ class LogNormalModelInput(FinanceModelInput):
|
|
36
37
|
def num_output_qubits(self) -> int:
|
37
38
|
return self.num_qubits
|
38
39
|
|
39
|
-
|
40
|
-
frozen = True
|
40
|
+
model_config = ConfigDict(frozen=True)
|
@@ -1,6 +1,8 @@
|
|
1
1
|
import abc
|
2
2
|
from typing import Tuple
|
3
3
|
|
4
|
+
from pydantic import ConfigDict
|
5
|
+
|
4
6
|
from classiq.interface.helpers.hashable_pydantic_base_model import (
|
5
7
|
HashablePydanticBaseModel,
|
6
8
|
)
|
@@ -13,8 +15,7 @@ class FinanceModelInput(HashablePydanticBaseModel):
|
|
13
15
|
def num_output_qubits(self) -> int:
|
14
16
|
return 0
|
15
17
|
|
16
|
-
|
17
|
-
frozen = True
|
18
|
+
model_config = ConfigDict(frozen=True)
|
18
19
|
|
19
20
|
@property
|
20
21
|
@abc.abstractmethod
|
@@ -1,8 +1,9 @@
|
|
1
1
|
import re
|
2
|
-
from typing import
|
2
|
+
from typing import get_args
|
3
3
|
|
4
4
|
import pydantic
|
5
5
|
import sympy
|
6
|
+
from typing_extensions import Self
|
6
7
|
|
7
8
|
from classiq.interface.enum_utils import StrEnum
|
8
9
|
from classiq.interface.exceptions import ClassiqValueError
|
@@ -50,7 +51,8 @@ class AmplitudeLoading(FunctionParams):
|
|
50
51
|
description="Implementation options.",
|
51
52
|
)
|
52
53
|
|
53
|
-
@pydantic.
|
54
|
+
@pydantic.field_validator("expression", mode="before")
|
55
|
+
@classmethod
|
54
56
|
def validate_coefficient(cls, expression: str) -> str:
|
55
57
|
if isinstance(expression, str):
|
56
58
|
# We validate the given value is legal and does not contain code that will be executed in our BE.
|
@@ -65,9 +67,9 @@ class AmplitudeLoading(FunctionParams):
|
|
65
67
|
return str(expression)
|
66
68
|
return expression
|
67
69
|
|
68
|
-
@pydantic.
|
69
|
-
def check_all_variable_are_defined(
|
70
|
-
expression =
|
70
|
+
@pydantic.model_validator(mode="after")
|
71
|
+
def check_all_variable_are_defined(self) -> Self:
|
72
|
+
expression = self.expression or ""
|
71
73
|
literals = set(re.findall(SUPPORTED_VAR_NAMES_REG, expression))
|
72
74
|
|
73
75
|
not_allowed = literals.intersection(FORBIDDEN_LITERALS) - BOOLEAN_LITERALS
|
@@ -79,7 +81,7 @@ class AmplitudeLoading(FunctionParams):
|
|
79
81
|
|
80
82
|
if len(variables) != 1:
|
81
83
|
raise ClassiqValueError(f"{variables} must contain exactly single variable")
|
82
|
-
return
|
84
|
+
return self
|
83
85
|
|
84
86
|
def _create_ios(self) -> None:
|
85
87
|
self._inputs = {
|
@@ -1,6 +1,9 @@
|
|
1
1
|
from typing import Tuple, Union
|
2
2
|
|
3
3
|
from classiq.interface.generator.arith import number_utils
|
4
|
+
from classiq.interface.generator.arith.number_utils import (
|
5
|
+
get_int_representation_and_fraction_places,
|
6
|
+
)
|
4
7
|
from classiq.interface.generator.arith.register_user_input import RegisterArithmeticInfo
|
5
8
|
|
6
9
|
RegisterOrConst = Union[RegisterArithmeticInfo, float]
|
@@ -73,3 +76,24 @@ def as_arithmetic_info(
|
|
73
76
|
fraction_places=number_utils.fraction_places(arg),
|
74
77
|
bounds=(arg, arg) if with_bounds else None,
|
75
78
|
)
|
79
|
+
|
80
|
+
|
81
|
+
def unsigned_integer_interpretation(
|
82
|
+
value: float, register: RegisterArithmeticInfo
|
83
|
+
) -> int:
|
84
|
+
int_val, fraction_digits = get_int_representation_and_fraction_places(value)
|
85
|
+
|
86
|
+
# align fraction digits
|
87
|
+
fraction_digits_diff = register.fraction_places - fraction_digits
|
88
|
+
if fraction_digits_diff < 0:
|
89
|
+
int_val >>= -fraction_digits_diff
|
90
|
+
else:
|
91
|
+
int_val <<= fraction_digits_diff
|
92
|
+
|
93
|
+
# extend sign bit
|
94
|
+
if int(value) < 0:
|
95
|
+
bin_val = number_utils.binary_string(int_val)
|
96
|
+
bin_val += "1" * (register.size - len(bin_val))
|
97
|
+
int_val = number_utils.binary_to_int(bin_val[::-1])
|
98
|
+
|
99
|
+
return int_val
|
@@ -2,6 +2,7 @@ from typing import Any, Dict, Final, Optional, Set
|
|
2
2
|
|
3
3
|
import networkx as nx
|
4
4
|
import pydantic
|
5
|
+
from pydantic_core.core_schema import ValidationInfo
|
5
6
|
|
6
7
|
from classiq.interface.exceptions import ClassiqValueError
|
7
8
|
from classiq.interface.generator.arith import arithmetic_expression_parser
|
@@ -31,11 +32,12 @@ class Arithmetic(ArithmeticExpressionABC):
|
|
31
32
|
target: Optional[RegisterArithmeticInfo] = None
|
32
33
|
inputs_to_save: Set[str] = pydantic.Field(default_factory=set)
|
33
34
|
|
34
|
-
@pydantic.
|
35
|
+
@pydantic.field_validator("inputs_to_save")
|
36
|
+
@classmethod
|
35
37
|
def _validate_inputs_to_save(
|
36
|
-
cls, inputs_to_save: Set[str],
|
38
|
+
cls, inputs_to_save: Set[str], info: ValidationInfo
|
37
39
|
) -> Set[str]:
|
38
|
-
assert all(reg in
|
40
|
+
assert all(reg in info.data.get("definitions", {}) for reg in inputs_to_save)
|
39
41
|
return inputs_to_save
|
40
42
|
|
41
43
|
@staticmethod
|
@@ -5,6 +5,7 @@ from typing import Any, Dict, Optional, Set, Tuple, Union
|
|
5
5
|
|
6
6
|
import networkx as nx
|
7
7
|
import pydantic
|
8
|
+
from pydantic import TypeAdapter
|
8
9
|
from typing_extensions import TypeAlias
|
9
10
|
|
10
11
|
from classiq.interface.exceptions import ClassiqValueError
|
@@ -46,14 +47,10 @@ class ArithmeticExpressionABC(abc.ABC, FunctionParams):
|
|
46
47
|
def _get_literal_set(self) -> Set[str]:
|
47
48
|
return _extract_literals(self.expression)
|
48
49
|
|
49
|
-
@
|
50
|
+
@classmethod
|
50
51
|
def _validate_expression_literals_and_definitions(
|
51
|
-
cls, definitions: Dict[str, ValidDefinitions],
|
52
|
+
cls, definitions: Dict[str, ValidDefinitions], expression: PydanticExpressionStr
|
52
53
|
) -> Dict[str, ValidDefinitions]:
|
53
|
-
expression = values.get("expression")
|
54
|
-
if expression is None:
|
55
|
-
return definitions
|
56
|
-
|
57
54
|
literals = _extract_literals(expression)
|
58
55
|
|
59
56
|
forbidden = literals.intersection(FORBIDDEN_LITERALS)
|
@@ -70,12 +67,33 @@ class ArithmeticExpressionABC(abc.ABC, FunctionParams):
|
|
70
67
|
raise ClassiqValueError(f"The following names are undefined: {undefined}")
|
71
68
|
return definitions
|
72
69
|
|
73
|
-
@pydantic.
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
70
|
+
@pydantic.model_validator(mode="before")
|
71
|
+
@classmethod
|
72
|
+
def _validate_expression(cls, values: Any) -> Dict[str, Any]:
|
73
|
+
if not isinstance(values, dict):
|
74
|
+
return values
|
75
|
+
expression_adapter: TypeAdapter = TypeAdapter(Optional[PydanticExpressionStr])
|
76
|
+
expression = expression_adapter.validate_python(values.get("expression"))
|
77
|
+
definitions_adapter: TypeAdapter = TypeAdapter(
|
78
|
+
Optional[Dict[str, ValidDefinitions]]
|
79
|
+
)
|
80
|
+
definition_dict = values.get("definitions")
|
81
|
+
if (
|
82
|
+
isinstance(definition_dict, list)
|
83
|
+
and len(definition_dict) > 0
|
84
|
+
and isinstance(definition_dict[0], tuple)
|
85
|
+
):
|
86
|
+
definition_dict = dict(definition_dict)
|
87
|
+
definitions = definitions_adapter.validate_python(definition_dict)
|
88
|
+
machine_precision: Optional[int] = values.get(
|
89
|
+
"machine_precision", DEFAULT_MACHINE_PRECISION
|
90
|
+
)
|
91
|
+
if (
|
92
|
+
expression is None
|
93
|
+
or expression == ""
|
94
|
+
or definitions is None
|
95
|
+
or machine_precision is None
|
96
|
+
):
|
79
97
|
return values
|
80
98
|
|
81
99
|
try:
|
@@ -108,14 +126,18 @@ class ArithmeticExpressionABC(abc.ABC, FunctionParams):
|
|
108
126
|
graph = parse_expression(expression)
|
109
127
|
cls._validate_expression_graph(graph, values)
|
110
128
|
|
129
|
+
validated_defs = cls._validate_expression_literals_and_definitions(
|
130
|
+
definitions, expression
|
131
|
+
)
|
132
|
+
|
111
133
|
validate_arithmetic_result_type(
|
112
134
|
graph=graph,
|
113
|
-
definitions=
|
135
|
+
definitions=validated_defs,
|
114
136
|
machine_precision=machine_precision,
|
115
137
|
)
|
116
138
|
|
117
139
|
new_expr, new_defs = cls._replace_const_definitions_in_expression(
|
118
|
-
expression,
|
140
|
+
expression, validated_defs, machine_precision
|
119
141
|
)
|
120
142
|
values["expression"] = new_expr
|
121
143
|
values["definitions"] = new_defs
|
@@ -16,7 +16,7 @@ MODULO_WITH_FRACTION_PLACES_ERROR_MSG: Final[str] = (
|
|
16
16
|
|
17
17
|
|
18
18
|
class ArithmeticOperationParams(FunctionParams):
|
19
|
-
output_size: Optional[pydantic.PositiveInt]
|
19
|
+
output_size: Optional[pydantic.PositiveInt] = pydantic.Field(default=None)
|
20
20
|
machine_precision: pydantic.PositiveInt = DEFAULT_MACHINE_PRECISION
|
21
21
|
output_name: ClassVar[str]
|
22
22
|
garbage_output_name: ClassVar[str] = DEFAULT_GARBAGE_OUT_NAME
|
@@ -43,9 +43,12 @@ class ArithmeticOperationParams(FunctionParams):
|
|
43
43
|
return self.output_size is None
|
44
44
|
|
45
45
|
def _legal_bounds(
|
46
|
-
self, suggested_bounds: Tuple[float, float]
|
46
|
+
self, suggested_bounds: Tuple[float, float], max_bounds: Tuple[float, float]
|
47
47
|
) -> Optional[Tuple[float, float]]:
|
48
|
-
if self.
|
48
|
+
if self.output_size is None or (
|
49
|
+
suggested_bounds[0] >= max_bounds[0]
|
50
|
+
and suggested_bounds[1] <= max_bounds[1]
|
51
|
+
):
|
49
52
|
return suggested_bounds
|
50
53
|
return None
|
51
54
|
|