order.py 12 KB

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