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"""An accumulation bounds.
  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 does not 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] https://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) -> Expr: # type: ignore
  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. v.update(vi.args or (vi,))
  269. return AccumBounds(Min(*v), Max(*v))
  270. if other is S.Infinity:
  271. if self.min.is_zero:
  272. return AccumBounds(0, oo)
  273. if self.max.is_zero:
  274. return AccumBounds(-oo, 0)
  275. if other is S.NegativeInfinity:
  276. if self.min.is_zero:
  277. return AccumBounds(-oo, 0)
  278. if self.max.is_zero:
  279. return AccumBounds(0, oo)
  280. if other.is_extended_real:
  281. if other.is_zero:
  282. if self.max is S.Infinity:
  283. return AccumBounds(0, oo)
  284. if self.min is S.NegativeInfinity:
  285. return AccumBounds(-oo, 0)
  286. return S.Zero
  287. if other.is_extended_positive:
  288. return AccumBounds(
  289. Mul(self.min, other),
  290. Mul(self.max, other))
  291. elif other.is_extended_negative:
  292. return AccumBounds(
  293. Mul(self.max, other),
  294. Mul(self.min, other))
  295. if isinstance(other, Order):
  296. return other
  297. return Mul(self, other, evaluate=False)
  298. return NotImplemented
  299. __rmul__ = __mul__
  300. @_sympifyit('other', NotImplemented)
  301. def __truediv__(self, other):
  302. if isinstance(other, Expr):
  303. if isinstance(other, AccumBounds):
  304. if other.min.is_positive or other.max.is_negative:
  305. return self * AccumBounds(1/other.max, 1/other.min)
  306. if (self.min.is_extended_nonpositive and self.max.is_extended_nonnegative and
  307. other.min.is_extended_nonpositive and other.max.is_extended_nonnegative):
  308. if self.min.is_zero and other.min.is_zero:
  309. return AccumBounds(0, oo)
  310. if self.max.is_zero and other.min.is_zero:
  311. return AccumBounds(-oo, 0)
  312. return AccumBounds(-oo, oo)
  313. if self.max.is_extended_negative:
  314. if other.min.is_extended_negative:
  315. if other.max.is_zero:
  316. return AccumBounds(self.max / other.min, oo)
  317. if other.max.is_extended_positive:
  318. # if we were dealing with intervals we would return
  319. # Union(Interval(-oo, self.max/other.max),
  320. # Interval(self.max/other.min, oo))
  321. return AccumBounds(-oo, oo)
  322. if other.min.is_zero and other.max.is_extended_positive:
  323. return AccumBounds(-oo, self.max / other.max)
  324. if self.min.is_extended_positive:
  325. if other.min.is_extended_negative:
  326. if other.max.is_zero:
  327. return AccumBounds(-oo, self.min / other.min)
  328. if other.max.is_extended_positive:
  329. # if we were dealing with intervals we would return
  330. # Union(Interval(-oo, self.min/other.min),
  331. # Interval(self.min/other.max, oo))
  332. return AccumBounds(-oo, oo)
  333. if other.min.is_zero and other.max.is_extended_positive:
  334. return AccumBounds(self.min / other.max, oo)
  335. elif other.is_extended_real:
  336. if other in (S.Infinity, S.NegativeInfinity):
  337. if self == AccumBounds(-oo, oo):
  338. return AccumBounds(-oo, oo)
  339. if self.max is S.Infinity:
  340. return AccumBounds(Min(0, other), Max(0, other))
  341. if self.min is S.NegativeInfinity:
  342. return AccumBounds(Min(0, -other), Max(0, -other))
  343. if other.is_extended_positive:
  344. return AccumBounds(self.min / other, self.max / other)
  345. elif other.is_extended_negative:
  346. return AccumBounds(self.max / other, self.min / other)
  347. if (1 / other) is S.ComplexInfinity:
  348. return Mul(self, 1 / other, evaluate=False)
  349. else:
  350. return Mul(self, 1 / other)
  351. return NotImplemented
  352. @_sympifyit('other', NotImplemented)
  353. def __rtruediv__(self, other):
  354. if isinstance(other, Expr):
  355. if other.is_extended_real:
  356. if other.is_zero:
  357. return S.Zero
  358. if (self.min.is_extended_nonpositive and self.max.is_extended_nonnegative):
  359. if self.min.is_zero:
  360. if other.is_extended_positive:
  361. return AccumBounds(Mul(other, 1 / self.max), oo)
  362. if other.is_extended_negative:
  363. return AccumBounds(-oo, Mul(other, 1 / self.max))
  364. if self.max.is_zero:
  365. if other.is_extended_positive:
  366. return AccumBounds(-oo, Mul(other, 1 / self.min))
  367. if other.is_extended_negative:
  368. return AccumBounds(Mul(other, 1 / self.min), oo)
  369. return AccumBounds(-oo, oo)
  370. else:
  371. return AccumBounds(Min(other / self.min, other / self.max),
  372. Max(other / self.min, other / self.max))
  373. return Mul(other, 1 / self, evaluate=False)
  374. else:
  375. return NotImplemented
  376. @_sympifyit('other', NotImplemented)
  377. def __pow__(self, other):
  378. if isinstance(other, Expr):
  379. if other is S.Infinity:
  380. if self.min.is_extended_nonnegative:
  381. if self.max < 1:
  382. return S.Zero
  383. if self.min > 1:
  384. return S.Infinity
  385. return AccumBounds(0, oo)
  386. elif self.max.is_extended_negative:
  387. if self.min > -1:
  388. return S.Zero
  389. if self.max < -1:
  390. return zoo
  391. return S.NaN
  392. else:
  393. if self.min > -1:
  394. if self.max < 1:
  395. return S.Zero
  396. return AccumBounds(0, oo)
  397. return AccumBounds(-oo, oo)
  398. if other is S.NegativeInfinity:
  399. return (1/self)**oo
  400. # generically true
  401. if (self.max - self.min).is_nonnegative:
  402. # well defined
  403. if self.min.is_nonnegative:
  404. # no 0 to worry about
  405. if other.is_nonnegative:
  406. # no infinity to worry about
  407. return self.func(self.min**other, self.max**other)
  408. if other.is_zero:
  409. return S.One # x**0 = 1
  410. if other.is_Integer or other.is_integer:
  411. if self.min.is_extended_positive:
  412. return AccumBounds(
  413. Min(self.min**other, self.max**other),
  414. Max(self.min**other, self.max**other))
  415. elif self.max.is_extended_negative:
  416. return AccumBounds(
  417. Min(self.max**other, self.min**other),
  418. Max(self.max**other, self.min**other))
  419. if other % 2 == 0:
  420. if other.is_extended_negative:
  421. if self.min.is_zero:
  422. return AccumBounds(self.max**other, oo)
  423. if self.max.is_zero:
  424. return AccumBounds(self.min**other, oo)
  425. return (1/self)**(-other)
  426. return AccumBounds(
  427. S.Zero, Max(self.min**other, self.max**other))
  428. elif other % 2 == 1:
  429. if other.is_extended_negative:
  430. if self.min.is_zero:
  431. return AccumBounds(self.max**other, oo)
  432. if self.max.is_zero:
  433. return AccumBounds(-oo, self.min**other)
  434. return (1/self)**(-other)
  435. return AccumBounds(self.min**other, self.max**other)
  436. # non-integer exponent
  437. # 0**neg or neg**frac yields complex
  438. if (other.is_number or other.is_rational) and (
  439. self.min.is_extended_nonnegative or (
  440. other.is_extended_nonnegative and
  441. self.min.is_extended_nonnegative)):
  442. num, den = other.as_numer_denom()
  443. if num is S.One:
  444. return AccumBounds(*[i**(1/den) for i in self.args])
  445. elif den is not S.One: # e.g. if other is not Float
  446. return (self**num)**(1/den) # ok for non-negative base
  447. if isinstance(other, AccumBounds):
  448. if (self.min.is_extended_positive or
  449. self.min.is_extended_nonnegative and
  450. other.min.is_extended_nonnegative):
  451. p = [self**i for i in other.args]
  452. if not any(i.is_Pow for i in p):
  453. a = [j for i in p for j in i.args or (i,)]
  454. try:
  455. return self.func(min(a), max(a))
  456. except TypeError: # can't sort
  457. pass
  458. return Pow(self, other, evaluate=False)
  459. return NotImplemented
  460. @_sympifyit('other', NotImplemented)
  461. def __rpow__(self, other):
  462. if other.is_real and other.is_extended_nonnegative and (
  463. self.max - self.min).is_extended_positive:
  464. if other is S.One:
  465. return S.One
  466. if other.is_extended_positive:
  467. a, b = [other**i for i in self.args]
  468. if min(a, b) != a:
  469. a, b = b, a
  470. return self.func(a, b)
  471. if other.is_zero:
  472. if self.min.is_zero:
  473. return self.func(0, 1)
  474. if self.min.is_extended_positive:
  475. return S.Zero
  476. return Pow(other, self, evaluate=False)
  477. def __abs__(self):
  478. if self.max.is_extended_negative:
  479. return self.__neg__()
  480. elif self.min.is_extended_negative:
  481. return AccumBounds(S.Zero, Max(abs(self.min), self.max))
  482. else:
  483. return self
  484. def __contains__(self, other):
  485. """
  486. Returns ``True`` if other is contained in self, where other
  487. belongs to extended real numbers, ``False`` if not contained,
  488. otherwise TypeError is raised.
  489. Examples
  490. ========
  491. >>> from sympy import AccumBounds, oo
  492. >>> 1 in AccumBounds(-1, 3)
  493. True
  494. -oo and oo go together as limits (in AccumulationBounds).
  495. >>> -oo in AccumBounds(1, oo)
  496. True
  497. >>> oo in AccumBounds(-oo, 0)
  498. True
  499. """
  500. other = _sympify(other)
  501. if other in (S.Infinity, S.NegativeInfinity):
  502. if self.min is S.NegativeInfinity or self.max is S.Infinity:
  503. return True
  504. return False
  505. rv = And(self.min <= other, self.max >= other)
  506. if rv not in (True, False):
  507. raise TypeError("input failed to evaluate")
  508. return rv
  509. def intersection(self, other):
  510. """
  511. Returns the intersection of 'self' and 'other'.
  512. Here other can be an instance of :py:class:`~.FiniteSet` or AccumulationBounds.
  513. Parameters
  514. ==========
  515. other : AccumulationBounds
  516. Another AccumulationBounds object with which the intersection
  517. has to be computed.
  518. Returns
  519. =======
  520. AccumulationBounds
  521. Intersection of ``self`` and ``other``.
  522. Examples
  523. ========
  524. >>> from sympy import AccumBounds, FiniteSet
  525. >>> AccumBounds(1, 3).intersection(AccumBounds(2, 4))
  526. AccumBounds(2, 3)
  527. >>> AccumBounds(1, 3).intersection(AccumBounds(4, 6))
  528. EmptySet
  529. >>> AccumBounds(1, 4).intersection(FiniteSet(1, 2, 5))
  530. {1, 2}
  531. """
  532. if not isinstance(other, (AccumBounds, FiniteSet)):
  533. raise TypeError(
  534. "Input must be AccumulationBounds or FiniteSet object")
  535. if isinstance(other, FiniteSet):
  536. fin_set = S.EmptySet
  537. for i in other:
  538. if i in self:
  539. fin_set = fin_set + FiniteSet(i)
  540. return fin_set
  541. if self.max < other.min or self.min > other.max:
  542. return S.EmptySet
  543. if self.min <= other.min:
  544. if self.max <= other.max:
  545. return AccumBounds(other.min, self.max)
  546. if self.max > other.max:
  547. return other
  548. if other.min <= self.min:
  549. if other.max < self.max:
  550. return AccumBounds(self.min, other.max)
  551. if other.max > self.max:
  552. return self
  553. def union(self, other):
  554. # TODO : Devise a better method for Union of AccumBounds
  555. # this method is not actually correct and
  556. # can be made better
  557. if not isinstance(other, AccumBounds):
  558. raise TypeError(
  559. "Input must be AccumulationBounds or FiniteSet object")
  560. if self.min <= other.min and self.max >= other.min:
  561. return AccumBounds(self.min, Max(self.max, other.max))
  562. if other.min <= self.min and other.max >= self.min:
  563. return AccumBounds(other.min, Max(self.max, other.max))
  564. @dispatch(AccumulationBounds, AccumulationBounds) # type: ignore # noqa:F811
  565. def _eval_is_le(lhs, rhs): # noqa:F811
  566. if is_le(lhs.max, rhs.min):
  567. return True
  568. if is_gt(lhs.min, rhs.max):
  569. return False
  570. @dispatch(AccumulationBounds, Basic) # type: ignore # noqa:F811
  571. def _eval_is_le(lhs, rhs): # noqa: F811
  572. """
  573. Returns ``True `` if range of values attained by ``lhs`` AccumulationBounds
  574. object is greater than the range of values attained by ``rhs``,
  575. where ``rhs`` may be any value of type AccumulationBounds object or
  576. extended real number value, ``False`` if ``rhs`` satisfies
  577. the same property, else an unevaluated :py:class:`~.Relational`.
  578. Examples
  579. ========
  580. >>> from sympy import AccumBounds, oo
  581. >>> AccumBounds(1, 3) > AccumBounds(4, oo)
  582. False
  583. >>> AccumBounds(1, 4) > AccumBounds(3, 4)
  584. AccumBounds(1, 4) > AccumBounds(3, 4)
  585. >>> AccumBounds(1, oo) > -1
  586. True
  587. """
  588. if not rhs.is_extended_real:
  589. raise TypeError(
  590. "Invalid comparison of %s %s" %
  591. (type(rhs), rhs))
  592. elif rhs.is_comparable:
  593. if is_le(lhs.max, rhs):
  594. return True
  595. if is_gt(lhs.min, rhs):
  596. return False
  597. @dispatch(AccumulationBounds, AccumulationBounds)
  598. def _eval_is_ge(lhs, rhs): # noqa:F811
  599. if is_ge(lhs.min, rhs.max):
  600. return True
  601. if is_lt(lhs.max, rhs.min):
  602. return False
  603. @dispatch(AccumulationBounds, Expr) # type:ignore
  604. def _eval_is_ge(lhs, rhs): # noqa: F811
  605. """
  606. Returns ``True`` if range of values attained by ``lhs`` AccumulationBounds
  607. object is less that the range of values attained by ``rhs``, where
  608. other may be any value of type AccumulationBounds object or extended
  609. real number value, ``False`` if ``rhs`` satisfies the same
  610. property, else an unevaluated :py:class:`~.Relational`.
  611. Examples
  612. ========
  613. >>> from sympy import AccumBounds, oo
  614. >>> AccumBounds(1, 3) >= AccumBounds(4, oo)
  615. False
  616. >>> AccumBounds(1, 4) >= AccumBounds(3, 4)
  617. AccumBounds(1, 4) >= AccumBounds(3, 4)
  618. >>> AccumBounds(1, oo) >= 1
  619. True
  620. """
  621. if not rhs.is_extended_real:
  622. raise TypeError(
  623. "Invalid comparison of %s %s" %
  624. (type(rhs), rhs))
  625. elif rhs.is_comparable:
  626. if is_ge(lhs.min, rhs):
  627. return True
  628. if is_lt(lhs.max, rhs):
  629. return False
  630. @dispatch(Expr, AccumulationBounds) # type:ignore
  631. def _eval_is_ge(lhs, rhs): # noqa:F811
  632. if not lhs.is_extended_real:
  633. raise TypeError(
  634. "Invalid comparison of %s %s" %
  635. (type(lhs), lhs))
  636. elif lhs.is_comparable:
  637. if is_le(rhs.max, lhs):
  638. return True
  639. if is_gt(rhs.min, lhs):
  640. return False
  641. @dispatch(AccumulationBounds, AccumulationBounds) # type:ignore
  642. def _eval_is_ge(lhs, rhs): # noqa:F811
  643. if is_ge(lhs.min, rhs.max):
  644. return True
  645. if is_lt(lhs.max, rhs.min):
  646. return False
  647. # setting an alias for AccumulationBounds
  648. AccumBounds = AccumulationBounds