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
@@ -14,7 +14,9 @@ from typing import (
14
14
  )
15
15
 
16
16
  import pydantic
17
- from pydantic.generics import GenericModel
17
+ from pydantic import BaseModel
18
+ from pydantic_core.core_schema import ValidationInfo
19
+ from typing_extensions import Self
18
20
 
19
21
  from classiq.interface.enum_utils import StrEnum
20
22
  from classiq.interface.exceptions import ClassiqValueError
@@ -54,21 +56,24 @@ class ArgToInplace(StrEnum):
54
56
 
55
57
 
56
58
  class BinaryOpParams(
57
- ArithmeticOperationParams, GenericModel, Generic[LeftDataT, RightDataT]
59
+ ArithmeticOperationParams, BaseModel, Generic[LeftDataT, RightDataT]
58
60
  ):
59
61
  left_arg: LeftDataT
60
62
  right_arg: RightDataT
61
63
  left_arg_name: ClassVar[str] = DEFAULT_LEFT_ARG_NAME
62
64
  right_arg_name: ClassVar[str] = DEFAULT_RIGHT_ARG_NAME
63
65
 
64
- @pydantic.root_validator(pre=True)
65
- def _clone_repeated_arg(cls, values: Dict[str, Any]) -> Dict[str, Any]:
66
- left_arg = values.get("left_arg")
67
- right_arg = values.get("right_arg")
68
- if left_arg is right_arg and isinstance(left_arg, pydantic.BaseModel):
69
- # In case both arguments refer to the same object, copy it.
70
- # This prevents changes performed on one argument to affect the other.
71
- values["right_arg"] = left_arg.copy(deep=True)
66
+ @pydantic.model_validator(mode="before")
67
+ @classmethod
68
+ def _clone_repeated_arg(cls, values: Any) -> Dict[str, Any]:
69
+ if isinstance(values, dict):
70
+ left_arg = values.get("left_arg")
71
+ right_arg = values.get("right_arg")
72
+
73
+ if left_arg is right_arg and isinstance(left_arg, pydantic.BaseModel):
74
+ # In case both arguments refer to the same object, copy it.
75
+ # This prevents changes performed on one argument to affect the other.
76
+ values["right_arg"] = left_arg.model_copy(deep=True)
72
77
  return values
73
78
 
74
79
  def garbage_output_size(self) -> pydantic.NonNegativeInt:
@@ -106,19 +111,21 @@ class InplacableBinaryOpParams(
106
111
  ):
107
112
  inplace_arg: Optional[ArgToInplace] = None
108
113
 
109
- @pydantic.root_validator(pre=True)
110
- def _validate_inplace_arg(cls, values: Dict[str, Any]) -> Dict[str, Any]:
111
- left_arg = values.get("left_arg")
112
- right_arg = values.get("right_arg")
113
- inplace_arg: Optional[ArgToInplace] = values.get("inplace_arg")
114
- if inplace_arg == ArgToInplace.RIGHT and isinstance(right_arg, Numeric):
115
- raise ClassiqValueError(
116
- _NumericArgumentInplaceErrorMessage.format(right_arg)
117
- )
118
- elif inplace_arg == ArgToInplace.LEFT and isinstance(left_arg, Numeric):
119
- raise ClassiqValueError(
120
- _NumericArgumentInplaceErrorMessage.format(left_arg)
121
- )
114
+ @pydantic.model_validator(mode="before")
115
+ @classmethod
116
+ def _validate_inplace_arg(cls, values: Any) -> Dict[str, Any]:
117
+ if isinstance(values, dict):
118
+ left_arg = values.get("left_arg")
119
+ right_arg = values.get("right_arg")
120
+ inplace_arg: Optional[ArgToInplace] = values.get("inplace_arg")
121
+ if inplace_arg == ArgToInplace.RIGHT and isinstance(right_arg, Numeric):
122
+ raise ClassiqValueError(
123
+ _NumericArgumentInplaceErrorMessage.format(right_arg)
124
+ )
125
+ elif inplace_arg == ArgToInplace.LEFT and isinstance(left_arg, Numeric):
126
+ raise ClassiqValueError(
127
+ _NumericArgumentInplaceErrorMessage.format(left_arg)
128
+ )
122
129
  return values
