Coverage for /usr/lib/python3/dist-packages/sympy/calculus/accumulationbounds.py: 17%

364 statements  

« prev     ^ index     » next       coverage.py v7.9.1, created at 2025-06-14 15:55 +0200

1from sympy.core import Add, Mul, Pow, S 

2from sympy.core.basic import Basic 

3from sympy.core.expr import Expr 

4from sympy.core.numbers import _sympifyit, oo, zoo 

5from sympy.core.relational import is_le, is_lt, is_ge, is_gt 

6from sympy.core.sympify import _sympify 

7from sympy.functions.elementary.miscellaneous import Min, Max 

8from sympy.logic.boolalg import And 

9from sympy.multipledispatch import dispatch 

10from sympy.series.order import Order 

11from sympy.sets.sets import FiniteSet 

12 

13 

14class AccumulationBounds(Expr): 

15 r"""An accumulation bounds. 

16 

17 # Note AccumulationBounds has an alias: AccumBounds 

18 

19 AccumulationBounds represent an interval `[a, b]`, which is always closed 

20 at the ends. Here `a` and `b` can be any value from extended real numbers. 

21 

22 The intended meaning of AccummulationBounds is to give an approximate 

23 location of the accumulation points of a real function at a limit point. 

24 

25 Let `a` and `b` be reals such that `a \le b`. 

26 

27 `\left\langle a, b\right\rangle = \{x \in \mathbb{R} \mid a \le x \le b\}` 

28 

29 `\left\langle -\infty, b\right\rangle = \{x \in \mathbb{R} \mid x \le b\} \cup \{-\infty, \infty\}` 

30 

31 `\left\langle a, \infty \right\rangle = \{x \in \mathbb{R} \mid a \le x\} \cup \{-\infty, \infty\}` 

32 

33 `\left\langle -\infty, \infty \right\rangle = \mathbb{R} \cup \{-\infty, \infty\}` 

34 

35 ``oo`` and ``-oo`` are added to the second and third definition respectively, 

36 since if either ``-oo`` or ``oo`` is an argument, then the other one should 

37 be included (though not as an end point). This is forced, since we have, 

38 for example, ``1/AccumBounds(0, 1) = AccumBounds(1, oo)``, and the limit at 

39 `0` is not one-sided. As `x` tends to `0-`, then `1/x \rightarrow -\infty`, so `-\infty` 

40 should be interpreted as belonging to ``AccumBounds(1, oo)`` though it need 

41 not appear explicitly. 

42 

43 In many cases it suffices to know that the limit set is bounded. 

44 However, in some other cases more exact information could be useful. 

45 For example, all accumulation values of `\cos(x) + 1` are non-negative. 

46 (``AccumBounds(-1, 1) + 1 = AccumBounds(0, 2)``) 

47 

48 A AccumulationBounds object is defined to be real AccumulationBounds, 

49 if its end points are finite reals. 

50 

51 Let `X`, `Y` be real AccumulationBounds, then their sum, difference, 

52 product are defined to be the following sets: 

53 

54 `X + Y = \{ x+y \mid x \in X \cap y \in Y\}` 

55 

56 `X - Y = \{ x-y \mid x \in X \cap y \in Y\}` 

57 

58 `X \times Y = \{ x \times y \mid x \in X \cap y \in Y\}` 

59 

60 When an AccumBounds is raised to a negative power, if 0 is contained 

61 between the bounds then an infinite range is returned, otherwise if an 

62 endpoint is 0 then a semi-infinite range with consistent sign will be returned. 

63 

64 AccumBounds in expressions behave a lot like Intervals but the 

65 semantics are not necessarily the same. Division (or exponentiation 

66 to a negative integer power) could be handled with *intervals* by 

67 returning a union of the results obtained after splitting the 

68 bounds between negatives and positives, but that is not done with 

69 AccumBounds. In addition, bounds are assumed to be independent of 

70 each other; if the same bound is used in more than one place in an 

71 expression, the result may not be the supremum or infimum of the 

72 expression (see below). Finally, when a boundary is ``1``, 

73 exponentiation to the power of ``oo`` yields ``oo``, neither 

74 ``1`` nor ``nan``. 

75 

76 Examples 

77 ======== 

78 

79 >>> from sympy import AccumBounds, sin, exp, log, pi, E, S, oo 

80 >>> from sympy.abc import x 

81 

82 >>> AccumBounds(0, 1) + AccumBounds(1, 2) 

83 AccumBounds(1, 3) 

84 

85 >>> AccumBounds(0, 1) - AccumBounds(0, 2) 

86 AccumBounds(-2, 1) 

87 

88 >>> AccumBounds(-2, 3)*AccumBounds(-1, 1) 

89 AccumBounds(-3, 3) 

90 

91 >>> AccumBounds(1, 2)*AccumBounds(3, 5) 

92 AccumBounds(3, 10) 

93 

94 The exponentiation of AccumulationBounds is defined 

95 as follows: 

96 

97 If 0 does not belong to `X` or `n > 0` then 

98 

99 `X^n = \{ x^n \mid x \in X\}` 

100 

101 >>> AccumBounds(1, 4)**(S(1)/2) 

102 AccumBounds(1, 2) 

103 

104 otherwise, an infinite or semi-infinite result is obtained: 

105 

106 >>> 1/AccumBounds(-1, 1) 

107 AccumBounds(-oo, oo) 

108 >>> 1/AccumBounds(0, 2) 

109 AccumBounds(1/2, oo) 

110 >>> 1/AccumBounds(-oo, 0) 

111 AccumBounds(-oo, 0) 

112 

113 A boundary of 1 will always generate all nonnegatives: 

114 

115 >>> AccumBounds(1, 2)**oo 

116 AccumBounds(0, oo) 

117 >>> AccumBounds(0, 1)**oo 

118 AccumBounds(0, oo) 

119 

120 If the exponent is itself an AccumulationBounds or is not an 

121 integer then unevaluated results will be returned unless the base 

122 values are positive: 

123 

124 >>> AccumBounds(2, 3)**AccumBounds(-1, 2) 

125 AccumBounds(1/3, 9) 

126 >>> AccumBounds(-2, 3)**AccumBounds(-1, 2) 

127 AccumBounds(-2, 3)**AccumBounds(-1, 2) 

128 

129 >>> AccumBounds(-2, -1)**(S(1)/2) 

130 sqrt(AccumBounds(-2, -1)) 

131 

132 Note: `\left\langle a, b\right\rangle^2` is not same as `\left\langle a, b\right\rangle \times \left\langle a, b\right\rangle` 

133 

134 >>> AccumBounds(-1, 1)**2 

135 AccumBounds(0, 1) 

136 

137 >>> AccumBounds(1, 3) < 4 

138 True 

139 

140 >>> AccumBounds(1, 3) < -1 

141 False 

142 

143 Some elementary functions can also take AccumulationBounds as input. 

144 A function `f` evaluated for some real AccumulationBounds `\left\langle a, b \right\rangle` 

145 is defined as `f(\left\langle a, b\right\rangle) = \{ f(x) \mid a \le x \le b \}` 

146 

147 >>> sin(AccumBounds(pi/6, pi/3)) 

148 AccumBounds(1/2, sqrt(3)/2) 

149 

150 >>> exp(AccumBounds(0, 1)) 

151 AccumBounds(1, E) 

152 

153 >>> log(AccumBounds(1, E)) 

154 AccumBounds(0, 1) 

155 

156 Some symbol in an expression can be substituted for a AccumulationBounds 

157 object. But it does not necessarily evaluate the AccumulationBounds for 

158 that expression. 

159 

160 The same expression can be evaluated to different values depending upon 

161 the form it is used for substitution since each instance of an 

162 AccumulationBounds is considered independent. For example: 

163 

164 >>> (x**2 + 2*x + 1).subs(x, AccumBounds(-1, 1)) 

165 AccumBounds(-1, 4) 

166 

167 >>> ((x + 1)**2).subs(x, AccumBounds(-1, 1)) 

168 AccumBounds(0, 4) 

169 

170 References 

171 ========== 

172 

173 .. [1] https://en.wikipedia.org/wiki/Interval_arithmetic 

174 

175 .. [2] https://fab.cba.mit.edu/classes/S62.12/docs/Hickey_interval.pdf 

176 

177 Notes 

178 ===== 

179 

180 Do not use ``AccumulationBounds`` for floating point interval arithmetic 

181 calculations, use ``mpmath.iv`` instead. 

182 """ 

