algebraic_abstract.py 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. # algebraic_abstract.py: abstract class for algebraic objects
  2. # Copyright 2022 Romain Serra
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. from abc import ABCMeta, abstractmethod
  15. from typing import Optional, Union, Iterable
  16. import math
  17. AlgebraicOrScalar = Union["AlgebraicAbstract", complex, float, Iterable["AlgebraicAbstract"], Iterable[complex],
  18. Iterable[float]]
  19. class AlgebraicAbstract(metaclass=ABCMeta):
  20. """Abstract class for all objects of an algebra.
  21. """
  22. # constants for angular conversions
  23. _rad2deg_float = math.degrees(1.)
  24. _deg2rad_float = math.radians(1.)
  25. @abstractmethod
  26. def __add__(self, other: AlgebraicOrScalar) -> "AlgebraicAbstract":
  27. """Abstract method defining right-hand side addition to be implemented by all inheritors.
  28. Args:
  29. other (AlgebraicOrScalar): object to be added.
  30. Returns:
  31. AlgebraicAbstract: summed objects.
  32. """
  33. raise NotImplementedError
  34. @abstractmethod
  35. def __sub__(self, other: AlgebraicOrScalar) -> "AlgebraicAbstract":
  36. """Abstract method defining right-hand side subtraction to be implemented by all inheritors.
  37. Args:
  38. other (AlgebraicOrScalar): object to be subtracted.
  39. Returns:
  40. AlgebraicAbstract: subtracted objects.
  41. """
  42. raise NotImplementedError
  43. def __radd__(self, other: AlgebraicOrScalar) -> "AlgebraicAbstract":
  44. """Method defining left-hand side addition from right-hand side.
  45. Args:
  46. other (AlgebraicOrScalar): object to be added.
  47. Returns:
  48. AlgebraicAbstract: summed objects.
  49. """
  50. return self.__add__(other)
  51. def __rsub__(self, other: AlgebraicOrScalar) -> "AlgebraicAbstract":
  52. """Method defining left-hand side subtraction.
  53. Args:
  54. other (AlgebraicOrScalar): object to perform subtraction on.
  55. Returns:
  56. AlgebraicAbstract: subtracted objects.
  57. """
  58. return self.__neg__().__add__(other)
  59. @abstractmethod
  60. def __mul__(self, other: AlgebraicOrScalar) -> "AlgebraicAbstract":
  61. """Abstract method defining right-hand side multiplication to be implemented by all inheritors. It must work
  62. for the external multiplication i.e. with scalars and for the internal one, that is between expansions.
  63. Args:
  64. other (AlgebraicOrScalar): object to be multiplied with.
  65. Returns:
  66. AlgebraicAbstract: multiplied objects.
  67. """
  68. raise NotImplementedError
  69. def __rmul__(self, other: AlgebraicOrScalar) -> "AlgebraicAbstract":
  70. """Method defining left-hand side multiplication from right-hand side one.
  71. Args:
  72. other (AlgebraicOrScalar): object to be multiplied with.
  73. Returns:
  74. AlgebraicAbstract: multiplied objects.
  75. """
  76. return self.__mul__(other)
  77. def __neg__(self) -> "AlgebraicAbstract":
  78. """Method defining negation (additive inverse).
  79. Returns:
  80. AlgebraicAbstract: opposite of object (from the point of view of addition).
  81. """
  82. return self.__mul__(-1.)
  83. def pow2(self) -> "AlgebraicAbstract":
  84. """Method to raise algebraic objet to the power 2 i.e. compute its multiplicative square.
  85. Returns:
  86. AlgebraicAbstract: algebraic object raised to power 2.
  87. """
  88. return self * self
  89. def _pown(self, power: int) -> "AlgebraicAbstract":
  90. """Method to raise algebraic object at a power than is a natural integer greater or equal to two.
  91. Args:
  92. power (int): exponent.
  93. Returns:
  94. AlgebraicAbstract: algebraic object raised to input power.
  95. """
  96. if power == 2:
  97. return self.pow2()
  98. if power == 3:
  99. return self.pow2() * self
  100. half_power = int(power / 2)
  101. if float(half_power) == power / 2.:
  102. return self._pown(half_power).pow2() # recursive call
  103. # exponent is odd
  104. return self._pown(half_power).pow2() * self # recursive call
  105. @abstractmethod
  106. def __pow__(self, power: Union[float, int], modulo: Optional[float] = None) -> "AlgebraicAbstract":
  107. """Abstract method defining power rising to be implemented by all inheritors.
  108. Args:
  109. power (Union[float, int]): exponent.
  110. Returns:
  111. AlgebraicAbstract: powered object.
  112. """
  113. raise NotImplementedError
  114. def __truediv__(self, other: AlgebraicOrScalar) -> "AlgebraicAbstract":
  115. """Method defining left-hand side division to be implemented by all inheritors.
  116. Args:
  117. other (AlgebraicOrScalar): object to divide with.
  118. Returns:
  119. AlgebraicAbstract: object divided by input.
  120. """
  121. if isinstance(other, self.__class__):
  122. return self * other.reciprocal()
  123. # scalar case
  124. return self * (1. / other)
  125. def __rtruediv__(self, other: AlgebraicOrScalar) -> "AlgebraicAbstract":
  126. """Method defining right-hand side division to be implemented by all inheritors.
  127. Args:
  128. other (AlgebraicOrScalar): object divided.
  129. Returns:
  130. AlgebraicAbstract: input divided by object.
  131. """
  132. return other * self.reciprocal()
  133. @abstractmethod
  134. def reciprocal(self) -> "AlgebraicAbstract":
  135. """Method defining the reciprocal a.k.a. multiplicative inverse (within the algebra).
  136. Returns:
  137. AlgebraicAbstract: multiplicative inverse of object.
  138. """
  139. raise NotImplementedError
  140. def degrees(self) -> "AlgebraicAbstract":
  141. """
  142. Method to convert from radians to degrees.
  143. Returns:
  144. AlgebraicAbstract: object converted into degrees (assuming radians originally)
  145. """
  146. return self * self._rad2deg_float
  147. def radians(self) -> "AlgebraicAbstract":
  148. """
  149. Method to convert from degrees to radians.
  150. Returns:
  151. AlgebraicAbstract: object converted into radians (assuming degrees originally)
  152. """
  153. return self * self._deg2rad_float
  154. def deg2rad(self) -> "AlgebraicAbstract":
  155. return self.radians()
  156. def rad2deg(self) -> "AlgebraicAbstract":
  157. return self.degrees()
  158. @abstractmethod
  159. def __str__(self) -> str:
  160. """Abstract method to cast object as a string.
  161. Returns:
  162. str: string representing the object.
  163. """
  164. raise NotImplementedError
  165. @abstractmethod
  166. def copy(self) -> "AlgebraicAbstract":
  167. """Abstract method defining copy to be implemented by all inheritors.
  168. Returns:
  169. AlgebraicAbstract: copied object.
  170. """
  171. return self.__class__()
  172. @abstractmethod
  173. def log(self) -> "AlgebraicAbstract":
  174. """Natural logarithm of algebraic object.
  175. Returns:
  176. AlgebraicAbstract: natural logarithm of algebraic object.
  177. """
  178. raise NotImplementedError
  179. @abstractmethod
  180. def exp(self) -> "AlgebraicAbstract":
  181. """Exponential of algebraic object.
  182. Returns:
  183. AlgebraicAbstract: exponential of algebraic object.
  184. """
  185. raise NotImplementedError
  186. def sqrt(self) -> "AlgebraicAbstract":
  187. """Square root of algebraic object.
  188. Returns:
  189. AlgebraicAbstract: square root of algebraic object.
  190. """
  191. return (self.log() / 2.).exp()