accumulationbounds.py 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804
  1. from sympy.core import Add, Mul, Pow, S
  2. from sympy.core.basic import Basic
  3. from sympy.core.expr import Expr
  4. from sympy.core.numbers import _sympifyit, oo, zoo
  5. from sympy.core.relational import is_le, is_lt, is_ge, is_gt
  6. from sympy.core.sympify import _sympify
  7. from sympy.functions.elementary.miscellaneous import Min, Max
  8. from sympy.logic.boolalg import And
  9. from sympy.multipledispatch import dispatch
  10. from sympy.series.order import Order
  11. from sympy.sets.sets import FiniteSet
  12. class AccumulationBounds(Expr):
  13. r"""
  14. # Note AccumulationBounds has an alias: AccumBounds
  15. AccumulationBounds represent an interval `[a, b]`, which is always closed
  16. at the ends. Here `a` and `b` can be any value from extended real numbers.
  17. The intended meaning of AccummulationBounds is to give an approximate
  18. location of the accumulation points of a real function at a limit point.
  19. Let `a` and `b` be reals such that `a \le b`.
  20. `\left\langle a, b\right\rangle = \{x \in \mathbb{R} \mid a \le x \le b\}`
  21. `\left\langle -\infty, b\right\rangle = \{x \in \mathbb{R} \mid x \le b\} \cup \{-\infty, \infty\}`
  22. `\left\langle a, \infty \right\rangle = \{x \in \mathbb{R} \mid a \le x\} \cup \{-\infty, \infty\}`
  23. `\left\langle -\infty, \infty \right\rangle = \mathbb{R} \cup \{-\infty, \infty\}`
  24. ``oo`` and ``-oo`` are added to the second and third definition respectively,
  25. since if either ``-oo`` or ``oo`` is an argument, then the other one should
  26. be included (though not as an end point). This is forced, since we have,
  27. for example, ``1/AccumBounds(0, 1) = AccumBounds(1, oo)``, and the limit at
  28. `0` is not one-sided. As `x` tends to `0-`, then `1/x \rightarrow -\infty`, so `-\infty`
  29. should be interpreted as belonging to ``AccumBounds(1, oo)`` though it need
  30. not appear explicitly.
  31. In many cases it suffices to know that the limit set is bounded.
  32. However, in some other cases more exact information could be useful.
  33. For example, all accumulation values of `\cos(x) + 1` are non-negative.
  34. (``AccumBounds(-1, 1) + 1 = AccumBounds(0, 2)``)
  35. A AccumulationBounds object is defined to be real AccumulationBounds,
  36. if its end points are finite reals.
  37. Let `X`, `Y` be real AccumulationBounds, then their sum, difference,
  38. product are defined to be the following sets:
  39. `X + Y = \{ x+y \mid x \in X \cap y \in Y\}`
  40. `X - Y = \{ x-y \mid x \in X \cap y \in Y\}`
  41. `X \times Y = \{ x \times y \mid x \in X \cap y \in Y\}`
  42. When an AccumBounds is raised to a negative power, if 0 is contained
  43. between the bounds then an infinite range is returned, otherwise if an
  44. endpoint is 0 then a semi-infinite range with consistent sign will be returned.
  45. AccumBounds in expressions behave a lot like Intervals but the
  46. semantics are not necessarily the same. Division (or exponentiation
  47. to a negative integer power) could be handled with *intervals* by
  48. returning a union of the results obtained after splitting the
  49. bounds between negatives and positives, but that is not done with
  50. AccumBounds. In addition, bounds are assumed to be independent of
  51. each other; if the same bound is used in more than one place in an
  52. expression, the result may not be the supremum or infimum of the
  53. expression (see below). Finally, when a boundary is ``1``,
  54. exponentiation to the power of ``oo`` yields ``oo``, neither
  55. ``1`` nor ``nan``.
  56. Examples
  57. ========
  58. >>> from sympy import AccumBounds, sin, exp, log, pi, E, S, oo
  59. >>> from sympy.abc import x
  60. >>> AccumBounds(0, 1) + AccumBounds(1, 2)
  61. AccumBounds(1, 3)
  62. >>> AccumBounds(0, 1) - AccumBounds(0, 2)
  63. AccumBounds(-2, 1)
  64. >>> AccumBounds(-2, 3)*AccumBounds(-1, 1)
  65. AccumBounds(-3, 3)
  66. >>> AccumBounds(1, 2)*AccumBounds(3, 5)
  67. AccumBounds(3, 10)
  68. The exponentiation of AccumulationBounds is defined
  69. as follows:
  70. If 0 does not belong to `X` or `n > 0` then
  71. `X^n = \{ x^n \mid x \in X\}`
  72. >>> AccumBounds(1, 4)**(S(1)/2)
  73. AccumBounds(1, 2)
  74. otherwise, an infinite or semi-infinite result is obtained:
  75. >>> 1/AccumBounds(-1, 1)
  76. AccumBounds(-oo, oo)
  77. >>> 1/AccumBounds(0, 2)
  78. AccumBounds(1/2, oo)
  79. >>> 1/AccumBounds(-oo, 0)
  80. AccumBounds(-oo, 0)
  81. A boundary of 1 will always generate all nonnegatives:
  82. >>> AccumBounds(1, 2)**oo
  83. AccumBounds(0, oo)
  84. >>> AccumBounds(0, 1)**oo
  85. AccumBounds(0, oo)
  86. If the exponent is itself an AccumulationBounds or is not an
  87. integer then unevaluated results will be returned unless the base
  88. values are positive:
  89. >>> AccumBounds(2, 3)**AccumBounds(-1, 2)
  90. AccumBounds(1/3, 9)
  91. >>> AccumBounds(-2, 3)**AccumBounds(-1, 2)
  92. AccumBounds(-2, 3)**AccumBounds(-1, 2)
  93. >>> AccumBounds(-2, -1)**(S(1)/2)
  94. sqrt(AccumBounds(-2, -1))
  95. 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`
  96. >>> AccumBounds(-1, 1)**2
  97. AccumBounds(0, 1)
  98. >>> AccumBounds(1, 3) < 4
  99. True
  100. >>> AccumBounds(1, 3) < -1
  101. False
  102. Some elementary functions can also take AccumulationBounds as input.
  103. A function `f` evaluated for some real AccumulationBounds `\left\langle a, b \right\rangle`
  104. is defined as `f(\left\langle a, b\right\rangle) = \{ f(x) \mid a \le x \le b \}`
  105. >>> sin(AccumBounds(pi/6, pi/3))
  106. AccumBounds(1/2, sqrt(3)/2)
  107. >>> exp(AccumBounds(0, 1))
  108. AccumBounds(1, E)
  109. >>> log(AccumBounds(1, E))
  110. AccumBounds(0, 1)
  111. Some symbol in an expression can be substituted for a AccumulationBounds
  112. object. But it doesn't necessarily evaluate the AccumulationBounds for
  113. that expression.
  114. The same expression can be evaluated to different values depending upon
  115. the form it is used for substitution since each instance of an
  116. AccumulationBounds is considered independent. For example:
  117. >>> (x**2 + 2*x + 1).subs(x, AccumBounds(-1, 1))
  118. AccumBounds(-1, 4)
  119. >>> ((x + 1)**2).subs(x, AccumBounds(-1, 1))
  120. AccumBounds(0, 4)
  121. References
  122. ==========
  123. .. [1] https://en.wikipedia.org/wiki/Interval_arithmetic
  124. .. [2] http://fab.cba.mit.edu/classes/S62.12/docs/Hickey_interval.pdf
  125. Notes
  126. =====
  127. Do not use ``AccumulationBounds`` for floating point interval arithmetic
  128. calculations, use ``mpmath.iv`` instead.
  129. """
  130. is_extended_real = True
  131. is_number = False
  132. def __new__(cls, min, max):
  133. min = _sympify(min)
  134. max = _sympify(max)
  135. # Only allow real intervals (use symbols with 'is_extended_real=True').
  136. if not min.is_extended_real or not max.is_extended_real:
  137. raise ValueError("Only real AccumulationBounds are supported")
  138. if max == min:
  139. return max
  140. # Make sure that the created AccumBounds object will be valid.
  141. if max.is_number and min.is_number:
  142. bad = max.is_comparable and min.is_comparable and max < min
  143. else:
  144. bad = (max - min).is_extended_negative
  145. if bad:
  146. raise ValueError(
  147. "Lower limit should be smaller than upper limit")
  148. return Basic.__new__(cls, min, max)
  149. # setting the operation priority
  150. _op_priority = 11.0
  151. def _eval_is_real(self):
  152. if self.min.is_real and self.max.is_real:
  153. return True
  154. @property
  155. def min(self):
  156. """
  157. Returns the minimum possible value attained by AccumulationBounds
  158. object.
  159. Examples
  160. ========
  161. >>> from sympy import AccumBounds
  162. >>> AccumBounds(1, 3).min
  163. 1
  164. """
  165. return self.args[0]
  166. @property
  167. def max(self):
  168. """
  169. Returns the maximum possible value attained by AccumulationBounds
  170. object.
  171. Examples
  172. ========
  173. >>> from sympy import AccumBounds
  174. >>> AccumBounds(1, 3).max
  175. 3
  176. """
  177. return self.args[1]
  178. @property
  179. def delta(self):
  180. """
  181. Returns the difference of maximum possible value attained by
  182. AccumulationBounds object and minimum possible value attained
  183. by AccumulationBounds object.
  184. Examples
  185. ========
  186. >>> from sympy import AccumBounds
  187. >>> AccumBounds(1, 3).delta
  188. 2
  189. """
  190. return self.max - self.min
  191. @property
  192. def mid(self):
  193. """
  194. Returns the mean of maximum possible value attained by
  195. AccumulationBounds object and minimum possible value
  196. attained by AccumulationBounds object.
  197. Examples
  198. ========
  199. >>> from sympy import AccumBounds
  200. >>> AccumBounds(1, 3).mid
  201. 2
  202. """
  203. return (self.min + self.max) / 2
  204. @_sympifyit('other', NotImplemented)
  205. def _eval_power(self, other):
  206. return self.__pow__(other)
  207. @_sympifyit('other', NotImplemented)
  208. def __add__(self, other):
  209. if isinstance(other, Expr):
  210. if isinstance(other, AccumBounds):
  211. return AccumBounds(
  212. Add(self.min, other.min),
  213. Add(self.max, other.max))
  214. if other is S.Infinity and self.min is S.NegativeInfinity or \
  215. other is S.NegativeInfinity and self.max is S.Infinity:
  216. return AccumBounds(-oo, oo)
  217. elif other.is_extended_real:
  218. if self.min is S.NegativeInfinity and self.max is S.Infinity:
  219. return AccumBounds(-oo, oo)
  220. elif self.min is S.NegativeInfinity:
  221. return AccumBounds(-oo, self.max + other)
  222. elif self.max is S.Infinity:
  223. return AccumBounds(self.min + other, oo)
  224. else:
  225. return AccumBounds(Add(self.min, other), Add(self.max, other))
  226. return Add(self, other, evaluate=False)
  227. return NotImplemented
  228. __radd__ = __add__
  229. def __neg__(self):
  230. return AccumBounds(-self.max, -self.min)
  231. @_sympifyit('other', NotImplemented)
  232. def __sub__(self, other):
  233. if isinstance(other, Expr):
  234. if isinstance(other, AccumBounds):
  235. return AccumBounds(
  236. Add(self.min, -other.max),
  237. Add(self.max, -other.min))
  238. if other is S.NegativeInfinity and self.min is S.NegativeInfinity or \
  239. other is S.Infinity and self.max is S.Infinity:
  240. return AccumBounds(-oo, oo)
  241. elif other.is_extended_real:
  242. if self.min is S.NegativeInfinity and self.max is S.Infinity:
  243. return AccumBounds(-oo, oo)
  244. elif self.min is S.NegativeInfinity:
  245. return AccumBounds(-oo, self.max - other)
  246. elif self.max is S.Infinity:
  247. return AccumBounds(self.min - other, oo)
  248. else:
  249. return AccumBounds(
  250. Add(self.min, -other),
  251. Add(self.max, -other))
  252. return Add(self, -other, evaluate=False)
  253. return NotImplemented
  254. @_sympifyit('other', NotImplemented)
  255. def __rsub__(self, other):
  256. return self.__neg__() + other
  257. @_sympifyit('other', NotImplemented)
  258. def __mul__(self, other):
  259. if self.args == (-oo, oo):
  260. return self
  261. if isinstance(other, Expr):
  262. if isinstance(other, AccumBounds):
  263. if other.args == (-oo, oo):
  264. return other
  265. v = set()
  266. for a in self.args:
  267. vi = other*a
  268. for i in vi.args or (vi,):
  269. v.add(i)
  270. return AccumBounds(Min(*v), Max(*v))
  271. if other is S.Infinity:
  272. if self.min.is_zero:
  273. return AccumBounds(0, oo)
  274. if self.max.is_zero:
  275. return AccumBounds(-oo, 0)
  276. if other is S.NegativeInfinity:
  277. if self.min.is_zero:
  278. return AccumBounds(-oo, 0)
  279. if self.max.is_zero:
  280. return AccumBounds(0, oo)
  281. if other.is_extended_real:
  282. if other.is_zero:
  283. if self.max is S.Infinity:
  284. return AccumBounds(0, oo)
  285. if self.min is S.NegativeInfinity:
  286. return AccumBounds(-oo, 0)
  287. return S.Zero
  288. if other.is_extended_positive:
  289. return AccumBounds(
  290. Mul(self.min, other),
  291. Mul(self.max, other))
  292. elif other.is_extended_negative:
  293. return AccumBounds(
  294. Mul(self.max, other),
  295. Mul(self.min, other))
  296. if isinstance(other, Order):
  297. return other
  298. return Mul(self, other, evaluate=False)
  299. return NotImplemented
  300. __rmul__ = __mul__
  301. @_sympifyit('other', NotImplemented)
  302. def __truediv__(self, other):
  303. if isinstance(other, Expr):
  304. if isinstance(other, AccumBounds):
  305. if other.min.is_positive or other.max.is_negative:
  306. return self * AccumBounds(1/other.max, 1/other.min)
  307. if (self.min.is_extended_nonpositive and self.max.is_extended_nonnegative and
  308. other.min.is_extended_nonpositive and other.max.is_extended_nonnegative):
  309. if self.min.is_zero and other.min.is_zero:
  310. return AccumBounds(0, oo)
  311. if self.max.is_zero and other.min.is_zero:
  312. return AccumBounds(-oo, 0)
  313. return AccumBounds(-oo, oo)
  314. if self.max.is_extended_negative:
  315. if other.min.is_extended_negative:
  316. if other.max.is_zero:
  317. return AccumBounds(self.max / other.min, oo)
  318. if other.max.is_extended_positive:
  319. # if we were dealing with intervals we would return
  320. # Union(Interval(-oo, self.max/other.max),
  321. # Interval(self.max/other.min, oo))
  322. return AccumBounds(-oo, oo)
  323. if other.min.is_zero and other.max.is_extended_positive:
  324. return AccumBounds(-oo, self.max / other.max)
  325. if self.min.is_extended_positive:
  326. if other.min.is_extended_negative:
  327. if other.max.is_zero:
  328. return AccumBounds(-oo, self.min / other.min)
  329. if other.max.is_extended_positive:
  330. # if we were dealing with intervals we would return
  331. # Union(Interval(-oo, self.min/other.min),
  332. # Interval(self.min/other.max, oo))
  333. return AccumBounds(-oo, oo)
  334. if other.min.is_zero and other.max.is_extended_positive:
  335. return AccumBounds(self.min / other.max, oo)
  336. elif other.is_extended_real:
  337. if other in (S.Infinity, S.NegativeInfinity):
  338. if self == AccumBounds(-oo, oo):
  339. return AccumBounds(-oo, oo)
  340. if self.max is S.Infinity:
  341. return AccumBounds(Min(0, other), Max(0, other))
  342. if self.min is S.NegativeInfinity:
  343. return AccumBounds(Min(0, -other), Max(0, -other))
  344. if other.is_extended_positive:
  345. return AccumBounds(self.min / other, self.max / other)
  346. elif other.is_extended_negative:
  347. return AccumBounds(self.max / other, self.min / other)
  348. if (1 / other) is S.ComplexInfinity:
  349. return Mul(self, 1 / other, evaluate=False)
  350. else:
  351. return Mul(self, 1 / other)
  352. return NotImplemented
  353. @_sympifyit('other', NotImplemented)
  354. def __rtruediv__(self, other):
  355. if isinstance(other, Expr):
  356. if other.is_extended_real:
  357. if other.is_zero:
  358. return S.Zero
  359. if (self.min.is_extended_nonpositive and self.max.is_extended_nonnegative):
  360. if self.min.is_zero:
  361. if other.is_extended_positive:
  362. return AccumBounds(Mul(other, 1 / self.max), oo)
  363. if other.is_extended_negative:
  364. return AccumBounds(-oo, Mul(other, 1 / self.max))
  365. if self.max.is_zero:
  366. if other.is_extended_positive:
  367. return AccumBounds(-oo, Mul(other, 1 / self.min))
  368. if other.is_extended_negative:
  369. return AccumBounds(Mul(other, 1 / self.min), oo)
  370. return AccumBounds(-oo, oo)
  371. else:
  372. return AccumBounds(Min(other / self.min, other / self.max),
  373. Max(other / self.min, other / self.max))
  374. return Mul(other, 1 / self, evaluate=False)
  375. else:
  376. return NotImplemented
  377. @_sympifyit('other', NotImplemented)
  378. def __pow__(self, other):
  379. if isinstance(other, Expr):
  380. if other is S.Infinity:
  381. if self.min.is_extended_nonnegative:
  382. if self.max < 1:
  383. return S.Zero
  384. if self.min > 1:
  385. return S.Infinity
  386. return AccumBounds(0, oo)
  387. elif self.max.is_extended_negative:
  388. if self.min > -1:
  389. return S.Zero
  390. if self.max < -1:
  391. return zoo
  392. return S.NaN
  393. else:
  394. if self.min > -1:
  395. if self.max < 1:
  396. return S.Zero
  397. return AccumBounds(0, oo)
  398. return AccumBounds(-oo, oo)
  399. if other is S.NegativeInfinity:
  400. return (1/self)**oo
  401. # generically true
  402. if (self.max - self.min).is_nonnegative:
  403. # well defined
  404. if self.min.is_nonnegative:
  405. # no 0 to worry about
  406. if other.is_nonnegative:
  407. # no infinity to worry about
  408. return self.func(self.min**other, self.max**other)
  409. if other.is_zero:
  410. return S.One # x**0 = 1
  411. if other.is_Integer or other.is_integer:
  412. if self.min.is_extended_positive:
  413. return AccumBounds(
  414. Min(self.min**other, self.max**other),
  415. Max(self.min**other, self.max**other))
  416. elif self.max.is_extended_negative:
  417. return AccumBounds(
  418. Min(self.max**other, self.min**other),
  419. Max(self.max**other, self.min**other))
  420. if other % 2 == 0:
  421. if other.is_extended_negative:
  422. if self.min.is_zero:
  423. return AccumBounds(self.max**other, oo)
  424. if self.max.is_zero:
  425. return AccumBounds(self.min**other, oo)
  426. return AccumBounds(0, oo)
  427. return AccumBounds(
  428. S.Zero, Max(self.min**other, self.max**other))
  429. elif other % 2 == 1:
  430. if other.is_extended_negative:
  431. if self.min.is_zero:
  432. return AccumBounds(self.max**other, oo)
  433. if self.max.is_zero:
  434. return AccumBounds(-oo, self.min**other)
  435. return AccumBounds(-oo, oo)
  436. return AccumBounds(self.min**other, self.max**other)
  437. # non-integer exponent
  438. # 0**neg or neg**frac yields complex
  439. if (other.is_number or other.is_rational) and (
  440. self.min.is_extended_nonnegative or (
  441. other.is_extended_nonnegative and
  442. self.min.is_extended_nonnegative)):
  443. num, den = other.as_numer_denom()
  444. if num is S.One:
  445. return AccumBounds(*[i**(1/den) for i in self.args])
  446. elif den is not S.One: # e.g. if other is not Float
  447. return (self**num)**(1/den) # ok for non-negative base
  448. if isinstance(other, AccumBounds):
  449. if (self.min.is_extended_positive or
  450. self.min.is_extended_nonnegative and
  451. other.min.is_extended_nonnegative):
  452. p = [self**i for i in other.args]
  453. if not any(i.is_Pow for i in p):
  454. a = [j for i in p for j in i.args or (i,)]
  455. try:
  456. return self.func(min(a), max(a))
  457. except TypeError: # can't sort
  458. pass
  459. return Pow(self, other, evaluate=False)
  460. return NotImplemented
  461. @_sympifyit('other', NotImplemented)
  462. def __rpow__(self, other):
  463. if other.is_real and other.is_extended_nonnegative and (
  464. self.max - self.min).is_extended_positive:
  465. if other is S.One:
  466. return S.One
  467. if other.is_extended_positive:
  468. a, b = [other**i for i in self.args]
  469. if min(a, b) != a:
  470. a, b = b, a
  471. return self.func(a, b)
  472. if other.is_zero:
  473. if self.min.is_zero:
  474. return self.func(0, 1)
  475. if self.min.is_extended_positive:
  476. return S.Zero
  477. return Pow(other, self, evaluate=False)
  478. def __abs__(self):
  479. if self.max.is_extended_negative:
  480. return self.__neg__()
  481. elif self.min.is_extended_negative:
  482. return AccumBounds(S.Zero, Max(abs(self.min), self.max))
  483. else:
  484. return self
  485. def __contains__(self, other):
  486. """
  487. Returns ``True`` if other is contained in self, where other
  488. belongs to extended real numbers, ``False`` if not contained,
  489. otherwise TypeError is raised.
  490. Examples
  491. ========
  492. >>> from sympy import AccumBounds, oo
  493. >>> 1 in AccumBounds(-1, 3)
  494. True
  495. -oo and oo go together as limits (in AccumulationBounds).
  496. >>> -oo in AccumBounds(1, oo)
  497. True
  498. >>> oo in AccumBounds(-oo, 0)
  499. True
  500. """
  501. other = _sympify(other)
  502. if other in (S.Infinity, S.NegativeInfinity):
  503. if self.min is S.NegativeInfinity or self.max is S.Infinity:
  504. return True
  505. return False
  506. rv = And(self.min <= other, self.max >= other)
  507. if rv not in (True, False):
  508. raise TypeError("input failed to evaluate")
  509. return rv
  510. def intersection(self, other):
  511. """
  512. Returns the intersection of 'self' and 'other'.
  513. Here other can be an instance of :py:class:`~.FiniteSet` or AccumulationBounds.
  514. Parameters
  515. ==========
  516. other: AccumulationBounds
  517. Another AccumulationBounds object with which the intersection
  518. has to be computed.
  519. Returns
  520. =======
  521. AccumulationBounds
  522. Intersection of ``self`` and ``other``.
  523. Examples
  524. ========
  525. >>> from sympy import AccumBounds, FiniteSet
  526. >>> AccumBounds(1, 3).intersection(AccumBounds(2, 4))
  527. AccumBounds(2, 3)
  528. >>> AccumBounds(1, 3).intersection(AccumBounds(4, 6))
  529. EmptySet
  530. >>> AccumBounds(1, 4).intersection(FiniteSet(1, 2, 5))
  531. {1, 2}
  532. """
  533. if not isinstance(other, (AccumBounds, FiniteSet)):
  534. raise TypeError(
  535. "Input must be AccumulationBounds or FiniteSet object")
  536. if isinstance(other, FiniteSet):
  537. fin_set = S.EmptySet
  538. for i in other:
  539. if i in self:
  540. fin_set = fin_set + FiniteSet(i)
  541. return fin_set
  542. if self.max < other.min or self.min > other.max:
  543. return S.EmptySet
  544. if self.min <= other.min:
  545. if self.max <= other.max:
  546. return AccumBounds(other.min, self.max)
  547. if self.max > other.max:
  548. return other
  549. if other.min <= self.min:
  550. if other.max < self.max:
  551. return AccumBounds(self.min, other.max)
  552. if other.max > self.max:
  553. return self
  554. def union(self, other):
  555. # TODO : Devise a better method for Union of AccumBounds
  556. # this method is not actually correct and
  557. # can be made better
  558. if not isinstance(other, AccumBounds):
  559. raise TypeError(
  560. "Input must be AccumulationBounds or FiniteSet object")
  561. if self.min <= other.min and self.max >= other.min:
  562. return AccumBounds(self.min, Max(self.max, other.max))
  563. if other.min <= self.min and other.max >= self.min:
  564. return AccumBounds(other.min, Max(self.max, other.max))
  565. @dispatch(AccumulationBounds, AccumulationBounds) # type: ignore # noqa:F811
  566. def _eval_is_le(lhs, rhs): # noqa:F811
  567. if is_le(lhs.max, rhs.min):
  568. return True
  569. if is_gt(lhs.min, rhs.max):
  570. return False
  571. @dispatch(AccumulationBounds, Basic) # type: ignore # noqa:F811
  572. def _eval_is_le(lhs, rhs): # noqa: F811
  573. """
  574. Returns ``True `` if range of values attained by ``lhs`` AccumulationBounds
  575. object is greater than the range of values attained by ``rhs``,
  576. where ``rhs`` may be any value of type AccumulationBounds object or
  577. extended real number value, ``False`` if ``rhs`` satisfies
  578. the same property, else an unevaluated :py:class:`~.Relational`.
  579. Examples
  580. ========
  581. >>> from sympy import AccumBounds, oo
  582. >>> AccumBounds(1, 3) > AccumBounds(4, oo)
  583. False
  584. >>> AccumBounds(1, 4) > AccumBounds(3, 4)
  585. AccumBounds(1, 4) > AccumBounds(3, 4)
  586. >>> AccumBounds(1, oo) > -1
  587. True
  588. """
  589. if not rhs.is_extended_real:
  590. raise TypeError(
  591. "Invalid comparison of %s %s" %
  592. (type(rhs), rhs))
  593. elif rhs.is_comparable:
  594. if is_le(lhs.max, rhs):
  595. return True
  596. if is_gt(lhs.min, rhs):
  597. return False
  598. @dispatch(AccumulationBounds, AccumulationBounds)
  599. def _eval_is_ge(lhs, rhs): # noqa:F811
  600. if is_ge(lhs.min, rhs.max):
  601. return True
  602. if is_lt(lhs.max, rhs.min):
  603. return False
  604. @dispatch(AccumulationBounds, Expr) # type:ignore
  605. def _eval_is_ge(lhs, rhs): # noqa: F811
  606. """
  607. Returns ``True`` if range of values attained by ``lhs`` AccumulationBounds
  608. object is less that the range of values attained by ``rhs``, where
  609. other may be any value of type AccumulationBounds object or extended
  610. real number value, ``False`` if ``rhs`` satisfies the same
  611. property, else an unevaluated :py:class:`~.Relational`.
  612. Examples
  613. ========
  614. >>> from sympy import AccumBounds, oo
  615. >>> AccumBounds(1, 3) >= AccumBounds(4, oo)
  616. False
  617. >>> AccumBounds(1, 4) >= AccumBounds(3, 4)
  618. AccumBounds(1, 4) >= AccumBounds(3, 4)
  619. >>> AccumBounds(1, oo) >= 1
  620. True
  621. """
  622. if not rhs.is_extended_real:
  623. raise TypeError(
  624. "Invalid comparison of %s %s" %
  625. (type(rhs), rhs))
  626. elif rhs.is_comparable:
  627. if is_ge(lhs.min, rhs):
  628. return True
  629. if is_lt(lhs.max, rhs):
  630. return False
  631. @dispatch(Expr, AccumulationBounds) # type:ignore
  632. def _eval_is_ge(lhs, rhs): # noqa:F811
  633. if not lhs.is_extended_real:
  634. raise TypeError(
  635. "Invalid comparison of %s %s" %
  636. (type(lhs), lhs))
  637. elif lhs.is_comparable:
  638. if is_le(rhs.max, lhs):
  639. return True
  640. if is_gt(rhs.min, lhs):
  641. return False
  642. @dispatch(AccumulationBounds, AccumulationBounds) # type:ignore
  643. def _eval_is_ge(lhs, rhs): # noqa:F811
  644. if is_ge(lhs.min, rhs.max):
  645. return True
  646. if is_lt(lhs.max, rhs.min):
  647. return False
  648. # setting an alias for AccumulationBounds
  649. AccumBounds = AccumulationBounds