183 

184 is_extended_real = True 

185 is_number = False 

186 

187 def __new__(cls, min, max): 

188 

189 min = _sympify(min) 

190 max = _sympify(max) 

191 

192 # Only allow real intervals (use symbols with 'is_extended_real=True'). 

193 if not min.is_extended_real or not max.is_extended_real: 

194 raise ValueError("Only real AccumulationBounds are supported") 

195 

196 if max == min: 

197 return max 

198 

199 # Make sure that the created AccumBounds object will be valid. 

200 if max.is_number and min.is_number: 

201 bad = max.is_comparable and min.is_comparable and max < min 

202 else: 

203 bad = (max - min).is_extended_negative 

204 if bad: 

205 raise ValueError( 

206 "Lower limit should be smaller than upper limit") 

207 

208 return Basic.__new__(cls, min, max) 

209 

210 # setting the operation priority 

211 _op_priority = 11.0 

212 

213 def _eval_is_real(self): 

214 if self.min.is_real and self.max.is_real: 

215 return True 

216 

217 @property 

218 def min(self): 

219 """ 

220 Returns the minimum possible value attained by AccumulationBounds 

221 object. 

222 

223 Examples 

224 ======== 

225 

226 >>> from sympy import AccumBounds 

227 >>> AccumBounds(1, 3).min 

228 1 

229 

230 """ 

