|
|
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)
@property def exp(self): return self.args[0]
@property 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
@property def terms(self): return self.args
@property def leading_term(self): if self == ord0: raise ValueError("ordinal zero has no leading term") return self.terms[0]
@property def trailing_term(self): if self == ord0: raise ValueError("ordinal zero has no trailing term") return self.terms[-1]
@property def is_successor_ordinal(self): try: return self.trailing_term.exp == ord0 except ValueError: return False
@property def is_limit_ordinal(self): try: return not self.trailing_term.exp == ord0 except ValueError: return False
@property def degree(self): return self.leading_term.exp
@classmethod 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)
@property def terms(self): return (OmegaPower(1, 1),)
ord0 = OrdinalZero() omega = OrdinalOmega()
|