Spaces:
Sleeping
Sleeping
from sympy.core import Basic, Integer | |
import operator | |
class OmegaPower(Basic): | |
""" | |
Represents ordinal exponential and multiplication terms one of the | |
building blocks of the :class:`Ordinal` class. | |
In ``OmegaPower(a, b)``, ``a`` represents exponent and ``b`` represents multiplicity. | |
""" | |
def __new__(cls, a, b): | |
if isinstance(b, int): | |
b = Integer(b) | |
if not isinstance(b, Integer) or b <= 0: | |
raise TypeError("multiplicity must be a positive integer") | |
if not isinstance(a, Ordinal): | |
a = Ordinal.convert(a) | |
return Basic.__new__(cls, a, b) | |
def exp(self): | |
return self.args[0] | |
def mult(self): | |
return self.args[1] | |
def _compare_term(self, other, op): | |
if self.exp == other.exp: | |
return op(self.mult, other.mult) | |
else: | |
return op(self.exp, other.exp) | |
def __eq__(self, other): | |
if not isinstance(other, OmegaPower): | |
try: | |
other = OmegaPower(0, other) | |
except TypeError: | |
return NotImplemented | |
return self.args == other.args | |
def __hash__(self): | |
return Basic.__hash__(self) | |
def __lt__(self, other): | |
if not isinstance(other, OmegaPower): | |
try: | |
other = OmegaPower(0, other) | |
except TypeError: | |
return NotImplemented | |
return self._compare_term(other, operator.lt) | |
class Ordinal(Basic): | |
""" | |
Represents ordinals in Cantor normal form. | |
Internally, this class is just a list of instances of OmegaPower. | |
Examples | |
======== | |
>>> from sympy import Ordinal, OmegaPower | |
>>> from sympy.sets.ordinals import omega | |
>>> w = omega | |
>>> w.is_limit_ordinal | |
True | |
>>> Ordinal(OmegaPower(w + 1, 1), OmegaPower(3, 2)) | |
w**(w + 1) + w**3*2 | |
>>> 3 + w | |
w | |
>>> (w + 1) * w | |
w**2 | |
References | |
========== | |
.. [1] https://en.wikipedia.org/wiki/Ordinal_arithmetic | |
""" | |
def __new__(cls, *terms): | |
obj = super().__new__(cls, *terms) | |
powers = [i.exp for i in obj.args] | |
if not all(powers[i] >= powers[i+1] for i in range(len(powers) - 1)): | |
raise ValueError("powers must be in decreasing order") | |
return obj | |
def terms(self): | |
return self.args | |
def leading_term(self): | |
if self == ord0: | |
raise ValueError("ordinal zero has no leading term") | |
return self.terms[0] | |
def trailing_term(self): | |
if self == ord0: | |
raise ValueError("ordinal zero has no trailing term") | |
return self.terms[-1] | |
def is_successor_ordinal(self): | |
try: | |
return self.trailing_term.exp == ord0 | |
except ValueError: | |
return False | |
def is_limit_ordinal(self): | |
try: | |
return not self.trailing_term.exp == ord0 | |
except ValueError: | |
return False | |
def degree(self): | |
return self.leading_term.exp | |
def convert(cls, integer_value): | |
if integer_value == 0: | |
return ord0 | |
return Ordinal(OmegaPower(0, integer_value)) | |
def __eq__(self, other): | |
if not isinstance(other, Ordinal): | |
try: | |
other = Ordinal.convert(other) | |
except TypeError: | |
return NotImplemented | |
return self.terms == other.terms | |
def __hash__(self): | |
return hash(self.args) | |
def __lt__(self, other): | |
if not isinstance(other, Ordinal): | |
try: | |
other = Ordinal.convert(other) | |
except TypeError: | |
return NotImplemented | |
for term_self, term_other in zip(self.terms, other.terms): | |
if term_self != term_other: | |
return term_self < term_other | |
return len(self.terms) < len(other.terms) | |
def __le__(self, other): | |
return (self == other or self < other) | |
def __gt__(self, other): | |
return not self <= other | |
def __ge__(self, other): | |
return not self < other | |
def __str__(self): | |
net_str = "" | |
plus_count = 0 | |
if self == ord0: | |
return 'ord0' | |
for i in self.terms: | |
if plus_count: | |
net_str += " + " | |
if i.exp == ord0: | |
net_str += str(i.mult) | |
elif i.exp == 1: | |
net_str += 'w' | |
elif len(i.exp.terms) > 1 or i.exp.is_limit_ordinal: | |
net_str += 'w**(%s)'%i.exp | |
else: | |
net_str += 'w**%s'%i.exp | |
if not i.mult == 1 and not i.exp == ord0: | |
net_str += '*%s'%i.mult | |
plus_count += 1 | |
return(net_str) | |
__repr__ = __str__ | |
def __add__(self, other): | |
if not isinstance(other, Ordinal): | |
try: | |
other = Ordinal.convert(other) | |
except TypeError: | |
return NotImplemented | |
if other == ord0: | |
return self | |
a_terms = list(self.terms) | |
b_terms = list(other.terms) | |
r = len(a_terms) - 1 | |
b_exp = other.degree | |
while r >= 0 and a_terms[r].exp < b_exp: | |
r -= 1 | |
if r < 0: | |
terms = b_terms | |
elif a_terms[r].exp == b_exp: | |
sum_term = OmegaPower(b_exp, a_terms[r].mult + other.leading_term.mult) | |
terms = a_terms[:r] + [sum_term] + b_terms[1:] | |
else: | |
terms = a_terms[:r+1] + b_terms | |
return Ordinal(*terms) | |
def __radd__(self, other): | |
if not isinstance(other, Ordinal): | |
try: | |
other = Ordinal.convert(other) | |
except TypeError: | |
return NotImplemented | |
return other + self | |
def __mul__(self, other): | |
if not isinstance(other, Ordinal): | |
try: | |
other = Ordinal.convert(other) | |
except TypeError: | |
return NotImplemented | |
if ord0 in (self, other): | |
return ord0 | |
a_exp = self.degree | |
a_mult = self.leading_term.mult | |
summation = [] | |
if other.is_limit_ordinal: | |
for arg in other.terms: | |
summation.append(OmegaPower(a_exp + arg.exp, arg.mult)) | |
else: | |
for arg in other.terms[:-1]: | |
summation.append(OmegaPower(a_exp + arg.exp, arg.mult)) | |
b_mult = other.trailing_term.mult | |
summation.append(OmegaPower(a_exp, a_mult*b_mult)) | |
summation += list(self.terms[1:]) | |
return Ordinal(*summation) | |
def __rmul__(self, other): | |
if not isinstance(other, Ordinal): | |
try: | |
other = Ordinal.convert(other) | |
except TypeError: | |
return NotImplemented | |
return other * self | |
def __pow__(self, other): | |
if not self == omega: | |
return NotImplemented | |
return Ordinal(OmegaPower(other, 1)) | |
class OrdinalZero(Ordinal): | |
"""The ordinal zero. | |
OrdinalZero can be imported as ``ord0``. | |
""" | |
pass | |
class OrdinalOmega(Ordinal): | |
"""The ordinal omega which forms the base of all ordinals in cantor normal form. | |
OrdinalOmega can be imported as ``omega``. | |
Examples | |
======== | |
>>> from sympy.sets.ordinals import omega | |
>>> omega + omega | |
w*2 | |
""" | |
def __new__(cls): | |
return Ordinal.__new__(cls) | |
def terms(self): | |
return (OmegaPower(1, 1),) | |
ord0 = OrdinalZero() | |
omega = OrdinalOmega() | |