231 return self.args[0] 

232 

233 @property 

234 def max(self): 

235 """ 

236 Returns the maximum possible value attained by AccumulationBounds 

237 object. 

238 

239 Examples 

240 ======== 

241 

242 >>> from sympy import AccumBounds 

243 >>> AccumBounds(1, 3).max 

244 3 

245 

246 """ 

247 return self.args[1] 

248 

249 @property 

250 def delta(self): 

251 """ 

252 Returns the difference of maximum possible value attained by 

253 AccumulationBounds object and minimum possible value attained 

254 by AccumulationBounds object. 

255 

256 Examples 

257 ======== 

258 

259 >>> from sympy import AccumBounds 

260 >>> AccumBounds(1, 3).delta 

261 2 

262 

263 """ 

264 return self.max - self.min 

265 

266 @property 

267 def mid(self): 

268 """ 

269 Returns the mean of maximum possible value attained by 

270 AccumulationBounds object and minimum possible value 

271 attained by AccumulationBounds object. 

272 

273 Examples 

274 ======== 

275 

276 >>> from sympy import AccumBounds 

277 >>> AccumBounds(1, 3).mid 

278 2 

279 

280 """ 

281 return (self.min + self.max) / 2 

282 

283 @_sympifyit('other', NotImplemented) 

284 def _eval_power(self, other): 

285 return self.__pow__(other) 

286 

287 @_sympifyit('other', NotImplemented) 

288 def __add__(self, other): 

289 if isinstance(other, Expr): 

290 if isinstance(other, AccumBounds): 

291 return AccumBounds( 

292 Add(self.min, other.min), 

293 Add(self.max, other.max)) 

294 if other is S.Infinity and self.min is S.NegativeInfinity or \ 

