ordinals.py 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. from sympy.core import Basic, Integer
  2. import operator
  3. class OmegaPower(Basic):
  4. """
  5. Represents ordinal exponential and multiplication terms one of the
  6. building blocks of the :class:`Ordinal` class.
  7. In ``OmegaPower(a, b)``, ``a`` represents exponent and ``b`` represents multiplicity.
  8. """
  9. def __new__(cls, a, b):
  10. if isinstance(b, int):
  11. b = Integer(b)
  12. if not isinstance(b, Integer) or b <= 0:
  13. raise TypeError("multiplicity must be a positive integer")
  14. if not isinstance(a, Ordinal):
  15. a = Ordinal.convert(a)
  16. return Basic.__new__(cls, a, b)
  17. @property
  18. def exp(self):
  19. return self.args[0]
  20. @property
  21. def mult(self):
  22. return self.args[1]
  23. def _compare_term(self, other, op):
  24. if self.exp == other.exp:
  25. return op(self.mult, other.mult)
  26. else:
  27. return op(self.exp, other.exp)
  28. def __eq__(self, other):
  29. if not isinstance(other, OmegaPower):
  30. try:
  31. other = OmegaPower(0, other)
  32. except TypeError:
  33. return NotImplemented
  34. return self.args == other.args
  35. def __hash__(self):
  36. return Basic.__hash__(self)
  37. def __lt__(self, other):
  38. if not isinstance(other, OmegaPower):
  39. try:
  40. other = OmegaPower(0, other)
  41. except TypeError:
  42. return NotImplemented
  43. return self._compare_term(other, operator.lt)
  44. class Ordinal(Basic):
  45. """
  46. Represents ordinals in Cantor normal form.
  47. Internally, this class is just a list of instances of OmegaPower.
  48. Examples
  49. ========
  50. >>> from sympy import Ordinal, OmegaPower
  51. >>> from sympy.sets.ordinals import omega
  52. >>> w = omega
  53. >>> w.is_limit_ordinal
  54. True
  55. >>> Ordinal(OmegaPower(w + 1, 1), OmegaPower(3, 2))
  56. w**(w + 1) + w**3*2
  57. >>> 3 + w
  58. w
  59. >>> (w + 1) * w
  60. w**2
  61. References
  62. ==========
  63. .. [1] https://en.wikipedia.org/wiki/Ordinal_arithmetic
  64. """
  65. def __new__(cls, *terms):
  66. obj = super().__new__(cls, *terms)
  67. powers = [i.exp for i in obj.args]
  68. if not all(powers[i] >= powers[i+1] for i in range(len(powers) - 1)):
  69. raise ValueError("powers must be in decreasing order")
  70. return obj
  71. @property
  72. def terms(self):
  73. return self.args
  74. @property
  75. def leading_term(self):
  76. if self == ord0:
  77. raise ValueError("ordinal zero has no leading term")
  78. return self.terms[0]
  79. @property
  80. def trailing_term(self):
  81. if self == ord0:
  82. raise ValueError("ordinal zero has no trailing term")
  83. return self.terms[-1]
  84. @property
  85. def is_successor_ordinal(self):
  86. try:
  87. return self.trailing_term.exp == ord0
  88. except ValueError:
  89. return False
  90. @property
  91. def is_limit_ordinal(self):
  92. try:
  93. return not self.trailing_term.exp == ord0
  94. except ValueError:
  95. return False
  96. @property
  97. def degree(self):
  98. return self.leading_term.exp
  99. @classmethod
  100. def convert(cls, integer_value):
  101. if integer_value == 0:
  102. return ord0
  103. return Ordinal(OmegaPower(0, integer_value))
  104. def __eq__(self, other):
  105. if not isinstance(other, Ordinal):
  106. try:
  107. other = Ordinal.convert(other)
  108. except TypeError:
  109. return NotImplemented
  110. return self.terms == other.terms
  111. def __hash__(self):
  112. return hash(self.args)
  113. def __lt__(self, other):
  114. if not isinstance(other, Ordinal):
  115. try:
  116. other = Ordinal.convert(other)
  117. except TypeError:
  118. return NotImplemented
  119. for term_self, term_other in zip(self.terms, other.terms):
  120. if term_self != term_other:
  121. return term_self < term_other
  122. return len(self.terms) < len(other.terms)
  123. def __le__(self, other):
  124. return (self == other or self < other)
  125. def __gt__(self, other):
  126. return not self <= other
  127. def __ge__(self, other):
  128. return not self < other
  129. def __str__(self):
  130. net_str = ""
  131. plus_count = 0
  132. if self == ord0:
  133. return 'ord0'
  134. for i in self.terms:
  135. if plus_count:
  136. net_str += " + "
  137. if i.exp == ord0:
  138. net_str += str(i.mult)
  139. elif i.exp == 1:
  140. net_str += 'w'
  141. elif len(i.exp.terms) > 1 or i.exp.is_limit_ordinal:
  142. net_str += 'w**(%s)'%i.exp
  143. else:
  144. net_str += 'w**%s'%i.exp
  145. if not i.mult == 1 and not i.exp == ord0:
  146. net_str += '*%s'%i.mult
  147. plus_count += 1
  148. return(net_str)
  149. __repr__ = __str__
  150. def __add__(self, other):
  151. if not isinstance(other, Ordinal):
  152. try:
  153. other = Ordinal.convert(other)
  154. except TypeError:
  155. return NotImplemented
  156. if other == ord0:
  157. return self
  158. a_terms = list(self.terms)
  159. b_terms = list(other.terms)
  160. r = len(a_terms) - 1
  161. b_exp = other.degree
  162. while r >= 0 and a_terms[r].exp < b_exp:
  163. r -= 1
  164. if r < 0:
  165. terms = b_terms
  166. elif a_terms[r].exp == b_exp:
  167. sum_term = OmegaPower(b_exp, a_terms[r].mult + other.leading_term.mult)
  168. terms = a_terms[:r] + [sum_term] + b_terms[1:]
  169. else:
  170. terms = a_terms[:r+1] + b_terms
  171. return Ordinal(*terms)
  172. def __radd__(self, other):
  173. if not isinstance(other, Ordinal):
  174. try:
  175. other = Ordinal.convert(other)
  176. except TypeError:
  177. return NotImplemented
  178. return other + self
  179. def __mul__(self, other):
  180. if not isinstance(other, Ordinal):
  181. try:
  182. other = Ordinal.convert(other)
  183. except TypeError:
  184. return NotImplemented
  185. if ord0 in (self, other):
  186. return ord0
  187. a_exp = self.degree
  188. a_mult = self.leading_term.mult
  189. summation = []
  190. if other.is_limit_ordinal:
  191. for arg in other.terms:
  192. summation.append(OmegaPower(a_exp + arg.exp, arg.mult))
  193. else:
  194. for arg in other.terms[:-1]:
  195. summation.append(OmegaPower(a_exp + arg.exp, arg.mult))
  196. b_mult = other.trailing_term.mult
  197. summation.append(OmegaPower(a_exp, a_mult*b_mult))
  198. summation += list(self.terms[1:])
  199. return Ordinal(*summation)
  200. def __rmul__(self, other):
  201. if not isinstance(other, Ordinal):
  202. try:
  203. other = Ordinal.convert(other)
  204. except TypeError:
  205. return NotImplemented
  206. return other * self
  207. def __pow__(self, other):
  208. if not self == omega:
  209. return NotImplemented
  210. return Ordinal(OmegaPower(other, 1))
  211. class OrdinalZero(Ordinal):
  212. """The ordinal zero.
  213. OrdinalZero can be imported as ``ord0``.
  214. """
  215. pass
  216. class OrdinalOmega(Ordinal):
  217. """The ordinal omega which forms the base of all ordinals in cantor normal form.
  218. OrdinalOmega can be imported as ``omega``.
  219. Examples
  220. ========
  221. >>> from sympy.sets.ordinals import omega
  222. >>> omega + omega
  223. w*2
  224. """
  225. def __new__(cls):
  226. return Ordinal.__new__(cls)
  227. @property
  228. def terms(self):
  229. return (OmegaPower(1, 1),)
  230. ord0 = OrdinalZero()
  231. omega = OrdinalOmega()