123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404 |
- from typing import Dict as tDict, Callable
- from sympy.core import S, Add, Expr, Basic, Mul, Pow, Rational
- from sympy.core.logic import fuzzy_not
- from sympy.logic.boolalg import Boolean
- from sympy.assumptions import ask, Q # type: ignore
- def refine(expr, assumptions=True):
- """
- Simplify an expression using assumptions.
- Explanation
- ===========
- Unlike :func:`~.simplify()` which performs structural simplification
- without any assumption, this function transforms the expression into
- the form which is only valid under certain assumptions. Note that
- ``simplify()`` is generally not done in refining process.
- Refining boolean expression involves reducing it to ``S.true`` or
- ``S.false``. Unlike :func:`~.ask()`, the expression will not be reduced
- if the truth value cannot be determined.
- Examples
- ========
- >>> from sympy import refine, sqrt, Q
- >>> from sympy.abc import x
- >>> refine(sqrt(x**2), Q.real(x))
- Abs(x)
- >>> refine(sqrt(x**2), Q.positive(x))
- x
- >>> refine(Q.real(x), Q.positive(x))
- True
- >>> refine(Q.positive(x), Q.real(x))
- Q.positive(x)
- See Also
- ========
- sympy.simplify.simplify.simplify : Structural simplification without assumptions.
- sympy.assumptions.ask.ask : Query for boolean expressions using assumptions.
- """
- if not isinstance(expr, Basic):
- return expr
- if not expr.is_Atom:
- args = [refine(arg, assumptions) for arg in expr.args]
- # TODO: this will probably not work with Integral or Polynomial
- expr = expr.func(*args)
- if hasattr(expr, '_eval_refine'):
- ref_expr = expr._eval_refine(assumptions)
- if ref_expr is not None:
- return ref_expr
- name = expr.__class__.__name__
- handler = handlers_dict.get(name, None)
- if handler is None:
- return expr
- new_expr = handler(expr, assumptions)
- if (new_expr is None) or (expr == new_expr):
- return expr
- if not isinstance(new_expr, Expr):
- return new_expr
- return refine(new_expr, assumptions)
- def refine_abs(expr, assumptions):
- """
- Handler for the absolute value.
- Examples
- ========
- >>> from sympy import Q, Abs
- >>> from sympy.assumptions.refine import refine_abs
- >>> from sympy.abc import x
- >>> refine_abs(Abs(x), Q.real(x))
- >>> refine_abs(Abs(x), Q.positive(x))
- x
- >>> refine_abs(Abs(x), Q.negative(x))
- -x
- """
- from sympy.functions.elementary.complexes import Abs
- arg = expr.args[0]
- if ask(Q.real(arg), assumptions) and \
- fuzzy_not(ask(Q.negative(arg), assumptions)):
- # if it's nonnegative
- return arg
- if ask(Q.negative(arg), assumptions):
- return -arg
- # arg is Mul
- if isinstance(arg, Mul):
- r = [refine(abs(a), assumptions) for a in arg.args]
- non_abs = []
- in_abs = []
- for i in r:
- if isinstance(i, Abs):
- in_abs.append(i.args[0])
- else:
- non_abs.append(i)
- return Mul(*non_abs) * Abs(Mul(*in_abs))
- def refine_Pow(expr, assumptions):
- """
- Handler for instances of Pow.
- Examples
- ========
- >>> from sympy import Q
- >>> from sympy.assumptions.refine import refine_Pow
- >>> from sympy.abc import x,y,z
- >>> refine_Pow((-1)**x, Q.real(x))
- >>> refine_Pow((-1)**x, Q.even(x))
- 1
- >>> refine_Pow((-1)**x, Q.odd(x))
- -1
- For powers of -1, even parts of the exponent can be simplified:
- >>> refine_Pow((-1)**(x+y), Q.even(x))
- (-1)**y
- >>> refine_Pow((-1)**(x+y+z), Q.odd(x) & Q.odd(z))
- (-1)**y
- >>> refine_Pow((-1)**(x+y+2), Q.odd(x))
- (-1)**(y + 1)
- >>> refine_Pow((-1)**(x+3), True)
- (-1)**(x + 1)
- """
- from sympy.functions.elementary.complexes import Abs
- from sympy.functions import sign
- if isinstance(expr.base, Abs):
- if ask(Q.real(expr.base.args[0]), assumptions) and \
- ask(Q.even(expr.exp), assumptions):
- return expr.base.args[0] ** expr.exp
- if ask(Q.real(expr.base), assumptions):
- if expr.base.is_number:
- if ask(Q.even(expr.exp), assumptions):
- return abs(expr.base) ** expr.exp
- if ask(Q.odd(expr.exp), assumptions):
- return sign(expr.base) * abs(expr.base) ** expr.exp
- if isinstance(expr.exp, Rational):
- if isinstance(expr.base, Pow):
- return abs(expr.base.base) ** (expr.base.exp * expr.exp)
- if expr.base is S.NegativeOne:
- if expr.exp.is_Add:
- old = expr
- # For powers of (-1) we can remove
- # - even terms
- # - pairs of odd terms
- # - a single odd term + 1
- # - A numerical constant N can be replaced with mod(N,2)
- coeff, terms = expr.exp.as_coeff_add()
- terms = set(terms)
- even_terms = set()
- odd_terms = set()
- initial_number_of_terms = len(terms)
- for t in terms:
- if ask(Q.even(t), assumptions):
- even_terms.add(t)
- elif ask(Q.odd(t), assumptions):
- odd_terms.add(t)
- terms -= even_terms
- if len(odd_terms) % 2:
- terms -= odd_terms
- new_coeff = (coeff + S.One) % 2
- else:
- terms -= odd_terms
- new_coeff = coeff % 2
- if new_coeff != coeff or len(terms) < initial_number_of_terms:
- terms.add(new_coeff)
- expr = expr.base**(Add(*terms))
- # Handle (-1)**((-1)**n/2 + m/2)
- e2 = 2*expr.exp
- if ask(Q.even(e2), assumptions):
- if e2.could_extract_minus_sign():
- e2 *= expr.base
- if e2.is_Add:
- i, p = e2.as_two_terms()
- if p.is_Pow and p.base is S.NegativeOne:
- if ask(Q.integer(p.exp), assumptions):
- i = (i + 1)/2
- if ask(Q.even(i), assumptions):
- return expr.base**p.exp
- elif ask(Q.odd(i), assumptions):
- return expr.base**(p.exp + 1)
- else:
- return expr.base**(p.exp + i)
- if old != expr:
- return expr
- def refine_atan2(expr, assumptions):
- """
- Handler for the atan2 function.
- Examples
- ========
- >>> from sympy import Q, atan2
- >>> from sympy.assumptions.refine import refine_atan2
- >>> from sympy.abc import x, y
- >>> refine_atan2(atan2(y,x), Q.real(y) & Q.positive(x))
- atan(y/x)
- >>> refine_atan2(atan2(y,x), Q.negative(y) & Q.negative(x))
- atan(y/x) - pi
- >>> refine_atan2(atan2(y,x), Q.positive(y) & Q.negative(x))
- atan(y/x) + pi
- >>> refine_atan2(atan2(y,x), Q.zero(y) & Q.negative(x))
- pi
- >>> refine_atan2(atan2(y,x), Q.positive(y) & Q.zero(x))
- pi/2
- >>> refine_atan2(atan2(y,x), Q.negative(y) & Q.zero(x))
- -pi/2
- >>> refine_atan2(atan2(y,x), Q.zero(y) & Q.zero(x))
- nan
- """
- from sympy.functions.elementary.trigonometric import atan
- y, x = expr.args
- if ask(Q.real(y) & Q.positive(x), assumptions):
- return atan(y / x)
- elif ask(Q.negative(y) & Q.negative(x), assumptions):
- return atan(y / x) - S.Pi
- elif ask(Q.positive(y) & Q.negative(x), assumptions):
- return atan(y / x) + S.Pi
- elif ask(Q.zero(y) & Q.negative(x), assumptions):
- return S.Pi
- elif ask(Q.positive(y) & Q.zero(x), assumptions):
- return S.Pi/2
- elif ask(Q.negative(y) & Q.zero(x), assumptions):
- return -S.Pi/2
- elif ask(Q.zero(y) & Q.zero(x), assumptions):
- return S.NaN
- else:
- return expr
- def refine_re(expr, assumptions):
- """
- Handler for real part.
- Examples
- ========
- >>> from sympy.assumptions.refine import refine_re
- >>> from sympy import Q, re
- >>> from sympy.abc import x
- >>> refine_re(re(x), Q.real(x))
- x
- >>> refine_re(re(x), Q.imaginary(x))
- 0
- """
- arg = expr.args[0]
- if ask(Q.real(arg), assumptions):
- return arg
- if ask(Q.imaginary(arg), assumptions):
- return S.Zero
- return _refine_reim(expr, assumptions)
- def refine_im(expr, assumptions):
- """
- Handler for imaginary part.
- Explanation
- ===========
- >>> from sympy.assumptions.refine import refine_im
- >>> from sympy import Q, im
- >>> from sympy.abc import x
- >>> refine_im(im(x), Q.real(x))
- 0
- >>> refine_im(im(x), Q.imaginary(x))
- -I*x
- """
- arg = expr.args[0]
- if ask(Q.real(arg), assumptions):
- return S.Zero
- if ask(Q.imaginary(arg), assumptions):
- return - S.ImaginaryUnit * arg
- return _refine_reim(expr, assumptions)
- def refine_arg(expr, assumptions):
- """
- Handler for complex argument
- Explanation
- ===========
- >>> from sympy.assumptions.refine import refine_arg
- >>> from sympy import Q, arg
- >>> from sympy.abc import x
- >>> refine_arg(arg(x), Q.positive(x))
- 0
- >>> refine_arg(arg(x), Q.negative(x))
- pi
- """
- rg = expr.args[0]
- if ask(Q.positive(rg), assumptions):
- return S.Zero
- if ask(Q.negative(rg), assumptions):
- return S.Pi
- return None
- def _refine_reim(expr, assumptions):
- # Helper function for refine_re & refine_im
- expanded = expr.expand(complex = True)
- if expanded != expr:
- refined = refine(expanded, assumptions)
- if refined != expanded:
- return refined
- # Best to leave the expression as is
- return None
- def refine_sign(expr, assumptions):
- """
- Handler for sign.
- Examples
- ========
- >>> from sympy.assumptions.refine import refine_sign
- >>> from sympy import Symbol, Q, sign, im
- >>> x = Symbol('x', real = True)
- >>> expr = sign(x)
- >>> refine_sign(expr, Q.positive(x) & Q.nonzero(x))
- 1
- >>> refine_sign(expr, Q.negative(x) & Q.nonzero(x))
- -1
- >>> refine_sign(expr, Q.zero(x))
- 0
- >>> y = Symbol('y', imaginary = True)
- >>> expr = sign(y)
- >>> refine_sign(expr, Q.positive(im(y)))
- I
- >>> refine_sign(expr, Q.negative(im(y)))
- -I
- """
- arg = expr.args[0]
- if ask(Q.zero(arg), assumptions):
- return S.Zero
- if ask(Q.real(arg)):
- if ask(Q.positive(arg), assumptions):
- return S.One
- if ask(Q.negative(arg), assumptions):
- return S.NegativeOne
- if ask(Q.imaginary(arg)):
- arg_re, arg_im = arg.as_real_imag()
- if ask(Q.positive(arg_im), assumptions):
- return S.ImaginaryUnit
- if ask(Q.negative(arg_im), assumptions):
- return -S.ImaginaryUnit
- return expr
- def refine_matrixelement(expr, assumptions):
- """
- Handler for symmetric part.
- Examples
- ========
- >>> from sympy.assumptions.refine import refine_matrixelement
- >>> from sympy import MatrixSymbol, Q
- >>> X = MatrixSymbol('X', 3, 3)
- >>> refine_matrixelement(X[0, 1], Q.symmetric(X))
- X[0, 1]
- >>> refine_matrixelement(X[1, 0], Q.symmetric(X))
- X[0, 1]
- """
- from sympy.matrices.expressions.matexpr import MatrixElement
- matrix, i, j = expr.args
- if ask(Q.symmetric(matrix), assumptions):
- if (i - j).could_extract_minus_sign():
- return expr
- return MatrixElement(matrix, j, i)
- handlers_dict = {
- 'Abs': refine_abs,
- 'Pow': refine_Pow,
- 'atan2': refine_atan2,
- 're': refine_re,
- 'im': refine_im,
- 'arg': refine_arg,
- 'sign': refine_sign,
- 'MatrixElement': refine_matrixelement
- } # type: tDict[str, Callable[[Expr, Boolean], Expr]]
|