295 other is S.NegativeInfinity and self.max is S.Infinity: 

296 return AccumBounds(-oo, oo) 

297 elif other.is_extended_real: 

298 if self.min is S.NegativeInfinity and self.max is S.Infinity: 

299 return AccumBounds(-oo, oo) 

300 elif self.min is S.NegativeInfinity: 

301 return AccumBounds(-oo, self.max + other) 

302 elif self.max is S.Infinity: 

303 return AccumBounds(self.min + other, oo) 

304 else: 

305 return AccumBounds(Add(self.min, other), Add(self.max, other)) 

306 return Add(self, other, evaluate=False) 

307 return NotImplemented 

308 

309 __radd__ = __add__ 

310 

311 def __neg__(self): 

312 return AccumBounds(-self.max, -self.min) 

313 

314 @_sympifyit('other', NotImplemented) 

315 def __sub__(self, other): 

316 if isinstance(other, Expr): 

317 if isinstance(other, AccumBounds): 

318 return AccumBounds( 

319 Add(self.min, -other.max), 

320 Add(self.max, -other.min)) 

321 if other is S.NegativeInfinity and self.min is S.NegativeInfinity or \ 

322 other is S.Infinity and self.max is S.Infinity: 

323 return AccumBounds(-oo, oo) 

324 elif other.is_extended_real: 

325 if self.min is S.NegativeInfinity and self.max is S.Infinity: 

326 return AccumBounds(-oo, oo) 

327 elif self.min is S.NegativeInfinity: 

328 return AccumBounds(-oo, self.max - other) 

329 elif self.max is S.Infinity: 

330 return AccumBounds(self.min - other, oo) 

331 else: 

332 return AccumBounds( 

333 Add(self.min, -other), 

334 Add(self.max, -other)) 

335 return Add(self, -other, evaluate=False) 

336 return NotImplemented 

337 

338 @_sympifyit('other', NotImplemented) 

339 def __rsub__(self, other): 

340 return self.__neg__() + other 

341 

342 @_sympifyit('other', NotImplemented) 

343 def __mul__(self, other): 

344 if self.args == (-oo, oo): 

345 return self 

346 if isinstance(other, Expr): 

347 if isinstance(other, AccumBounds): 

348 if other.args == (-oo, oo): 

349 return other 

350 v = set() 

351 for a in self.args: 

352 vi = other*a 

353 for i in vi.args or (vi,): 

354 v.add(i) 

355 return AccumBounds(Min(*v), Max(*v)) 

356 if other is S.Infinity: 

357 if self.min.is_zero: 

358 return AccumBounds(0, oo) 

359 if self.max.is_zero: 

360 return AccumBounds(-oo, 0) 

361 if other is S.NegativeInfinity: 

362 if self.min.is_zero: 

363 return AccumBounds(-oo, 0) 

364 if self.max.is_zero: 

365 return AccumBounds(0, oo) 

366 if other.is_extended_real: 

367 if other.is_zero: 

368 if self.max is S.Infinity: 

369 return AccumBounds(0, oo) 

370 if self.min is S.NegativeInfinity: 

371 return AccumBounds(-oo, 0) 

372 return S.Zero 

373 if other.is_extended_positive: 

374 return AccumBounds( 

375 Mul(self.min, other), 

376 Mul(self.max, other)) 

377 elif other.is_extended_negative: 

378 return AccumBounds( 

379 Mul(self.max, other), 

380 Mul(self.min, other)) 

381 if isinstance(other, Order): 

382 return other 

383 return Mul(self, other, evaluate=False) 

384 return NotImplemented 

385 

386 __rmul__ = __mul__ 

387 

388 @_sympifyit('other', NotImplemented) 

389 def __truediv__(self, other): 

390 if isinstance(other, Expr): 

391 if isinstance(other, AccumBounds): 

392 if other.min.is_positive or other.max.is_negative: 

393 return self * AccumBounds(1/other.max, 1/other.min) 

