order.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  1. """
  2. Handlers related to order relations: positive, negative, etc.
  3. """
  4. from sympy.assumptions import Q, ask
  5. from sympy.core import Add, Basic, Expr, Mul, Pow, S
  6. from sympy.core.logic import fuzzy_not, fuzzy_and, fuzzy_or
  7. from sympy.core.numbers import E, ImaginaryUnit, NaN, I, pi
  8. from sympy.functions import Abs, acos, acot, asin, atan, exp, factorial, log
  9. from sympy.matrices import Determinant, Trace
  10. from sympy.matrices.expressions.matexpr import MatrixElement
  11. from sympy.multipledispatch import MDNotImplementedError
  12. from ..predicates.order import (NegativePredicate, NonNegativePredicate,
  13. NonZeroPredicate, ZeroPredicate, NonPositivePredicate, PositivePredicate,
  14. ExtendedNegativePredicate, ExtendedNonNegativePredicate,
  15. ExtendedNonPositivePredicate, ExtendedNonZeroPredicate,
  16. ExtendedPositivePredicate,)
  17. # NegativePredicate
  18. def _NegativePredicate_number(expr, assumptions):
  19. r, i = expr.as_real_imag()
  20. if r == S.NaN or i == S.NaN:
  21. return None
  22. # If the imaginary part can symbolically be shown to be zero then
  23. # we just evaluate the real part; otherwise we evaluate the imaginary
  24. # part to see if it actually evaluates to zero and if it does then
  25. # we make the comparison between the real part and zero.
  26. if not i:
  27. r = r.evalf(2)
  28. if r._prec != 1:
  29. return r < 0
  30. else:
  31. i = i.evalf(2)
  32. if i._prec != 1:
  33. if i != 0:
  34. return False
  35. r = r.evalf(2)
  36. if r._prec != 1:
  37. return r < 0
  38. @NegativePredicate.register(Basic)
  39. def _(expr, assumptions):
  40. if expr.is_number:
  41. return _NegativePredicate_number(expr, assumptions)
  42. @NegativePredicate.register(Expr)
  43. def _(expr, assumptions):
  44. ret = expr.is_negative
  45. if ret is None:
  46. raise MDNotImplementedError
  47. return ret
  48. @NegativePredicate.register(Add)
  49. def _(expr, assumptions):
  50. """
  51. Positive + Positive -> Positive,
  52. Negative + Negative -> Negative
  53. """
  54. if expr.is_number:
  55. return _NegativePredicate_number(expr, assumptions)
  56. r = ask(Q.real(expr), assumptions)
  57. if r is not True:
  58. return r
  59. nonpos = 0
  60. for arg in expr.args:
  61. if ask(Q.negative(arg), assumptions) is not True:
  62. if ask(Q.positive(arg), assumptions) is False:
  63. nonpos += 1
  64. else:
  65. break
  66. else:
  67. if nonpos < len(expr.args):
  68. return True
  69. @NegativePredicate.register(Mul)
  70. def _(expr, assumptions):
  71. if expr.is_number:
  72. return _NegativePredicate_number(expr, assumptions)
  73. result = None
  74. for arg in expr.args:
  75. if result is None:
  76. result = False
  77. if ask(Q.negative(arg), assumptions):
  78. result = not result
  79. elif ask(Q.positive(arg), assumptions):
  80. pass
  81. else:
  82. return
  83. return result
  84. @NegativePredicate.register(Pow)
  85. def _(expr, assumptions):
  86. """
  87. Real ** Even -> NonNegative
  88. Real ** Odd -> same_as_base
  89. NonNegative ** Positive -> NonNegative
  90. """
  91. if expr.base == E:
  92. # Exponential is always positive:
  93. if ask(Q.real(expr.exp), assumptions):
  94. return False
  95. return
  96. if expr.is_number:
  97. return _NegativePredicate_number(expr, assumptions)
  98. if ask(Q.real(expr.base), assumptions):
  99. if ask(Q.positive(expr.base), assumptions):
  100. if ask(Q.real(expr.exp), assumptions):
  101. return False
  102. if ask(Q.even(expr.exp), assumptions):
  103. return False
  104. if ask(Q.odd(expr.exp), assumptions):
  105. return ask(Q.negative(expr.base), assumptions)
  106. @NegativePredicate.register_many(Abs, ImaginaryUnit)
  107. def _(expr, assumptions):
  108. return False
  109. @NegativePredicate.register(exp)
  110. def _(expr, assumptions):
  111. if ask(Q.real(expr.exp), assumptions):
  112. return False
  113. raise MDNotImplementedError
  114. # NonNegativePredicate
  115. @NonNegativePredicate.register(Basic)
  116. def _(expr, assumptions):
  117. if expr.is_number:
  118. notnegative = fuzzy_not(_NegativePredicate_number(expr, assumptions))
  119. if notnegative:
  120. return ask(Q.real(expr), assumptions)
  121. else:
  122. return notnegative
  123. @NonNegativePredicate.register(Expr)
  124. def _(expr, assumptions):
  125. ret = expr.is_nonnegative
  126. if ret is None:
  127. raise MDNotImplementedError
  128. return ret
  129. # NonZeroPredicate
  130. @NonZeroPredicate.register(Expr)
  131. def _(expr, assumptions):
  132. ret = expr.is_nonzero
  133. if ret is None:
  134. raise MDNotImplementedError
  135. return ret
  136. @NonZeroPredicate.register(Basic)
  137. def _(expr, assumptions):
  138. if ask(Q.real(expr)) is False:
  139. return False
  140. if expr.is_number:
  141. # if there are no symbols just evalf
  142. i = expr.evalf(2)
  143. def nonz(i):
  144. if i._prec != 1:
  145. return i != 0
  146. return fuzzy_or(nonz(i) for i in i.as_real_imag())
  147. @NonZeroPredicate.register(Add)
  148. def _(expr, assumptions):
  149. if all(ask(Q.positive(x), assumptions) for x in expr.args) \
  150. or all(ask(Q.negative(x), assumptions) for x in expr.args):
  151. return True
  152. @NonZeroPredicate.register(Mul)
  153. def _(expr, assumptions):
  154. for arg in expr.args:
  155. result = ask(Q.nonzero(arg), assumptions)
  156. if result:
  157. continue
  158. return result
  159. return True
  160. @NonZeroPredicate.register(Pow)
  161. def _(expr, assumptions):
  162. return ask(Q.nonzero(expr.base), assumptions)
  163. @NonZeroPredicate.register(Abs)
  164. def _(expr, assumptions):
  165. return ask(Q.nonzero(expr.args[0]), assumptions)
  166. @NonZeroPredicate.register(NaN)
  167. def _(expr, assumptions):
  168. return None
  169. # ZeroPredicate
  170. @ZeroPredicate.register(Expr)
  171. def _(expr, assumptions):
  172. ret = expr.is_zero
  173. if ret is None:
  174. raise MDNotImplementedError
  175. return ret
  176. @ZeroPredicate.register(Basic)
  177. def _(expr, assumptions):
  178. return fuzzy_and([fuzzy_not(ask(Q.nonzero(expr), assumptions)),
  179. ask(Q.real(expr), assumptions)])
  180. @ZeroPredicate.register(Mul)
  181. def _(expr, assumptions):
  182. # TODO: This should be deducible from the nonzero handler
  183. return fuzzy_or(ask(Q.zero(arg), assumptions) for arg in expr.args)
  184. # NonPositivePredicate
  185. @NonPositivePredicate.register(Expr)
  186. def _(expr, assumptions):
  187. ret = expr.is_nonpositive
  188. if ret is None:
  189. raise MDNotImplementedError
  190. return ret
  191. @NonPositivePredicate.register(Basic)
  192. def _(expr, assumptions):
  193. if expr.is_number:
  194. notpositive = fuzzy_not(_PositivePredicate_number(expr, assumptions))
  195. if notpositive:
  196. return ask(Q.real(expr), assumptions)
  197. else:
  198. return notpositive
  199. # PositivePredicate
  200. def _PositivePredicate_number(expr, assumptions):
  201. r, i = expr.as_real_imag()
  202. # If the imaginary part can symbolically be shown to be zero then
  203. # we just evaluate the real part; otherwise we evaluate the imaginary
  204. # part to see if it actually evaluates to zero and if it does then
  205. # we make the comparison between the real part and zero.
  206. if not i:
  207. r = r.evalf(2)
  208. if r._prec != 1:
  209. return r > 0
  210. else:
  211. i = i.evalf(2)
  212. if i._prec != 1:
  213. if i != 0:
  214. return False
  215. r = r.evalf(2)
  216. if r._prec != 1:
  217. return r > 0
  218. @PositivePredicate.register(Expr)
  219. def _(expr, assumptions):
  220. ret = expr.is_positive
  221. if ret is None:
  222. raise MDNotImplementedError
  223. return ret
  224. @PositivePredicate.register(Basic)
  225. def _(expr, assumptions):
  226. if expr.is_number:
  227. return _PositivePredicate_number(expr, assumptions)
  228. @PositivePredicate.register(Mul)
  229. def _(expr, assumptions):
  230. if expr.is_number:
  231. return _PositivePredicate_number(expr, assumptions)
  232. result = True
  233. for arg in expr.args:
  234. if ask(Q.positive(arg), assumptions):
  235. continue
  236. elif ask(Q.negative(arg), assumptions):
  237. result = result ^ True
  238. else:
  239. return
  240. return result
  241. @PositivePredicate.register(Add)
  242. def _(expr, assumptions):
  243. if expr.is_number:
  244. return _PositivePredicate_number(expr, assumptions)
  245. r = ask(Q.real(expr), assumptions)
  246. if r is not True:
  247. return r
  248. nonneg = 0
  249. for arg in expr.args:
  250. if ask(Q.positive(arg), assumptions) is not True:
  251. if ask(Q.negative(arg), assumptions) is False:
  252. nonneg += 1
  253. else:
  254. break
  255. else:
  256. if nonneg < len(expr.args):
  257. return True
  258. @PositivePredicate.register(Pow)
  259. def _(expr, assumptions):
  260. if expr.base == E:
  261. if ask(Q.real(expr.exp), assumptions):
  262. return True
  263. if ask(Q.imaginary(expr.exp), assumptions):
  264. return ask(Q.even(expr.exp/(I*pi)), assumptions)
  265. return
  266. if expr.is_number:
  267. return _PositivePredicate_number(expr, assumptions)
  268. if ask(Q.positive(expr.base), assumptions):
  269. if ask(Q.real(expr.exp), assumptions):
  270. return True
  271. if ask(Q.negative(expr.base), assumptions):
  272. if ask(Q.even(expr.exp), assumptions):
  273. return True
  274. if ask(Q.odd(expr.exp), assumptions):
  275. return False
  276. @PositivePredicate.register(exp)
  277. def _(expr, assumptions):
  278. if ask(Q.real(expr.exp), assumptions):
  279. return True
  280. if ask(Q.imaginary(expr.exp), assumptions):
  281. return ask(Q.even(expr.exp/(I*pi)), assumptions)
  282. @PositivePredicate.register(log)
  283. def _(expr, assumptions):
  284. r = ask(Q.real(expr.args[0]), assumptions)
  285. if r is not True:
  286. return r
  287. if ask(Q.positive(expr.args[0] - 1), assumptions):
  288. return True
  289. if ask(Q.negative(expr.args[0] - 1), assumptions):
  290. return False
  291. @PositivePredicate.register(factorial)
  292. def _(expr, assumptions):
  293. x = expr.args[0]
  294. if ask(Q.integer(x) & Q.positive(x), assumptions):
  295. return True
  296. @PositivePredicate.register(ImaginaryUnit)
  297. def _(expr, assumptions):
  298. return False
  299. @PositivePredicate.register(Abs)
  300. def _(expr, assumptions):
  301. return ask(Q.nonzero(expr), assumptions)
  302. @PositivePredicate.register(Trace)
  303. def _(expr, assumptions):
  304. if ask(Q.positive_definite(expr.arg), assumptions):
  305. return True
  306. @PositivePredicate.register(Determinant)
  307. def _(expr, assumptions):
  308. if ask(Q.positive_definite(expr.arg), assumptions):
  309. return True
  310. @PositivePredicate.register(MatrixElement)
  311. def _(expr, assumptions):
  312. if (expr.i == expr.j
  313. and ask(Q.positive_definite(expr.parent), assumptions)):
  314. return True
  315. @PositivePredicate.register(atan)
  316. def _(expr, assumptions):
  317. return ask(Q.positive(expr.args[0]), assumptions)
  318. @PositivePredicate.register(asin)
  319. def _(expr, assumptions):
  320. x = expr.args[0]
  321. if ask(Q.positive(x) & Q.nonpositive(x - 1), assumptions):
  322. return True
  323. if ask(Q.negative(x) & Q.nonnegative(x + 1), assumptions):
  324. return False
  325. @PositivePredicate.register(acos)
  326. def _(expr, assumptions):
  327. x = expr.args[0]
  328. if ask(Q.nonpositive(x - 1) & Q.nonnegative(x + 1), assumptions):
  329. return True
  330. @PositivePredicate.register(acot)
  331. def _(expr, assumptions):
  332. return ask(Q.real(expr.args[0]), assumptions)
  333. @PositivePredicate.register(NaN)
  334. def _(expr, assumptions):
  335. return None
  336. # ExtendedNegativePredicate
  337. @ExtendedNegativePredicate.register(object)
  338. def _(expr, assumptions):
  339. return ask(Q.negative(expr) | Q.negative_infinite(expr), assumptions)
  340. # ExtendedPositivePredicate
  341. @ExtendedPositivePredicate.register(object)
  342. def _(expr, assumptions):
  343. return ask(Q.positive(expr) | Q.positive_infinite(expr), assumptions)
  344. # ExtendedNonZeroPredicate
  345. @ExtendedNonZeroPredicate.register(object)
  346. def _(expr, assumptions):
  347. return ask(
  348. Q.negative_infinite(expr) | Q.negative(expr) | Q.positive(expr) | Q.positive_infinite(expr),
  349. assumptions)
  350. # ExtendedNonPositivePredicate
  351. @ExtendedNonPositivePredicate.register(object)
  352. def _(expr, assumptions):
  353. return ask(
  354. Q.negative_infinite(expr) | Q.negative(expr) | Q.zero(expr),
  355. assumptions)
  356. # ExtendedNonNegativePredicate
  357. @ExtendedNonNegativePredicate.register(object)
  358. def _(expr, assumptions):
  359. return ask(
  360. Q.zero(expr) | Q.positive(expr) | Q.positive_infinite(expr),
  361. assumptions)