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

1from sympy.core import Basic, Integer 

2import operator 

3 

4 

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

16 

17 if not isinstance(a, Ordinal): 

18 a = Ordinal.convert(a) 

19 

20 return Basic.__new__(cls, a, b) 

21 

22 @property 

23 def exp(self): 

24 return self.args[0] 

25 

26 @property 

27 def mult(self): 

28 return self.args[1] 

29 

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) 

35 

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 

43 

44 def __hash__(self): 

45 return Basic.__hash__(self) 

46 

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) 

54 

55 

56class Ordinal(Basic): 

57 """ 

58 Represents ordinals in Cantor normal form. 

59 

60 Internally, this class is just a list of instances of OmegaPower. 

61 

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 

75 

76 References 

77 ========== 

78 

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 

87 

88 @property 

89 def terms(self): 

90 return self.args 

91 

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] 

97 

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] 

103 

104 @property 

105 def is_successor_ordinal(self): 

106 try: 

107 return self.trailing_term.exp == ord0 

108 except ValueError: 

109 return False 

110 

111 @property 

112 def is_limit_ordinal(self): 

113 try: 

114 return not self.trailing_term.exp == ord0 

115 except ValueError: 

116 return False 

117 

118 @property 

119 def degree(self): 

120 return self.leading_term.exp 

121 

122 @classmethod 

123 def convert(cls, integer_value): 

124 if integer_value == 0: 

125 return ord0 

126 return Ordinal(OmegaPower(0, integer_value)) 

127 

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 

135 

136 def __hash__(self): 

137 return hash(self.args) 

138 

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) 

149 

150 def __le__(self, other): 

151 return (self == other or self < other) 

152 

153 def __gt__(self, other): 

154 return not self <= other 

155 

156 def __ge__(self, other): 

157 return not self < other 

158 

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 += " + " 

167 

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 

176 

177 if not i.mult == 1 and not i.exp == ord0: 

178 net_str += '*%s'%i.mult 

179 

180 plus_count += 1 

181 return(net_str) 

182 

183 __repr__ = __str__ 

184 

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) 

207 

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 

215 

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

230 

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) 

238 

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 

246 

247 def __pow__(self, other): 

248 if not self == omega: 

249 return NotImplemented 

250 return Ordinal(OmegaPower(other, 1)) 

251 

252 

253class OrdinalZero(Ordinal): 

254 """The ordinal zero. 

255 

256 OrdinalZero can be imported as ``ord0``. 

257 """ 

258 pass 

259 

260 

261class OrdinalOmega(Ordinal): 

262 """The ordinal omega which forms the base of all ordinals in cantor normal form. 

263 

264 OrdinalOmega can be imported as ``omega``. 

265 

266 Examples 

267 ======== 

268 

269 >>> from sympy.sets.ordinals import omega 

270 >>> omega + omega 

271 w*2 

272 """ 

273 def __new__(cls): 

274 return Ordinal.__new__(cls) 

275 

276 @property 

277 def terms(self): 

278 return (OmegaPower(1, 1),) 

279 

280 

281ord0 = OrdinalZero() 

282omega = OrdinalOmega()