394 

395 if (self.min.is_extended_nonpositive and self.max.is_extended_nonnegative and 

396 other.min.is_extended_nonpositive and other.max.is_extended_nonnegative): 

397 if self.min.is_zero and other.min.is_zero: 

398 return AccumBounds(0, oo) 

399 if self.max.is_zero and other.min.is_zero: 

400 return AccumBounds(-oo, 0) 

401 return AccumBounds(-oo, oo) 

402 

403 if self.max.is_extended_negative: 

404 if other.min.is_extended_negative: 

405 if other.max.is_zero: 

406 return AccumBounds(self.max / other.min, oo) 

407 if other.max.is_extended_positive: 

408 # if we were dealing with intervals we would return 

409 # Union(Interval(-oo, self.max/other.max), 

410 # Interval(self.max/other.min, oo)) 

411 return AccumBounds(-oo, oo) 

412 

413 if other.min.is_zero and other.max.is_extended_positive: 

414 return AccumBounds(-oo, self.max / other.max) 

415 

416 if self.min.is_extended_positive: 

417 if other.min.is_extended_negative: 

418 if other.max.is_zero: 

419 return AccumBounds(-oo, self.min / other.min) 

420 if other.max.is_extended_positive: 

421 # if we were dealing with intervals we would return 

422 # Union(Interval(-oo, self.min/other.min), 

423 # Interval(self.min/other.max, oo)) 

424 return AccumBounds(-oo, oo) 

425 

426 if other.min.is_zero and other.max.is_extended_positive: 

427 return AccumBounds(self.min / other.max, oo) 

428 

429 elif other.is_extended_real: 

430 if other in (S.Infinity, S.NegativeInfinity): 

431 if self == AccumBounds(-oo, oo): 

432 return AccumBounds(-oo, oo) 

433 if self.max is S.Infinity: 

434 return AccumBounds(Min(0, other), Max(0, other)) 

435 if self.min is S.NegativeInfinity: 

436 return AccumBounds(Min(0, -other), Max(0, -other)) 

437 if other.is_extended_positive: 

438 return AccumBounds(self.min / other, self.max / other) 

439 elif other.is_extended_negative: 

440 return AccumBounds(self.max / other, self.min / other) 

441 if (1 / other) is S.ComplexInfinity: 

442 return Mul(self, 1 / other, evaluate=False) 

443 else: 

444 return Mul(self, 1 / other) 

445 

446 return NotImplemented 

447 

448 @_sympifyit('other', NotImplemented) 

449 def __rtruediv__(self, other): 

450 if isinstance(other, Expr): 

451 if other.is_extended_real: 

452 if other.is_zero: 

453 return S.Zero 

454 if (self.min.is_extended_nonpositive and self.max.is_extended_nonnegative): 

455 if self.min.is_zero: 

456 if other.is_extended_positive: 

457 return AccumBounds(Mul(other, 1 / self.max), oo) 

458 if other.is_extended_negative: 

459 return AccumBounds(-oo, Mul(other, 1 / self.max)) 

460 if self.max.is_zero: 

461 if other.is_extended_positive: 

462 return AccumBounds(-oo, Mul(other, 1 / self.min)) 

463 if other.is_extended_negative: 

464 return AccumBounds(Mul(other, 1 / self.min), oo) 

465 return AccumBounds(-oo, oo) 

466 else: 

467 return AccumBounds(Min(other / self.min, other / self.max), 

468 Max(other / self.min, other / self.max)) 

469 return Mul(other, 1 / self, evaluate=False) 

470 else: 

471 return NotImplemented 

472 

473 @_sympifyit('other', NotImplemented) 

474 def __pow__(self, other): 

475 if isinstance(other, Expr): 

476 if other is S.Infinity: 

477 if self.min.is_extended_nonnegative: 

478 if self.max < 1: 

479 return S.Zero 

480 if self.min > 1: 