123
130
 
124
131
  def _create_ios(self) -> None:
@@ -192,28 +199,28 @@ class InplacableBinaryOpParams(
192
199
  yield ArgToInplace.LEFT
193
200
 
194
201
  def get_params_inplace_options(self) -> Iterable["InplacableBinaryOpParams"]:
195
- params_kwargs = self.copy().__dict__
202
+ params_kwargs = self.model_copy().__dict__
196
203
  for inplace_arg in self._get_binary_op_inplace_options():
197
204
  params_kwargs["inplace_arg"] = inplace_arg
198
205
  yield self.__class__(**params_kwargs)
199
206
 
200
207
 
201
208
  class BinaryOpWithIntInputs(BinaryOpParams[RegisterOrInt, RegisterOrInt]):
202
- @pydantic.root_validator()
203
- def validate_int_registers(cls, values: Dict[str, Any]) -> Dict[str, Any]:
204
- left_arg = values.get("left_arg")
209
+ @pydantic.model_validator(mode="after")
210
+ def validate_int_registers(self) -> Self:
211
+ left_arg = self.left_arg
205
212
  is_left_arg_float_register = (
206
213
  isinstance(left_arg, RegisterArithmeticInfo)
207
214
  and left_arg.fraction_places > 0
208
215
  )
209
- right_arg = values.get("right_arg")
216
+ right_arg = self.right_arg
210
217
  is_right_arg_float_register = (
211
218
  isinstance(right_arg, RegisterArithmeticInfo)
212
219
  and right_arg.fraction_places > 0
213
220
  )
214
221
  if is_left_arg_float_register or is_right_arg_float_register:
215
222
  raise ClassiqValueError(BOOLEAN_OP_WITH_FRACTIONS_ERROR)
216
- return values
223
+ return self
217
224
 
218
225
  @staticmethod
219
226
  def _is_signed(arg: Union[int, RegisterArithmeticInfo]) -> bool:
@@ -344,11 +351,18 @@ class Subtractor(InplacableBinaryOpParams[RegisterOrConst, RegisterOrConst]):
344
351
  argument_utils.fraction_places(self.effective_right_arg),
345
352
  )
346
353
 
354
+ size = self.output_size or self._get_output_size(bounds, fraction_places)
355
+ is_signed = self._include_sign and min(bounds) < 0
347
356
  return RegisterArithmeticInfo(
348
- size=self.output_size or self._get_output_size(bounds, fraction_places),
357
+ size=size,
349
358
  fraction_places=fraction_places,
350
- is_signed=self._include_sign and min(bounds) < 0,
351
- bounds=self._legal_bounds(bounds),
359
+ is_signed=is_signed,
360
+ bounds=self._legal_bounds(
361
+ bounds,
362
+ RegisterArithmeticInfo.get_maximal_bounds(
363
+ size=size, is_signed=is_signed, fraction_places=fraction_places
364
+ ),
365
+ ),
352
366
  )
353
367
 
354
368
  def _get_output_size(
@@ -492,12 +506,20 @@ class Multiplier(BinaryOpWithFloatInputs):
492
506
  )
493
507
  bounds = number_utils.bounds_cut(bounds, max_bounds)
494
508
 
509
+ size = self.output_size or self._get_output_size(
510
+ bounds, fraction_places, left_arg, right_arg
511
+ )
512
+ is_signed = self._include_sign and min(bounds) < 0
495
513
  return RegisterArithmeticInfo(
496
- size=self.output_size
497
- or self._get_output_size(bounds, fraction_places, left_arg, right_arg),
514
+ size=size,
498
515
  fraction_places=fraction_places,
499
- is_signed=self._include_sign and min(bounds) < 0,
500
- bounds=self._legal_bounds(bounds),
516
+ is_signed=is_signed,
517
+ bounds=self._legal_bounds(
518
+ bounds,
519
+ RegisterArithmeticInfo.get_maximal_bounds(
520
+ size=size, is_signed=is_signed, fraction_places=fraction_places
521
+ ),
522
+ ),
501
523
  )
