Coverage for /usr/lib/python3/dist-packages/sympy/sets/ordinals.py: 28%
191 statements
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-14 15:55 +0200
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-14 15:55 +0200
1from sympy.core import Basic, Integer
2import operator
5class OmegaPower(Basic):
6 """
7 Represents ordinal exponential and multiplication terms one of the
8 building blocks of the :class:`Ordinal` class.
9 In ``OmegaPower(a, b)``, ``a`` represents exponent and ``b`` represents multiplicity.
10 """
11 def __new__(cls, a, b):
12 if isinstance(b, int):
13 b = Integer(b)
14 if not isinstance(b, Integer) or b <= 0:
15 raise TypeError("multiplicity must be a positive integer")
17 if not isinstance(a, Ordinal):
18 a = Ordinal.convert(a)
20 return Basic.__new__(cls, a, b)
22 @property
23 def exp(self):
24 return self.args[0]
26 @property
27 def mult(self):
28 return self.args[1]
30 def _compare_term(self, other, op):
31 if self.exp == other.exp:
32 return op(self.mult, other.mult)
33 else:
34 return op(self.exp, other.exp)
36 def __eq__(self, other):
37 if not isinstance(other, OmegaPower):
38 try:
39 other = OmegaPower(0, other)
40 except TypeError:
41 return NotImplemented
42 return self.args == other.args
44 def __hash__(self):
45 return Basic.__hash__(self)
47 def __lt__(self, other):
48 if not isinstance(other, OmegaPower):
49 try:
50 other = OmegaPower(0, other)
51 except TypeError:
52 return NotImplemented
53 return self._compare_term(other, operator.lt)
56class Ordinal(Basic):
57 """
58 Represents ordinals in Cantor normal form.
60 Internally, this class is just a list of instances of OmegaPower.
62 Examples
63 ========
64 >>> from sympy import Ordinal, OmegaPower
65 >>> from sympy.sets.ordinals import omega
66 >>> w = omega
67 >>> w.is_limit_ordinal
68 True
69 >>> Ordinal(OmegaPower(w + 1, 1), OmegaPower(3, 2))
70 w**(w + 1) + w**3*2
71 >>> 3 + w
72 w
73 >>> (w + 1) * w
74 w**2
76 References
77 ==========
79 .. [1] https://en.wikipedia.org/wiki/Ordinal_arithmetic
80 """
81 def __new__(cls, *terms):
82 obj = super().__new__(cls, *terms)
83 powers = [i.exp for i in obj.args]
84 if not all(powers[i] >= powers[i+1] for i in range(len(powers) - 1)):
85 raise ValueError("powers must be in decreasing order")
86 return obj
88 @property
89 def terms(self):
90 return self.args
92 @property
93 def leading_term(self):
94 if self == ord0:
95 raise ValueError("ordinal zero has no leading term")
96 return self.terms[0]
98 @property
99 def trailing_term(self):
100 if self == ord0:
101 raise ValueError("ordinal zero has no trailing term")
102 return self.terms[-1]
104 @property
105 def is_successor_ordinal(self):
106 try:
107 return self.trailing_term.exp == ord0
108 except ValueError:
109 return False
111 @property
112 def is_limit_ordinal(self):
113 try:
114 return not self.trailing_term.exp == ord0
115 except ValueError:
116 return False
118 @property
119 def degree(self):
120 return self.leading_term.exp
122 @classmethod
123 def convert(cls, integer_value):
124 if integer_value == 0:
125 return ord0
126 return Ordinal(OmegaPower(0, integer_value))
128 def __eq__(self, other):
129 if not isinstance(other, Ordinal):
130 try:
131 other = Ordinal.convert(other)
132 except TypeError:
133 return NotImplemented
134 return self.terms == other.terms
136 def __hash__(self):
137 return hash(self.args)
139 def __lt__(self, other):
140 if not isinstance(other, Ordinal):
141 try:
142 other = Ordinal.convert(other)
143 except TypeError:
144 return NotImplemented
145 for term_self, term_other in zip(self.terms, other.terms):
146 if term_self != term_other:
147 return term_self < term_other
148 return len(self.terms) < len(other.terms)
150 def __le__(self, other):
151 return (self == other or self < other)
153 def __gt__(self, other):
154 return not self <= other
156 def __ge__(self, other):
157 return not self < other
159 def __str__(self):
160 net_str = ""
161 plus_count = 0
162 if self == ord0:
163 return 'ord0'
164 for i in self.terms:
165 if plus_count:
166 net_str += " + "
168 if i.exp == ord0:
169 net_str += str(i.mult)
170 elif i.exp == 1:
171 net_str += 'w'
172 elif len(i.exp.terms) > 1 or i.exp.is_limit_ordinal:
173 net_str += 'w**(%s)'%i.exp
174 else:
175 net_str += 'w**%s'%i.exp
177 if not i.mult == 1 and not i.exp == ord0:
178 net_str += '*%s'%i.mult
180 plus_count += 1
181 return(net_str)
183 __repr__ = __str__
185 def __add__(self, other):
186 if not isinstance(other, Ordinal):
187 try:
188 other = Ordinal.convert(other)
189 except TypeError:
190 return NotImplemented
191 if other == ord0:
192 return self
193 a_terms = list(self.terms)
194 b_terms = list(other.terms)
195 r = len(a_terms) - 1
196 b_exp = other.degree
197 while r >= 0 and a_terms[r].exp < b_exp:
198 r -= 1
199 if r < 0:
200 terms = b_terms
201 elif a_terms[r].exp == b_exp:
202 sum_term = OmegaPower(b_exp, a_terms[r].mult + other.leading_term.mult)
203 terms = a_terms[:r] + [sum_term] + b_terms[1:]
204 else:
205 terms = a_terms[:r+1] + b_terms
206 return Ordinal(*terms)
208 def __radd__(self, other):
209 if not isinstance(other, Ordinal):
210 try:
211 other = Ordinal.convert(other)
212 except TypeError:
213 return NotImplemented
214 return other + self
216 def __mul__(self, other):
217 if not isinstance(other, Ordinal):
218 try:
219 other = Ordinal.convert(other)
220 except TypeError:
221 return NotImplemented
222 if ord0 in (self, other):
223 return ord0
224 a_exp = self.degree
225 a_mult = self.leading_term.mult
226 summation = []
227 if other.is_limit_ordinal:
228 for arg in other.terms:
229 summation.append(OmegaPower(a_exp + arg.exp, arg.mult))
231 else:
232 for arg in other.terms[:-1]:
233 summation.append(OmegaPower(a_exp + arg.exp, arg.mult))
234 b_mult = other.trailing_term.mult
235 summation.append(OmegaPower(a_exp, a_mult*b_mult))
236 summation += list(self.terms[1:])
237 return Ordinal(*summation)
239 def __rmul__(self, other):
240 if not isinstance(other, Ordinal):
241 try:
242 other = Ordinal.convert(other)
243 except TypeError:
244 return NotImplemented
245 return other * self
247 def __pow__(self, other):
248 if not self == omega:
249 return NotImplemented
250 return Ordinal(OmegaPower(other, 1))
253class OrdinalZero(Ordinal):
254 """The ordinal zero.
256 OrdinalZero can be imported as ``ord0``.
257 """
258 pass
261class OrdinalOmega(Ordinal):
262 """The ordinal omega which forms the base of all ordinals in cantor normal form.
264 OrdinalOmega can be imported as ``omega``.
266 Examples
267 ========
269 >>> from sympy.sets.ordinals import omega
270 >>> omega + omega
271 w*2
272 """
273 def __new__(cls):
274 return Ordinal.__new__(cls)
276 @property
277 def terms(self):
278 return (OmegaPower(1, 1),)
281ord0 = OrdinalZero()
282omega = OrdinalOmega()