481 return S.Infinity 

482 return AccumBounds(0, oo) 

483 elif self.max.is_extended_negative: 

484 if self.min > -1: 

485 return S.Zero 

486 if self.max < -1: 

487 return zoo 

488 return S.NaN 

489 else: 

490 if self.min > -1: 

491 if self.max < 1: 

492 return S.Zero 

493 return AccumBounds(0, oo) 

494 return AccumBounds(-oo, oo) 

495 

496 if other is S.NegativeInfinity: 

497 return (1/self)**oo 

498 

499 # generically true 

500 if (self.max - self.min).is_nonnegative: 

501 # well defined 

502 if self.min.is_nonnegative: 

503 # no 0 to worry about 

504 if other.is_nonnegative: 

505 # no infinity to worry about 

506 return self.func(self.min**other, self.max**other) 

507 

508 if other.is_zero: 

509 return S.One # x**0 = 1 

510 

511 if other.is_Integer or other.is_integer: 

512 if self.min.is_extended_positive: 

513 return AccumBounds( 

514 Min(self.min**other, self.max**other), 

515 Max(self.min**other, self.max**other)) 

516 elif self.max.is_extended_negative: 

517 return AccumBounds( 

518 Min(self.max**other, self.min**other), 

519 Max(self.max**other, self.min**other)) 

520 

521 if other % 2 == 0: 

522 if other.is_extended_negative: 

523 if self.min.is_zero: 

524 return AccumBounds(self.max**other, oo) 

525 if self.max.is_zero: 

526 return AccumBounds(self.min**other, oo) 

527 return (1/self)**(-other) 

528 return AccumBounds( 

529 S.Zero, Max(self.min**other, self.max**other)) 

530 elif other % 2 == 1: 

531 if other.is_extended_negative: 

532 if self.min.is_zero: 

533 return AccumBounds(self.max**other, oo) 

534 if self.max.is_zero: 

535 return AccumBounds(-oo, self.min**other) 

536 return (1/self)**(-other) 

537 return AccumBounds(self.min**other, self.max**other) 

538 

539 # non-integer exponent 

540 # 0**neg or neg**frac yields complex 

541 if (other.is_number or other.is_rational) and ( 

542 self.min.is_extended_nonnegative or ( 

543 other.is_extended_nonnegative and 

544 self.min.is_extended_nonnegative)): 

545 num, den = other.as_numer_denom() 

546 if num is S.One: 

547 return AccumBounds(*[i**(1/den) for i in self.args]) 

548 

549 elif den is not S.One: # e.g. if other is not Float 

550 return (self**num)**(1/den) # ok for non-negative base 

551 

552 if isinstance(other, AccumBounds): 

553 if (self.min.is_extended_positive or 

554 self.min.is_extended_nonnegative and 

555 other.min.is_extended_nonnegative): 

556 p = [self**i for i in other.args] 

557 if not any(i.is_Pow for i in p): 

558 a = [j for i in p for j in i.args or (i,)] 

559 try: 

560 return self.func(min(a), max(a)) 

561 except TypeError: # can't sort 

562 pass 

563 

564 return Pow(self, other, evaluate=False) 

565 

566 return NotImplemented 

567 

568 @_sympifyit('other', NotImplemented) 

569 def __rpow__(self, other): 

570 if other.is_real and other.is_extended_nonnegative and ( 

571 self.max - self.min).is_extended_positive: 

572 if other is S.One: 

573 return S.One 

574 if other.is_extended_positive: 

575 a, b = [other**i for i in self.args] 

576 if min(a, b) != a: 

577 a, b = b, a 

578 return self.func(a, b) 

579 if other.is_zero: 

580 if self.min.is_zero: 

581 return self.func(0, 1) 

582 if self.min.is_extended_positive: 

583 return S.Zero 

584 

585 return Pow(other, self, evaluate=False) 

586 

587 def __abs__(self): 