502
524
 
503
525
  @staticmethod
@@ -562,7 +584,8 @@ class LessEqual(Comparator):
562
584
  class Power(BinaryOpParams[RegisterArithmeticInfo, pydantic.PositiveInt]):
563
585
  output_name = "powered"
564
586
 
565
- @pydantic.validator("right_arg", pre=True)
587
+ @pydantic.field_validator("right_arg", mode="before")
588
+ @classmethod
566
589
  def _validate_legal_power(cls, right_arg: Any) -> pydantic.PositiveInt:
567
590
  if not float(right_arg).is_integer():
568
591
  raise ClassiqValueError("Power must be an integer")
@@ -674,19 +697,19 @@ class LShift(EffectiveUnaryOpParams[pydantic.NonNegativeInt]):
674
697
  output_name = "left_shifted"
675
698
  inplace_arg: Optional[ArgToInplace] = ArgToInplace.LEFT
676
699
 
677
- @pydantic.root_validator()
678
- def _validate_legal_modulo(cls, values: Dict[str, Any]) -> Dict[str, Any]:
679
- output_size = values.get("output_size")
700
+ @pydantic.model_validator(mode="after")
701
+ def _validate_legal_modulo(self) -> Self:
702
+ output_size = self.output_size
680
703
  if output_size is None:
681
- return values
682
- arg = values.get("left_arg")
683
- shift = values.get("right_arg")
704
+ return self
705
+ arg = self.left_arg
706
+ shift = self.right_arg
684
707
  if not isinstance(arg, RegisterArithmeticInfo):
685
708
  raise ClassiqValueError("left arg must be a RegisterArithmeticInfo")
686
709
  if not isinstance(shift, int):
687
710
  raise ClassiqValueError("Shift must be an integer")
688
711
  assert arg.fraction_places - shift <= 0, _FLOATING_POINT_MODULO_ERROR_MESSAGE
689
- return values
712
+ return self
690
713
 
691
714
  def garbage_output_size(self) -> pydantic.NonNegativeInt:
692
715
  if self.inplace_arg is None or self.output_size is None:
@@ -715,21 +738,21 @@ class RShift(EffectiveUnaryOpParams[pydantic.NonNegativeInt]):
715
738
  def _shifted_fraction_places(*, arg: RegisterArithmeticInfo, shift: int) -> int:
716
739
  return arg.fraction_places * int(arg.is_signed or shift < arg.size)
717
740
 
718
- @pydantic.root_validator()
719
- def _validate_legal_modulo(cls, values: Dict[str, Any]) -> Dict[str, Any]:
720
- output_size = values.get("output_size")
741
+ @pydantic.model_validator(mode="after")
742
+ def _validate_legal_modulo(self) -> Self:
743
+ output_size = self.output_size
721
744
  if output_size is None:
722
- return values
723
- arg = values.get("left_arg")
724
- shift = values.get("right_arg")
745
+ return self
746
+ arg = self.left_arg
747
+ shift = self.right_arg
725
748
  if not isinstance(arg, RegisterArithmeticInfo):
726
749
  raise ClassiqValueError("left arg must be a RegisterArithmeticInfo")
727
750
  if not isinstance(shift, int):
728
751
  raise ClassiqValueError("Shift must be an integer")
729
752
  assert (
730
- cls._shifted_fraction_places(arg=arg, shift=shift) == 0
753
+ self._shifted_fraction_places(arg=arg, shift=shift) == 0
731
754
  ), _FLOATING_POINT_MODULO_ERROR_MESSAGE
732
- return values
755
+ return self
733
756
 
