图片解析应用
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

404 lines
12 KiB

  1. from typing import Dict as tDict, Callable
  2. from sympy.core import S, Add, Expr, Basic, Mul, Pow, Rational
  3. from sympy.core.logic import fuzzy_not
  4. from sympy.logic.boolalg import Boolean
  5. from sympy.assumptions import ask, Q # type: ignore
  6. def refine(expr, assumptions=True):
  7. """
  8. Simplify an expression using assumptions.
  9. Explanation
  10. ===========
  11. Unlike :func:`~.simplify()` which performs structural simplification
  12. without any assumption, this function transforms the expression into
  13. the form which is only valid under certain assumptions. Note that
  14. ``simplify()`` is generally not done in refining process.
  15. Refining boolean expression involves reducing it to ``S.true`` or
  16. ``S.false``. Unlike :func:`~.ask()`, the expression will not be reduced
  17. if the truth value cannot be determined.
  18. Examples
  19. ========
  20. >>> from sympy import refine, sqrt, Q
  21. >>> from sympy.abc import x
  22. >>> refine(sqrt(x**2), Q.real(x))
  23. Abs(x)
  24. >>> refine(sqrt(x**2), Q.positive(x))
  25. x
  26. >>> refine(Q.real(x), Q.positive(x))
  27. True
  28. >>> refine(Q.positive(x), Q.real(x))
  29. Q.positive(x)
  30. See Also
  31. ========
  32. sympy.simplify.simplify.simplify : Structural simplification without assumptions.
  33. sympy.assumptions.ask.ask : Query for boolean expressions using assumptions.
  34. """
  35. if not isinstance(expr, Basic):
  36. return expr
  37. if not expr.is_Atom:
  38. args = [refine(arg, assumptions) for arg in expr.args]
  39. # TODO: this will probably not work with Integral or Polynomial
  40. expr = expr.func(*args)
  41. if hasattr(expr, '_eval_refine'):
  42. ref_expr = expr._eval_refine(assumptions)
  43. if ref_expr is not None:
  44. return ref_expr
  45. name = expr.__class__.__name__
  46. handler = handlers_dict.get(name, None)
  47. if handler is None:
  48. return expr
  49. new_expr = handler(expr, assumptions)
  50. if (new_expr is None) or (expr == new_expr):
  51. return expr
  52. if not isinstance(new_expr, Expr):
  53. return new_expr
  54. return refine(new_expr, assumptions)
  55. def refine_abs(expr, assumptions):
  56. """
  57. Handler for the absolute value.
  58. Examples
  59. ========
  60. >>> from sympy import Q, Abs
  61. >>> from sympy.assumptions.refine import refine_abs
  62. >>> from sympy.abc import x
  63. >>> refine_abs(Abs(x), Q.real(x))
  64. >>> refine_abs(Abs(x), Q.positive(x))
  65. x
  66. >>> refine_abs(Abs(x), Q.negative(x))
  67. -x
  68. """
  69. from sympy.functions.elementary.complexes import Abs
  70. arg = expr.args[0]
  71. if ask(Q.real(arg), assumptions) and \
  72. fuzzy_not(ask(Q.negative(arg), assumptions)):
  73. # if it's nonnegative
  74. return arg
  75. if ask(Q.negative(arg), assumptions):
  76. return -arg
  77. # arg is Mul
  78. if isinstance(arg, Mul):
  79. r = [refine(abs(a), assumptions) for a in arg.args]
  80. non_abs = []
  81. in_abs = []
  82. for i in r:
  83. if isinstance(i, Abs):
  84. in_abs.append(i.args[0])
  85. else:
  86. non_abs.append(i)
  87. return Mul(*non_abs) * Abs(Mul(*in_abs))
  88. def refine_Pow(expr, assumptions):
  89. """
  90. Handler for instances of Pow.
  91. Examples
  92. ========
  93. >>> from sympy import Q
  94. >>> from sympy.assumptions.refine import refine_Pow
  95. >>> from sympy.abc import x,y,z
  96. >>> refine_Pow((-1)**x, Q.real(x))
  97. >>> refine_Pow((-1)**x, Q.even(x))
  98. 1
  99. >>> refine_Pow((-1)**x, Q.odd(x))
  100. -1
  101. For powers of -1, even parts of the exponent can be simplified:
  102. >>> refine_Pow((-1)**(x+y), Q.even(x))
  103. (-1)**y
  104. >>> refine_Pow((-1)**(x+y+z), Q.odd(x) & Q.odd(z))
  105. (-1)**y
  106. >>> refine_Pow((-1)**(x+y+2), Q.odd(x))
  107. (-1)**(y + 1)
  108. >>> refine_Pow((-1)**(x+3), True)
  109. (-1)**(x + 1)
  110. """
  111. from sympy.functions.elementary.complexes import Abs
  112. from sympy.functions import sign
  113. if isinstance(expr.base, Abs):
  114. if ask(Q.real(expr.base.args[0]), assumptions) and \
  115. ask(Q.even(expr.exp), assumptions):
  116. return expr.base.args[0] ** expr.exp
  117. if ask(Q.real(expr.base), assumptions):
  118. if expr.base.is_number:
  119. if ask(Q.even(expr.exp), assumptions):
  120. return abs(expr.base) ** expr.exp
  121. if ask(Q.odd(expr.exp), assumptions):
  122. return sign(expr.base) * abs(expr.base) ** expr.exp
  123. if isinstance(expr.exp, Rational):
  124. if isinstance(expr.base, Pow):
  125. return abs(expr.base.base) ** (expr.base.exp * expr.exp)
  126. if expr.base is S.NegativeOne:
  127. if expr.exp.is_Add:
  128. old = expr
  129. # For powers of (-1) we can remove
  130. # - even terms
  131. # - pairs of odd terms
  132. # - a single odd term + 1
  133. # - A numerical constant N can be replaced with mod(N,2)
  134. coeff, terms = expr.exp.as_coeff_add()
  135. terms = set(terms)
  136. even_terms = set()
  137. odd_terms = set()
  138. initial_number_of_terms = len(terms)
  139. for t in terms:
  140. if ask(Q.even(t), assumptions):
  141. even_terms.add(t)
  142. elif ask(Q.odd(t), assumptions):
  143. odd_terms.add(t)
  144. terms -= even_terms
  145. if len(odd_terms) % 2:
  146. terms -= odd_terms
  147. new_coeff = (coeff + S.One) % 2
  148. else:
  149. terms -= odd_terms
  150. new_coeff = coeff % 2
  151. if new_coeff != coeff or len(terms) < initial_number_of_terms:
  152. terms.add(new_coeff)
  153. expr = expr.base**(Add(*terms))
  154. # Handle (-1)**((-1)**n/2 + m/2)
  155. e2 = 2*expr.exp
  156. if ask(Q.even(e2), assumptions):
  157. if e2.could_extract_minus_sign():
  158. e2 *= expr.base
  159. if e2.is_Add:
  160. i, p = e2.as_two_terms()
  161. if p.is_Pow and p.base is S.NegativeOne:
  162. if ask(Q.integer(p.exp), assumptions):
  163. i = (i + 1)/2
  164. if ask(Q.even(i), assumptions):
  165. return expr.base**p.exp
  166. elif ask(Q.odd(i), assumptions):
  167. return expr.base**(p.exp + 1)
  168. else:
  169. return expr.base**(p.exp + i)
  170. if old != expr:
  171. return expr
  172. def refine_atan2(expr, assumptions):
  173. """
  174. Handler for the atan2 function.
  175. Examples
  176. ========
  177. >>> from sympy import Q, atan2
  178. >>> from sympy.assumptions.refine import refine_atan2
  179. >>> from sympy.abc import x, y
  180. >>> refine_atan2(atan2(y,x), Q.real(y) & Q.positive(x))
  181. atan(y/x)
  182. >>> refine_atan2(atan2(y,x), Q.negative(y) & Q.negative(x))
  183. atan(y/x) - pi
  184. >>> refine_atan2(atan2(y,x), Q.positive(y) & Q.negative(x))
  185. atan(y/x) + pi
  186. >>> refine_atan2(atan2(y,x), Q.zero(y) & Q.negative(x))
  187. pi
  188. >>> refine_atan2(atan2(y,x), Q.positive(y) & Q.zero(x))
  189. pi/2
  190. >>> refine_atan2(atan2(y,x), Q.negative(y) & Q.zero(x))
  191. -pi/2
  192. >>> refine_atan2(atan2(y,x), Q.zero(y) & Q.zero(x))
  193. nan
  194. """
  195. from sympy.functions.elementary.trigonometric import atan
  196. y, x = expr.args
  197. if ask(Q.real(y) & Q.positive(x), assumptions):
  198. return atan(y / x)
  199. elif ask(Q.negative(y) & Q.negative(x), assumptions):
  200. return atan(y / x) - S.Pi
  201. elif ask(Q.positive(y) & Q.negative(x), assumptions):
  202. return atan(y / x) + S.Pi
  203. elif ask(Q.zero(y) & Q.negative(x), assumptions):
  204. return S.Pi
  205. elif ask(Q.positive(y) & Q.zero(x), assumptions):
  206. return S.Pi/2
  207. elif ask(Q.negative(y) & Q.zero(x), assumptions):
  208. return -S.Pi/2
  209. elif ask(Q.zero(y) & Q.zero(x), assumptions):
  210. return S.NaN
  211. else:
  212. return expr
  213. def refine_re(expr, assumptions):
  214. """
  215. Handler for real part.
  216. Examples
  217. ========
  218. >>> from sympy.assumptions.refine import refine_re
  219. >>> from sympy import Q, re
  220. >>> from sympy.abc import x
  221. >>> refine_re(re(x), Q.real(x))
  222. x
  223. >>> refine_re(re(x), Q.imaginary(x))
  224. 0
  225. """
  226. arg = expr.args[0]
  227. if ask(Q.real(arg), assumptions):
  228. return arg
  229. if ask(Q.imaginary(arg), assumptions):
  230. return S.Zero
  231. return _refine_reim(expr, assumptions)
  232. def refine_im(expr, assumptions):
  233. """
  234. Handler for imaginary part.
  235. Explanation
  236. ===========
  237. >>> from sympy.assumptions.refine import refine_im
  238. >>> from sympy import Q, im
  239. >>> from sympy.abc import x
  240. >>> refine_im(im(x), Q.real(x))
  241. 0
  242. >>> refine_im(im(x), Q.imaginary(x))
  243. -I*x
  244. """
  245. arg = expr.args[0]
  246. if ask(Q.real(arg), assumptions):
  247. return S.Zero
  248. if ask(Q.imaginary(arg), assumptions):
  249. return - S.ImaginaryUnit * arg
  250. return _refine_reim(expr, assumptions)
  251. def refine_arg(expr, assumptions):
  252. """
  253. Handler for complex argument
  254. Explanation
  255. ===========
  256. >>> from sympy.assumptions.refine import refine_arg
  257. >>> from sympy import Q, arg
  258. >>> from sympy.abc import x
  259. >>> refine_arg(arg(x), Q.positive(x))
  260. 0
  261. >>> refine_arg(arg(x), Q.negative(x))
  262. pi
  263. """
  264. rg = expr.args[0]
  265. if ask(Q.positive(rg), assumptions):
  266. return S.Zero
  267. if ask(Q.negative(rg), assumptions):
  268. return S.Pi
  269. return None
  270. def _refine_reim(expr, assumptions):
  271. # Helper function for refine_re & refine_im
  272. expanded = expr.expand(complex = True)
  273. if expanded != expr:
  274. refined = refine(expanded, assumptions)
  275. if refined != expanded:
  276. return refined
  277. # Best to leave the expression as is
  278. return None
  279. def refine_sign(expr, assumptions):
  280. """
  281. Handler for sign.
  282. Examples
  283. ========
  284. >>> from sympy.assumptions.refine import refine_sign
  285. >>> from sympy import Symbol, Q, sign, im
  286. >>> x = Symbol('x', real = True)
  287. >>> expr = sign(x)
  288. >>> refine_sign(expr, Q.positive(x) & Q.nonzero(x))
  289. 1
  290. >>> refine_sign(expr, Q.negative(x) & Q.nonzero(x))
  291. -1
  292. >>> refine_sign(expr, Q.zero(x))
  293. 0
  294. >>> y = Symbol('y', imaginary = True)
  295. >>> expr = sign(y)
  296. >>> refine_sign(expr, Q.positive(im(y)))
  297. I
  298. >>> refine_sign(expr, Q.negative(im(y)))
  299. -I
  300. """
  301. arg = expr.args[0]
  302. if ask(Q.zero(arg), assumptions):
  303. return S.Zero
  304. if ask(Q.real(arg)):
  305. if ask(Q.positive(arg), assumptions):
  306. return S.One
  307. if ask(Q.negative(arg), assumptions):
  308. return S.NegativeOne
  309. if ask(Q.imaginary(arg)):
  310. arg_re, arg_im = arg.as_real_imag()
  311. if ask(Q.positive(arg_im), assumptions):
  312. return S.ImaginaryUnit
  313. if ask(Q.negative(arg_im), assumptions):
  314. return -S.ImaginaryUnit
  315. return expr
  316. def refine_matrixelement(expr, assumptions):
  317. """
  318. Handler for symmetric part.
  319. Examples
  320. ========
  321. >>> from sympy.assumptions.refine import refine_matrixelement
  322. >>> from sympy import MatrixSymbol, Q
  323. >>> X = MatrixSymbol('X', 3, 3)
  324. >>> refine_matrixelement(X[0, 1], Q.symmetric(X))
  325. X[0, 1]
  326. >>> refine_matrixelement(X[1, 0], Q.symmetric(X))
  327. X[0, 1]
  328. """
  329. from sympy.matrices.expressions.matexpr import MatrixElement
  330. matrix, i, j = expr.args
  331. if ask(Q.symmetric(matrix), assumptions):
  332. if (i - j).could_extract_minus_sign():
  333. return expr
  334. return MatrixElement(matrix, j, i)
  335. handlers_dict = {
  336. 'Abs': refine_abs,
  337. 'Pow': refine_Pow,
  338. 'atan2': refine_atan2,
  339. 're': refine_re,
  340. 'im': refine_im,
  341. 'arg': refine_arg,
  342. 'sign': refine_sign,
  343. 'MatrixElement': refine_matrixelement
  344. } # type: tDict[str, Callable[[Expr, Boolean], Expr]]