588 if self.max.is_extended_negative: 

589 return self.__neg__() 

590 elif self.min.is_extended_negative: 

591 return AccumBounds(S.Zero, Max(abs(self.min), self.max)) 

592 else: 

593 return self 

594 

595 

596 def __contains__(self, other): 

597 """ 

598 Returns ``True`` if other is contained in self, where other 

599 belongs to extended real numbers, ``False`` if not contained, 

600 otherwise TypeError is raised. 

601 

602 Examples 

603 ======== 

604 

605 >>> from sympy import AccumBounds, oo 

606 >>> 1 in AccumBounds(-1, 3) 

607 True 

608 

609 -oo and oo go together as limits (in AccumulationBounds). 

610 

611 >>> -oo in AccumBounds(1, oo) 

612 True 

613 

614 >>> oo in AccumBounds(-oo, 0) 

615 True 

616 

617 """ 

618 other = _sympify(other) 

619 

620 if other in (S.Infinity, S.NegativeInfinity): 

621 if self.min is S.NegativeInfinity or self.max is S.Infinity: 

622 return True 

623 return False 

624 

625 rv = And(self.min <= other, self.max >= other) 

626 if rv not in (True, False): 

627 raise TypeError("input failed to evaluate") 

628 return rv 

629 

630 def intersection(self, other): 

631 """ 

632 Returns the intersection of 'self' and 'other'. 

633 Here other can be an instance of :py:class:`~.FiniteSet` or AccumulationBounds. 

634 

635 Parameters 

636 ========== 

637 

638 other : AccumulationBounds 

639 Another AccumulationBounds object with which the intersection 

640 has to be computed. 

641 

642 Returns 

643 ======= 

644 

645 AccumulationBounds 

646 Intersection of ``self`` and ``other``. 

647 

648 Examples 

649 ======== 

650 

651 >>> from sympy import AccumBounds, FiniteSet 

652 >>> AccumBounds(1, 3).intersection(AccumBounds(2, 4)) 

653 AccumBounds(2, 3) 

654 

655 >>> AccumBounds(1, 3).intersection(AccumBounds(4, 6)) 

656 EmptySet 

657 

658 >>> AccumBounds(1, 4).intersection(FiniteSet(1, 2, 5)) 

659 {1, 2} 

660 

661 """ 

662 if not isinstance(other, (AccumBounds, FiniteSet)): 

663 raise TypeError( 

664 "Input must be AccumulationBounds or FiniteSet object") 

665 

666 if isinstance(other, FiniteSet): 

667 fin_set = S.EmptySet 

668 for i in other: 

669 if i in self: 

670 fin_set = fin_set + FiniteSet(i) 

671 return fin_set 

672 

673 if self.max < other.min or self.min > other.max: 

674 return S.EmptySet 

675 

676 if self.min <= other.min: 

677 if self.max <= other.max: 

678 return AccumBounds(other.min, self.max) 

679 if self.max > other.max: 

680 return other 

681 

682 if other.min <= self.min: 

683 if other.max < self.max: 

684 return AccumBounds(self.min, other.max) 

685 if other.max > self.max: 

686 return self 

687 

688 def union(self, other): 

689 # TODO : Devise a better method for Union of AccumBounds 

690 # this method is not actually correct and 

691 # can be made better 

692 if not isinstance(other, AccumBounds): 

693 raise TypeError( 

694 "Input must be AccumulationBounds or FiniteSet object") 

695 

696 if self.min <= other.min and self.max >= other.min: 

697 return AccumBounds(self.min, Max(self.max, other.max)) 

698 

699 if other.min <= self.min and other.max >= self.min: 

700 return AccumBounds(other.min, Max(self.max, other.max)) 

701 

702 

703@dispatch(AccumulationBounds, AccumulationBounds) # type: ignore # noqa:F811 

704def _eval_is_le(lhs, rhs): # noqa:F811 

705 if is_le(lhs.max, rhs.min): 