734
757
  def garbage_output_size(self) -> pydantic.NonNegativeInt:
735
758
  if self.inplace_arg is None:
@@ -759,16 +782,16 @@ class CyclicShift(EffectiveUnaryOpParams[int]):
759
782
  output_name = "cyclic_shifted"
760
783
  inplace_arg: Optional[ArgToInplace] = ArgToInplace.LEFT
761
784
 
762
- @pydantic.root_validator()
763
- def _validate_legal_modulo(cls, values: Dict[str, Any]) -> Dict[str, Any]:
764
- output_size = values.get("output_size")
785
+ @pydantic.model_validator(mode="after")
786
+ def _validate_legal_modulo(self) -> Self:
787
+ output_size = self.output_size
765
788
  if output_size is None:
766
- return values
767
- arg = values.get("left_arg")
789
+ return self
790
+ arg = self.left_arg
768
791
  if not isinstance(arg, RegisterArithmeticInfo):
769
792
  raise ClassiqValueError("left arg must be a RegisterArithmeticInfo")
770
793
  assert arg.fraction_places == 0, _FLOATING_POINT_MODULO_ERROR_MESSAGE
771
- return values
794
+ return self
772
795
 
773
796
  def garbage_output_size(self) -> pydantic.NonNegativeInt:
774
797
  if self.inplace_arg is None:
@@ -787,24 +810,26 @@ class Modulo(EffectiveUnaryOpParams[int]):
787
810
  output_name = "modulus"
788
811
  inplace_arg: Optional[ArgToInplace] = ArgToInplace.LEFT
789
812
 
790
- @pydantic.validator("left_arg")
813
+ @pydantic.field_validator("left_arg", mode="before")
814
+ @classmethod
791
815
  def _validate_left_arg_is_integer(
792
816
  cls, left_arg: RegisterArithmeticInfo
793
817
  ) -> RegisterArithmeticInfo:
794
818
  assert left_arg.fraction_places == 0, _FLOATING_POINT_MODULO_ERROR_MESSAGE
795
819
  return left_arg
796
820
 
797
- @pydantic.validator("right_arg")
821
+ @pydantic.field_validator("right_arg", mode="before")
822
+ @classmethod
798
823
  def _validate_right_arg_is_a_power_of_two(
799
- cls, right_arg: int, values: Dict[str, Any]
824
+ cls, right_arg: int, info: ValidationInfo
800
825
  ) -> int:
801
826
  repr_qubits_float = math.log2(right_arg)
802
827
  repr_qubits = round(repr_qubits_float)
803
828
  assert abs(repr_qubits - repr_qubits_float) < 10**-8, NOT_POWER_OF_TWO_ERROR_MSG
804
- output_size = values.get("output_size")
829
+ output_size = info.data.get("output_size")
805
830
  if output_size is not None:
806
831
  repr_qubits = min(repr_qubits, output_size)
807
- values["output_size"] = None
832
+ info.data["output_size"] = None
808
833
  return 2 ** (repr_qubits)
809
834
 
810
835
  def _get_result_register(self) -> RegisterArithmeticInfo:
@@ -24,16 +24,18 @@ class Extremum(ArithmeticOperationParams):
24
24
  left_arg: RegisterOrConst
25
25
  right_arg: RegisterOrConst
26
26
 
