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.
Files changed (149) hide show
  1. classiq/_internals/api_wrapper.py +41 -15
  2. classiq/_internals/authentication/auth0.py +20 -4
  3. classiq/_internals/authentication/password_manager.py +16 -4
  4. classiq/_internals/client.py +2 -2
  5. classiq/_internals/host_checker.py +5 -3
  6. classiq/_internals/jobs.py +3 -3
  7. classiq/analyzer/analyzer_utilities.py +1 -1
  8. classiq/applications/chemistry/ground_state_problem.py +1 -1
  9. classiq/applications/combinatorial_helpers/pyomo_utils.py +3 -1
  10. classiq/applications/qnn/gradients/quantum_gradient.py +1 -1
  11. classiq/applications/qnn/qlayer.py +2 -2
  12. classiq/execution/__init__.py +3 -0
  13. classiq/execution/execution_session.py +2 -2
  14. classiq/execution/iqcc.py +63 -0
  15. classiq/execution/jobs.py +2 -2
  16. classiq/executor.py +2 -2
  17. classiq/interface/_version.py +1 -1
  18. classiq/interface/analyzer/analysis_params.py +19 -9
  19. classiq/interface/analyzer/cytoscape_graph.py +10 -3
  20. classiq/interface/analyzer/result.py +6 -5
  21. classiq/interface/applications/qsvm.py +13 -12
  22. classiq/interface/backend/backend_preferences.py +78 -105
  23. classiq/interface/backend/ionq/ionq_quantum_program.py +12 -19
  24. classiq/interface/backend/pydantic_backend.py +24 -12
  25. classiq/interface/backend/quantum_backend_providers.py +2 -0
  26. classiq/interface/chemistry/fermionic_operator.py +7 -7
  27. classiq/interface/chemistry/ground_state_problem.py +23 -18
  28. classiq/interface/chemistry/molecule.py +10 -5
  29. classiq/interface/chemistry/operator.py +71 -44
  30. classiq/interface/combinatorial_optimization/mht_qaoa_input.py +2 -1
  31. classiq/interface/debug_info/debug_info.py +3 -4
  32. classiq/interface/execution/iqcc.py +21 -0
  33. classiq/interface/execution/jobs.py +10 -10
  34. classiq/interface/executor/aws_execution_cost.py +37 -20
  35. classiq/interface/executor/execution_preferences.py +1 -2
  36. classiq/interface/executor/execution_request.py +2 -2
  37. classiq/interface/executor/execution_result.py +4 -2
  38. classiq/interface/executor/iqae_result.py +1 -1
  39. classiq/interface/executor/optimizer_preferences.py +14 -10
  40. classiq/interface/executor/quantum_code.py +21 -16
  41. classiq/interface/executor/register_initialization.py +10 -10
  42. classiq/interface/executor/result.py +19 -16
  43. classiq/interface/executor/vqe_result.py +1 -1
  44. classiq/interface/finance/function_input.py +27 -18
  45. classiq/interface/finance/log_normal_model_input.py +2 -2
  46. classiq/interface/finance/model_input.py +3 -2
  47. classiq/interface/generator/amplitude_loading.py +8 -6
  48. classiq/interface/generator/arith/argument_utils.py +24 -0
  49. classiq/interface/generator/arith/arithmetic.py +5 -3
  50. classiq/interface/generator/arith/arithmetic_expression_abc.py +36 -14
  51. classiq/interface/generator/arith/arithmetic_operations.py +6 -3
  52. classiq/interface/generator/arith/binary_ops.py +88 -63
  53. classiq/interface/generator/arith/extremum_operations.py +22 -13
  54. classiq/interface/generator/arith/logical_ops.py +6 -4
  55. classiq/interface/generator/arith/number_utils.py +3 -3
  56. classiq/interface/generator/arith/register_user_input.py +32 -17
  57. classiq/interface/generator/arith/unary_ops.py +5 -4
  58. classiq/interface/generator/chemistry_function_params.py +2 -1
  59. classiq/interface/generator/circuit_code/circuit_code.py +2 -1
  60. classiq/interface/generator/commuting_pauli_exponentiation.py +6 -5
  61. classiq/interface/generator/complex_type.py +14 -18
  62. classiq/interface/generator/control_state.py +32 -26
  63. classiq/interface/generator/expressions/expression.py +6 -5
  64. classiq/interface/generator/function_params.py +22 -39
  65. classiq/interface/generator/functions/classical_function_declaration.py +1 -1
  66. classiq/interface/generator/functions/classical_type.py +32 -23
  67. classiq/interface/generator/functions/concrete_types.py +8 -7
  68. classiq/interface/generator/functions/function_declaration.py +4 -5
  69. classiq/interface/generator/functions/type_name.py +5 -4
  70. classiq/interface/generator/generated_circuit_data.py +9 -6
  71. classiq/interface/generator/grover_diffuser.py +26 -18
  72. classiq/interface/generator/grover_operator.py +32 -22
  73. classiq/interface/generator/hamiltonian_evolution/exponentiation.py +3 -4
  74. classiq/interface/generator/hamiltonian_evolution/qdrift.py +4 -4
  75. classiq/interface/generator/hamiltonian_evolution/suzuki_trotter.py +8 -7
  76. classiq/interface/generator/hardware/hardware_data.py +27 -26
  77. classiq/interface/generator/hardware_efficient_ansatz.py +11 -6
  78. classiq/interface/generator/hartree_fock.py +2 -1
  79. classiq/interface/generator/identity.py +7 -2
  80. classiq/interface/generator/linear_pauli_rotations.py +27 -14
  81. classiq/interface/generator/mcu.py +15 -12
  82. classiq/interface/generator/mcx.py +18 -10
  83. classiq/interface/generator/model/constraints.py +4 -2
  84. classiq/interface/generator/model/model.py +2 -1
  85. classiq/interface/generator/model/preferences/preferences.py +30 -32
  86. classiq/interface/generator/oracles/custom_oracle.py +13 -10
  87. classiq/interface/generator/piecewise_linear_amplitude_loading.py +37 -21
  88. classiq/interface/generator/qpe.py +38 -26
  89. classiq/interface/generator/qsvm.py +4 -4
  90. classiq/interface/generator/quantum_function_call.py +57 -44
  91. classiq/interface/generator/quantum_program.py +8 -6
  92. classiq/interface/generator/range_types.py +10 -11
  93. classiq/interface/generator/standard_gates/controlled_standard_gates.py +9 -5
  94. classiq/interface/generator/standard_gates/standard_angle_metaclass.py +2 -6
  95. classiq/interface/generator/standard_gates/u_gate.py +7 -10
  96. classiq/interface/generator/state_preparation/computational_basis_state_preparation.py +2 -1
  97. classiq/interface/generator/state_preparation/distributions.py +12 -12
  98. classiq/interface/generator/state_preparation/state_preparation.py +22 -16
  99. classiq/interface/generator/types/enum_declaration.py +2 -1
  100. classiq/interface/generator/ucc.py +2 -1
  101. classiq/interface/generator/unitary_gate.py +2 -1
  102. classiq/interface/generator/user_defined_function_params.py +3 -0
  103. classiq/interface/generator/visitor.py +1 -1
  104. classiq/interface/hardware.py +18 -3
  105. classiq/interface/helpers/custom_pydantic_types.py +38 -47
  106. classiq/interface/helpers/pydantic_model_helpers.py +3 -2
  107. classiq/interface/helpers/versioned_model.py +1 -4
  108. classiq/interface/ide/ide_data.py +5 -5
  109. classiq/interface/ide/visual_model.py +5 -5
  110. classiq/interface/interface_version.py +1 -1
  111. classiq/interface/jobs.py +12 -22
  112. classiq/interface/model/bind_operation.py +2 -1
  113. classiq/interface/model/classical_parameter_declaration.py +10 -4
  114. classiq/interface/model/handle_binding.py +20 -24
  115. classiq/interface/model/inplace_binary_operation.py +16 -9
  116. classiq/interface/model/model.py +21 -11
  117. classiq/interface/model/port_declaration.py +10 -7
  118. classiq/interface/model/quantum_expressions/arithmetic_operation.py +6 -4
  119. classiq/interface/model/quantum_function_declaration.py +22 -11
  120. classiq/interface/model/quantum_statement.py +6 -7
  121. classiq/interface/model/quantum_type.py +22 -19
  122. classiq/interface/model/statement_block.py +9 -9
  123. classiq/interface/server/global_versions.py +4 -5
  124. classiq/interface/server/routes.py +8 -0
  125. classiq/model_expansions/evaluators/parameter_types.py +3 -3
  126. classiq/model_expansions/expression_renamer.py +1 -1
  127. classiq/model_expansions/quantum_operations/control.py +11 -12
  128. classiq/model_expansions/quantum_operations/emitter.py +22 -0
  129. classiq/model_expansions/quantum_operations/expression_operation.py +2 -20
  130. classiq/model_expansions/quantum_operations/inplace_binary_operation.py +38 -9
  131. classiq/model_expansions/quantum_operations/invert.py +1 -1
  132. classiq/model_expansions/quantum_operations/phase.py +4 -5
  133. classiq/model_expansions/quantum_operations/power.py +1 -1
  134. classiq/model_expansions/quantum_operations/quantum_assignment_operation.py +50 -9
  135. classiq/model_expansions/quantum_operations/variable_decleration.py +2 -2
  136. classiq/model_expansions/quantum_operations/within_apply.py +1 -1
  137. classiq/qmod/builtins/__init__.py +1 -3
  138. classiq/qmod/builtins/functions/__init__.py +4 -0
  139. classiq/qmod/builtins/functions/arithmetic.py +10 -0
  140. classiq/qmod/create_model_function.py +4 -4
  141. classiq/qmod/quantum_expandable.py +22 -9
  142. classiq/qmod/quantum_function.py +1 -1
  143. classiq/qmod/semantics/static_semantics_visitor.py +3 -1
  144. classiq/qmod/type_attribute_remover.py +1 -1
  145. classiq/qmod/write_qmod.py +2 -4
  146. classiq/synthesis.py +11 -13
  147. {classiq-0.51.1.dist-info → classiq-0.52.0.dist-info}/METADATA +3 -2
  148. {classiq-0.51.1.dist-info → classiq-0.52.0.dist-info}/RECORD +149 -147
  149. {classiq-0.51.1.dist-info → classiq-0.52.0.dist-info}/WHEEL +0 -0
