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()