27
- @pydantic.root_validator(pre=True)
28
- def _validate_one_is_register(cls, values: Dict[str, Any]) -> Dict[str, Any]:
29
- left_arg = values.get("left_arg")
30
- right_arg = values.get("right_arg")
31
- if isinstance(left_arg, Numeric) and isinstance(right_arg, Numeric):
32
- raise ClassiqValueError("One argument must be a register")
33
- if left_arg is right_arg and isinstance(left_arg, pydantic.BaseModel):
34
- # In case both arguments refer to the same object, copy it.
35
- # This prevents changes performed on one argument from affecting the other.
36
- values["right_arg"] = left_arg.copy(deep=True)
27
+ @pydantic.model_validator(mode="before")
28
+ @classmethod
29
+ def _validate_one_is_register(cls, values: Any) -> Dict[str, Any]:
30
+ if isinstance(values, dict):
31
+ left_arg = values.get("left_arg")
32
+ right_arg = values.get("right_arg")
33
+ if isinstance(left_arg, Numeric) and isinstance(right_arg, Numeric):
34
+ raise ClassiqValueError("One argument must be a register")
35
+ if left_arg is right_arg and isinstance(left_arg, pydantic.BaseModel):
36
+ # In case both arguments refer to the same object, copy it.
37
+ # This prevents changes performed on one argument from affecting the other.
38
+ values["right_arg"] = left_arg.model_copy(deep=True)
37
39
  return values
38
40
 
39
41
  def _create_ios(self) -> None:
@@ -116,11 +118,18 @@ class Extremum(ArithmeticOperationParams):
116
118
  size=self.output_size, is_signed=False, fraction_places=0
117
119
  )
118
120
  bounds = number_utils.bounds_cut(bounds, max_bounds)
121
+ size = self.output_size or required_size
122
+ is_signed = self._include_sign and min(bounds) < 0
119
123
  return RegisterArithmeticInfo(
120
- size=self.output_size or required_size,
124
+ size=size,
121
125
  fraction_places=fraction_places,
122
- is_signed=self._include_sign and min(bounds) < 0,
123
- bounds=self._legal_bounds(bounds),
126
+ is_signed=is_signed,
127
+ bounds=self._legal_bounds(
128
+ bounds,
129
+ RegisterArithmeticInfo.get_maximal_bounds(
130
+ size=size, is_signed=is_signed, fraction_places=fraction_places
131
+ ),
132
+ ),
124
133
  )
125
134
 
126
135
 
@@ -1,6 +1,7 @@
1
1
  from typing import Iterable, List, Optional
2
2
 
3
3
  import pydantic
4
+ from pydantic import ConfigDict
4
5
 
5
6
  from classiq.interface.exceptions import ClassiqValueError
6
7
  from classiq.interface.generator.arith.argument_utils import RegisterOrConst
@@ -23,13 +24,15 @@ class LogicalOps(ArithmeticOperationParams):
23
24
  def update_should_invert_node_list(self, invert_args: List[str]) -> None:
24
25
  self._should_invert_node_list.extend(invert_args)
25
26
 
26
- @pydantic.validator("output_size")
27
+ @pydantic.field_validator("output_size")
28
+ @classmethod
27
29
  def _validate_output_size(cls, output_size: Optional[int]) -> int:
28
30
  if output_size is not None and output_size != 1:
29
31
  raise ClassiqValueError("logical operation output size must be 1")
30
32
  return 1
31
33
 
32
- @pydantic.validator("args")
34
+ @pydantic.field_validator("args")
35
+ @classmethod
33
36
  def _validate_inputs_sizes(
34
37
  cls, arguments: List[RegisterOrConst]
35
38
  ) -> List[RegisterOrConst]:
@@ -64,8 +67,7 @@ class LogicalOps(ArithmeticOperationParams):
64
67
  def get_params_inplace_options(self) -> Iterable["LogicalOps"]:
65
68
  return ()
66
69
 
67
- class Config:
68
- arbitrary_types_allowed = True
70
+ model_config = ConfigDict(arbitrary_types_allowed=True)
69
71
 
70
72
 
71
73
  class LogicalAnd(LogicalOps):
@@ -12,7 +12,7 @@ def signed_int_to_unsigned(number: int) -> int:
12
12
  return number + 2 ** (number.bit_length() + 1 * not_power2)
13
13
 
14
14
 
15
- def _binary_to_int(bin_rep: str, is_signed: bool) -> int:
15
+ def binary_to_int(bin_rep: str, is_signed: bool = False) -> int:
16
16
  negative_offset: int = -(2 ** len(bin_rep)) * (bin_rep[0] == "1") * is_signed
