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
« 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
14class AccumulationBounds(Expr):
15 r"""An accumulation bounds.
17 # Note AccumulationBounds has an alias: AccumBounds
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.
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.
25 Let `a` and `b` be reals such that `a \le b`.
27 `\left\langle a, b\right\rangle = \{x \in \mathbb{R} \mid a \le x \le b\}`
29 `\left\langle -\infty, b\right\rangle = \{x \in \mathbb{R} \mid x \le b\} \cup \{-\infty, \infty\}`
31 `\left\langle a, \infty \right\rangle = \{x \in \mathbb{R} \mid a \le x\} \cup \{-\infty, \infty\}`
33 `\left\langle -\infty, \infty \right\rangle = \mathbb{R} \cup \{-\infty, \infty\}`
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.
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)``)
48 A AccumulationBounds object is defined to be real AccumulationBounds,
49 if its end points are finite reals.
51 Let `X`, `Y` be real AccumulationBounds, then their sum, difference,
52 product are defined to be the following sets:
54 `X + Y = \{ x+y \mid x \in X \cap y \in Y\}`
56 `X - Y = \{ x-y \mid x \in X \cap y \in Y\}`
58 `X \times Y = \{ x \times y \mid x \in X \cap y \in Y\}`
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.
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``.
76 Examples
77 ========
79 >>> from sympy import AccumBounds, sin, exp, log, pi, E, S, oo
80 >>> from sympy.abc import x
82 >>> AccumBounds(0, 1) + AccumBounds(1, 2)
83 AccumBounds(1, 3)
85 >>> AccumBounds(0, 1) - AccumBounds(0, 2)
86 AccumBounds(-2, 1)
88 >>> AccumBounds(-2, 3)*AccumBounds(-1, 1)
89 AccumBounds(-3, 3)
91 >>> AccumBounds(1, 2)*AccumBounds(3, 5)
92 AccumBounds(3, 10)
94 The exponentiation of AccumulationBounds is defined
95 as follows:
97 If 0 does not belong to `X` or `n > 0` then
99 `X^n = \{ x^n \mid x \in X\}`
101 >>> AccumBounds(1, 4)**(S(1)/2)
102 AccumBounds(1, 2)
104 otherwise, an infinite or semi-infinite result is obtained:
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)
113 A boundary of 1 will always generate all nonnegatives:
115 >>> AccumBounds(1, 2)**oo
116 AccumBounds(0, oo)
117 >>> AccumBounds(0, 1)**oo
118 AccumBounds(0, oo)
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:
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)
129 >>> AccumBounds(-2, -1)**(S(1)/2)
130 sqrt(AccumBounds(-2, -1))
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`
134 >>> AccumBounds(-1, 1)**2
135 AccumBounds(0, 1)
137 >>> AccumBounds(1, 3) < 4
138 True
140 >>> AccumBounds(1, 3) < -1
141 False
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 \}`
147 >>> sin(AccumBounds(pi/6, pi/3))
148 AccumBounds(1/2, sqrt(3)/2)
150 >>> exp(AccumBounds(0, 1))
151 AccumBounds(1, E)
153 >>> log(AccumBounds(1, E))
154 AccumBounds(0, 1)
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.
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:
164 >>> (x**2 + 2*x + 1).subs(x, AccumBounds(-1, 1))
165 AccumBounds(-1, 4)
167 >>> ((x + 1)**2).subs(x, AccumBounds(-1, 1))
168 AccumBounds(0, 4)
170 References
171 ==========
173 .. [1] https://en.wikipedia.org/wiki/Interval_arithmetic
175 .. [2] https://fab.cba.mit.edu/classes/S62.12/docs/Hickey_interval.pdf
177 Notes
178 =====
180 Do not use ``AccumulationBounds`` for floating point interval arithmetic
181 calculations, use ``mpmath.iv`` instead.
182 """
184 is_extended_real = True
185 is_number = False
187 def __new__(cls, min, max):
189 min = _sympify(min)
190 max = _sympify(max)
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")
196 if max == min:
197 return max
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")
208 return Basic.__new__(cls, min, max)
210 # setting the operation priority
211 _op_priority = 11.0
213 def _eval_is_real(self):
214 if self.min.is_real and self.max.is_real:
215 return True
217 @property
218 def min(self):
219 """
220 Returns the minimum possible value attained by AccumulationBounds
221 object.
223 Examples
224 ========
226 >>> from sympy import AccumBounds
227 >>> AccumBounds(1, 3).min
228 1
230 """
231 return self.args[0]
233 @property
234 def max(self):
235 """
236 Returns the maximum possible value attained by AccumulationBounds
237 object.
239 Examples
240 ========
242 >>> from sympy import AccumBounds
243 >>> AccumBounds(1, 3).max
244 3
246 """
247 return self.args[1]
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.
256 Examples
257 ========
259 >>> from sympy import AccumBounds
260 >>> AccumBounds(1, 3).delta
261 2
263 """
264 return self.max - self.min
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.
273 Examples
274 ========
276 >>> from sympy import AccumBounds
277 >>> AccumBounds(1, 3).mid
278 2
280 """
281 return (self.min + self.max) / 2
283 @_sympifyit('other', NotImplemented)
284 def _eval_power(self, other):
285 return self.__pow__(other)
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
309 __radd__ = __add__
311 def __neg__(self):
312 return AccumBounds(-self.max, -self.min)
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
338 @_sympifyit('other', NotImplemented)
339 def __rsub__(self, other):
340 return self.__neg__() + other
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
386 __rmul__ = __mul__
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)
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)
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)
413 if other.min.is_zero and other.max.is_extended_positive:
414 return AccumBounds(-oo, self.max / other.max)
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)
426 if other.min.is_zero and other.max.is_extended_positive:
427 return AccumBounds(self.min / other.max, oo)
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)
446 return NotImplemented
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
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)
496 if other is S.NegativeInfinity:
497 return (1/self)**oo
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)
508 if other.is_zero:
509 return S.One # x**0 = 1
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))
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)
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])
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
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
564 return Pow(self, other, evaluate=False)
566 return NotImplemented
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
585 return Pow(other, self, evaluate=False)
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
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.
602 Examples
603 ========
605 >>> from sympy import AccumBounds, oo
606 >>> 1 in AccumBounds(-1, 3)
607 True
609 -oo and oo go together as limits (in AccumulationBounds).
611 >>> -oo in AccumBounds(1, oo)
612 True
614 >>> oo in AccumBounds(-oo, 0)
615 True
617 """
618 other = _sympify(other)
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
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
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.
635 Parameters
636 ==========
638 other : AccumulationBounds
639 Another AccumulationBounds object with which the intersection
640 has to be computed.
642 Returns
643 =======
645 AccumulationBounds
646 Intersection of ``self`` and ``other``.
648 Examples
649 ========
651 >>> from sympy import AccumBounds, FiniteSet
652 >>> AccumBounds(1, 3).intersection(AccumBounds(2, 4))
653 AccumBounds(2, 3)
655 >>> AccumBounds(1, 3).intersection(AccumBounds(4, 6))
656 EmptySet
658 >>> AccumBounds(1, 4).intersection(FiniteSet(1, 2, 5))
659 {1, 2}
661 """
662 if not isinstance(other, (AccumBounds, FiniteSet)):
663 raise TypeError(
664 "Input must be AccumulationBounds or FiniteSet object")
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
673 if self.max < other.min or self.min > other.max:
674 return S.EmptySet
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
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
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")
696 if self.min <= other.min and self.max >= other.min:
697 return AccumBounds(self.min, Max(self.max, other.max))
699 if other.min <= self.min and other.max >= self.min:
700 return AccumBounds(other.min, Max(self.max, other.max))
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
711@dispatch(AccumulationBounds, Basic) # type: ignore # noqa:F811
712def _eval_is_le(lhs, rhs): # noqa: F811
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`.
721 Examples
722 ========
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
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
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
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`.
761 Examples
762 ========
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 """
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
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
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
804# setting an alias for AccumulationBounds
805AccumBounds = AccumulationBounds