@@ -1,6 +1,7 @@
1
- from typing import Any, Dict, List
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.validator("initial_condition", pre=True)
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.root_validator()
25
- def _validate_register_initialization(
26
- cls, values: Dict[str, Any]
27
- ) -> Dict[str, Any]:
28
- qubits: List[int] = values.get("qubits", list())
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 values
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, Any]]
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.update_forward_refs(RegisterValue=RegisterValue)
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.validator("counts", pre=True)
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.validator("num_shots", always=True)
192
- def _validate_num_shots(
193
- cls, num_shots: Optional[int], values: Dict[str, Any]
194
- ) -> Optional[int]:
195
- if num_shots is not None:
196
- return num_shots
197
- counts = values.get("counts")
198
- if not counts:
199
- return None
200
- return sum(shots for _, shots in counts.items())
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=pydantic.Extra.allow):
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
@@ -19,7 +19,7 @@ class SolverResult(BaseModel):
19
19
  energy: float
20
20
  # TODO: add time units (like seconds)
21
21
  time: Optional[float] = None
22
- solution: Optional[Solution]
22
+ solution: Optional[Solution] = None
23
23
 
24
24
 
25
25
  class SolutionData(BaseModel):
@@ -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.validator("f", pre=True)
54
- def _convert_f_if_str(cls, f: Any) -> "FinanceFunctionType":
55
- # Keep this for backwards-compatible string support
56
- if f in get_finance_function_dict():
57
- return get_finance_function_dict()[f]
58
- return f
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.validator("use_chebyshev_polynomial_approximation")
67
+ @pydantic.field_validator("use_chebyshev_polynomial_approximation", mode="before")
68
+ @classmethod
61
69
  def _validate_polynomial_flag(
62
- cls, use_chebyshev_flag: bool, values: Dict[str, Any]
70
+ cls, use_chebyshev_flag: bool, info: ValidationInfo
63
71
  ) -> bool:
64
- if use_chebyshev_flag ^ (values.get("polynomial_degree") is None):
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.validator("f")
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.validator("tail_probability", always=True)
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
- values: Dict[str, Any],
94
+ info: ValidationInfo,
85
95
  ) -> Optional[PydanticNonZeroProbabilityFloat]:
86
- if values.get("f") == FinanceFunctionType.SHORTFALL and not tail_probability:
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
- class Config:
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
- class Config:
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
- class Config:
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 Any, Dict, get_args
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.validator("expression", pre=True)
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.root_validator()
69
- def check_all_variable_are_defined(cls, values: Dict[str, Any]) -> Dict[str, Any]:
70
- expression = values.get("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 values
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.validator("inputs_to_save", always=True)
35
+ @pydantic.field_validator("inputs_to_save")
36
+ @classmethod
35
37
  def _validate_inputs_to_save(
36
- cls, inputs_to_save: Set[str], values: Dict[str, Any]
38
+ cls, inputs_to_save: Set[str], info: ValidationInfo
37
39
  ) -> Set[str]:
38
- assert all(reg in values.get("definitions", {}) for reg in inputs_to_save)
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
- @pydantic.validator("definitions")
50
+ @classmethod
50
51
  def _validate_expression_literals_and_definitions(
51
- cls, definitions: Dict[str, ValidDefinitions], values: Dict[str, Any]
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.root_validator
74
- def _validate_expression(cls, values: Dict[str, Any]) -> Dict[str, Any]:
75
- expression: Optional[str] = values.get("expression")
76
- definitions: Optional[Dict[str, ValidDefinitions]] = values.get("definitions")
77
- machine_precision: Optional[int] = values.get("machine_precision")
78
- if expression is None or definitions is None or machine_precision is None:
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=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, definitions, machine_precision
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._include_sign or min(suggested_bounds) >= 0:
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