17
17
  return int(bin_rep, 2) + negative_offset
18
18
 
@@ -20,14 +20,14 @@ def _binary_to_int(bin_rep: str, is_signed: bool) -> int:
20
20
  def binary_to_float(
21
21
  bin_rep: str, fraction_part_size: int = 0, is_signed: bool = False
22
22
  ) -> float:
23
- return _binary_to_int(bin_rep, is_signed) / 2**fraction_part_size
23
+ return binary_to_int(bin_rep, is_signed) / 2**fraction_part_size
24
24
 
25
25
 
26
26
  def binary_to_float_or_int(
27
27
  bin_rep: str, fraction_part_size: int = 0, is_signed: bool = False
28
28
  ) -> Union[float, int]:
29
29
  if fraction_part_size == 0:
30
- return _binary_to_int(bin_rep, is_signed)
30
+ return binary_to_int(bin_rep, is_signed)
31
31
  return binary_to_float(bin_rep, fraction_part_size, is_signed)
32
32
 
33
33
 
@@ -1,6 +1,8 @@
1
1
  from typing import Any, Dict, Optional, Tuple
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.arith import number_utils
@@ -11,16 +13,28 @@ from classiq.interface.helpers.hashable_pydantic_base_model import (
11
13
 
12
14
 
13
15
  class RegisterArithmeticInfo(HashablePydanticBaseModel):
14
- size: pydantic.PositiveInt
16
+ size: pydantic.PositiveInt = pydantic.Field(default=1)
15
17
  is_signed: bool = pydantic.Field(default=False)
16
18
  fraction_places: pydantic.NonNegativeInt = pydantic.Field(default=0)
17
19
  bypass_bounds_validation: bool = pydantic.Field(default=False)
18
- bounds: PydanticFloatTuple = pydantic.Field(default=None)
20
+ bounds: PydanticFloatTuple = pydantic.Field(
21
+ default=None,
22
+ validate_default=True,
23
+ )
19
24
 
20
- @pydantic.root_validator(pre=True)
21
- def _remove_name(cls, values: Dict[str, Any]) -> Dict[str, Any]:
22
- if "name" in values:
23
- values.pop("name")
25
+ @pydantic.model_validator(mode="before")
26
+ @classmethod
27
+ def _remove_name(cls, values: Any, info: ValidationInfo) -> Dict[str, Any]:
28
+ if isinstance(values, dict):
29
+ values = values.copy()
30
+ elif hasattr(values, "__dict__"):
31
+ values = values.__dict__.copy()
32
+ elif values is None and info.data:
33
+ values = info.data.copy()
34
+ else:
35
+ raise ValueError("Unsupported type for removing 'name'.")
36
+
37
+ values.pop("name", None)
24
38
  return values
25
39
 
26
40
  @staticmethod
@@ -32,13 +46,14 @@ class RegisterArithmeticInfo(HashablePydanticBaseModel):
32
46
  fraction_factor = float(2**-fraction_places)
33
47
  return (lb * fraction_factor, ub * fraction_factor)
34
48
 
35
- @pydantic.validator("bounds", always=True)
49
+ @pydantic.field_validator("bounds", mode="before")
50
+ @classmethod
36
51
  def _validate_bounds(
37
- cls, bounds: Optional[PydanticFloatTuple], values: Dict[str, Any]
52
+ cls, bounds: Optional[PydanticFloatTuple], info: ValidationInfo
38
53
  ) -> PydanticFloatTuple:
39
- size = values.get("size")
40
- is_signed = values.get("is_signed", False)
41
- fraction_places = values.get("fraction_places", 0)
54
+ size = info.data.get("size")
55
+ is_signed = info.data.get("is_signed", False)
56
+ fraction_places = info.data.get("fraction_places", 0)
42
57
  if not isinstance(size, int):
43
58
  raise ClassiqValueError("RegisterArithmeticInfo must have an integer size")
44
59
 
@@ -52,7 +67,7 @@ class RegisterArithmeticInfo(HashablePydanticBaseModel):
52
67
  number_utils.limit_fraction_places(min(bounds), fraction_places),
53
68
  number_utils.limit_fraction_places(max(bounds), fraction_places),
54
69
  )