706 return True 

707 if is_gt(lhs.min, rhs.max): 

708 return False 

709 

710 

711@dispatch(AccumulationBounds, Basic) # type: ignore # noqa:F811 

712def _eval_is_le(lhs, rhs): # noqa: F811 

713 

714 """ 

715 Returns ``True `` if range of values attained by ``lhs`` AccumulationBounds 

716 object is greater than the range of values attained by ``rhs``, 

717 where ``rhs`` may be any value of type AccumulationBounds object or 

718 extended real number value, ``False`` if ``rhs`` satisfies 

719 the same property, else an unevaluated :py:class:`~.Relational`. 

720 

721 Examples 

722 ======== 

723 

724 >>> from sympy import AccumBounds, oo 

725 >>> AccumBounds(1, 3) > AccumBounds(4, oo) 

726 False 

727 >>> AccumBounds(1, 4) > AccumBounds(3, 4) 

728 AccumBounds(1, 4) > AccumBounds(3, 4) 

729 >>> AccumBounds(1, oo) > -1 

730 True 

731 

732 """ 

733 if not rhs.is_extended_real: 

734 raise TypeError( 

735 "Invalid comparison of %s %s" % 

736 (type(rhs), rhs)) 

737 elif rhs.is_comparable: 

738 if is_le(lhs.max, rhs): 

739 return True 

740 if is_gt(lhs.min, rhs): 

741 return False 

742 

743 

744@dispatch(AccumulationBounds, AccumulationBounds) 

745def _eval_is_ge(lhs, rhs): # noqa:F811 

746 if is_ge(lhs.min, rhs.max): 

747 return True 

748 if is_lt(lhs.max, rhs.min): 

749 return False 

750 

751 

752@dispatch(AccumulationBounds, Expr) # type:ignore 

753def _eval_is_ge(lhs, rhs): # noqa: F811 

754 """ 

755 Returns ``True`` if range of values attained by ``lhs`` AccumulationBounds 

756 object is less that the range of values attained by ``rhs``, where 

757 other may be any value of type AccumulationBounds object or extended 

758 real number value, ``False`` if ``rhs`` satisfies the same 

759 property, else an unevaluated :py:class:`~.Relational`. 

760 

761 Examples 

762 ======== 

763 

764 >>> from sympy import AccumBounds, oo 

765 >>> AccumBounds(1, 3) >= AccumBounds(4, oo) 

766 False 

767 >>> AccumBounds(1, 4) >= AccumBounds(3, 4) 

768 AccumBounds(1, 4) >= AccumBounds(3, 4) 

769 >>> AccumBounds(1, oo) >= 1 

770 True 

771 """ 

772 

773 if not rhs.is_extended_real: 

774 raise TypeError( 

775 "Invalid comparison of %s %s" % 

776 (type(rhs), rhs)) 

777 elif rhs.is_comparable: 

778 if is_ge(lhs.min, rhs): 

779 return True 

780 if is_lt(lhs.max, rhs): 

781 return False 

782 

783 

784@dispatch(Expr, AccumulationBounds) # type:ignore 

785def _eval_is_ge(lhs, rhs): # noqa:F811 

786 if not lhs.is_extended_real: 

787 raise TypeError( 

788 "Invalid comparison of %s %s" % 

789 (type(lhs), lhs)) 

790 elif lhs.is_comparable: 

791 if is_le(rhs.max, lhs): 

792 return True 

793 if is_gt(rhs.min, lhs): 

794 return False 

795 

796 

797@dispatch(AccumulationBounds, AccumulationBounds) # type:ignore 

798def _eval_is_ge(lhs, rhs): # noqa:F811 

799 if is_ge(lhs.min, rhs.max): 

800 return True 

801 if is_lt(lhs.max, rhs.min): 

802 return False 

803 

804# setting an alias for AccumulationBounds 

805AccumBounds = AccumulationBounds