55
- if not values.get("bypass_bounds_validation", False):
70
+ if not info.data.get("bypass_bounds_validation", False):
56
71
  assert min(trimmed_bounds) >= min(maximal_bounds), "Illegal bound min"
57
72
  assert max(trimmed_bounds) <= max(maximal_bounds), "Illegal bound max"
58
73
  return trimmed_bounds
@@ -79,8 +94,7 @@ class RegisterArithmeticInfo(HashablePydanticBaseModel):
79
94
  def integer_part_size(self) -> pydantic.NonNegativeInt:
80
95
  return self.size - self.fraction_places
81
96
 
82
- class Config:
83
- frozen = True
97
+ model_config = ConfigDict(frozen=True)
84
98
 
85
99
 
86
100
  class RegisterUserInput(RegisterArithmeticInfo):
@@ -90,12 +104,13 @@ class RegisterUserInput(RegisterArithmeticInfo):
90
104
  super().__init__(**data)
91
105
  self._fields_to_skip_in_hash = frozenset({"name"})
92
106
 
93
- @pydantic.root_validator(pre=True)
94
- def _remove_name(cls, values: Dict[str, Any]) -> Dict[str, Any]:
107
+ @pydantic.model_validator(mode="before")
108
+ @classmethod
109
+ def _remove_name(cls, values: Any, info: ValidationInfo) -> Dict[str, Any]:
95
110
  return values
96
111
 
97
112
  def revalued(self, **kwargs: Any) -> "RegisterUserInput":
98
- return self.copy(update=kwargs)
113
+ return self.model_copy(update=kwargs)
99
114
 
100
115
  @classmethod
101
116
  def from_arithmetic_info(
@@ -1,6 +1,7 @@
1
1
  from typing import TYPE_CHECKING, Final, Iterable, Optional
2
2
 
3
3
  import pydantic
4
+ from pydantic import ConfigDict
4
5
 
5
6
  from classiq.interface.exceptions import ClassiqValueError
6
7
  from classiq.interface.generator.arith import argument_utils, number_utils
@@ -52,12 +53,11 @@ class UnaryOpParams(ArithmeticOperationParams):
52
53
  return self.inplace
53
54
 
54
55
  def get_params_inplace_options(self) -> Iterable["UnaryOpParams"]:
55
- params_kwargs = self.copy().__dict__
56
+ params_kwargs = self.model_copy().model_dump()
56
57
  params_kwargs["inplace"] = True
57
58
  yield self.__class__(**params_kwargs)
58
59
 
59
- class Config:
60
- arbitrary_types_allowed = True
60
+ model_config = ConfigDict(arbitrary_types_allowed=True)
61
61
 
62
62
 
63
63
  class BitwiseInvert(UnaryOpParams):
@@ -119,7 +119,8 @@ class Negation(UnaryOpParams):
119
119
  class Sign(UnaryOpParams):
120
120
  output_name = "sign"
121
121
 
122
- @pydantic.validator("output_size")
122
+ @pydantic.field_validator("output_size")
123
+ @classmethod
123
124
  def _validate_output_size(
124
125
  cls, output_size: Optional[pydantic.PositiveInt]
125
126
  ) -> pydantic.PositiveInt:
@@ -16,7 +16,8 @@ from classiq.interface.generator.function_params import (
16
16
  class ChemistryFunctionParams(FunctionParams):
17
17
  gs_problem: CHEMISTRY_PROBLEMS_TYPE
18
18
 
19
- @pydantic.validator("gs_problem")
19
+ @pydantic.field_validator("gs_problem")
20
+ @classmethod
20
21
  def validate_gs_problem_contains_num_qubits(
21
22
  cls, gs_problem: CHEMISTRY_PROBLEMS_TYPE
22
23
  ) -> CHEMISTRY_PROBLEMS_TYPE:
@@ -18,7 +18,8 @@ class CircuitCodeInterface(pydantic.BaseModel):
18
18
  outputs: Dict[QuantumFormat, Code]
19
19
  qasm_version: QasmVersion
20
20
 
21
- @pydantic.validator("outputs")
21
+ @pydantic.field_validator("outputs")
22
+ @classmethod
22
23
  def reformat_long_string_output_formats(
23
24
  cls, outputs: Dict[QuantumFormat, str]
24
25
  ) -> Dict[QuantumFormat, LongStr]:
@@ -8,8 +8,8 @@ from classiq.interface.generator.function_params import (
8
8
  DEFAULT_INPUT_NAME,
9
9
  DEFAULT_OUTPUT_NAME,
10
10
  FunctionParams,
11
+ FunctionParamsNumericParameter,
11
12
  )
12
- from classiq.interface.generator.parameters import ParameterFloatType
13
13
 
14
14
 
15
15
  class CommutingPauliExponentiation(FunctionParams):
@@ -20,17 +20,18 @@ class CommutingPauliExponentiation(FunctionParams):
20
20
  pauli_operator: PauliOperator = pydantic.Field(
21
21
  description="A weighted sum of Pauli strings."
22
22
  )
23
- evolution_coefficient: ParameterFloatType = pydantic.Field(
23
+ evolution_coefficient: FunctionParamsNumericParameter = pydantic.Field(
24
24
  default=1.0,
25
25
  description="A global coefficient multiplying the operator.",
26
- is_exec_param=True,
27
26
  )
28
27
 
29
- @pydantic.validator("pauli_operator")
28
+ @pydantic.field_validator("pauli_operator")
29
+ @classmethod
30
30
  def _validate_is_hermitian(cls, pauli_operator: PauliOperator) -> PauliOperator:
31
31
  return operator.validate_operator_is_hermitian(pauli_operator)
32
32
 
33
- @pydantic.validator("pauli_operator")
33
+ @pydantic.field_validator("pauli_operator")
34
+ @classmethod
34
35
  def _validate_paulis_commute(cls, pauli_operator: PauliOperator) -> PauliOperator:
35
36
  if not pauli_operator.is_commutative:
36
37
  raise ClassiqValueError("Pauli strings are not commutative")
@@ -1,23 +1,19 @@
1
- from typing import Any, Callable, Dict, Iterator
1
+ from typing import Union
2
2
 
3
- from typing_extensions import Self
3
+ from pydantic import PlainSerializer, PlainValidator
4
+ from pydantic.json_schema import WithJsonSchema
5
+ from typing_extensions import Annotated
4
6
 
5
7
 
6
- # Use this class as a type for complex data from the json, e.g., in the state_propagator function.
7
- class Complex(complex):
8
- @classmethod
9
- def __get_validators__(cls) -> Iterator[Callable]:
10
- yield cls.validate
8
+ def validate_complex(v: Union[complex, str]) -> complex:
9
+ if isinstance(v, str):
10
+ v = "".join(v.split())
11
+ return complex(v)
11
12
 
12
- @classmethod
13
- def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None:
14
- field_schema.update(
15
- pattern=r"[+-]?\d+\.?\d* *[+-] *\d+\.?\d*j",
16
- )
17
13
 
18
- @classmethod
19
- def validate(cls, v: Any) -> Self:
20
- if isinstance(v, str):
21
- v = "".join(v.split())
22
-
23
- return cls(v)
14
+ Complex = Annotated[
15
+ complex,
16
+ PlainValidator(validate_complex),
17
+ PlainSerializer(lambda x: str(x)),
18
+ WithJsonSchema({"type": "string", "pattern": r"[+-]?\d+\.?\d* *[+-] *\d+\.?\d*j"}),
19
+ ]