|
|
"""
Handlers for predicates related to set membership: integer, rational, etc. """
from sympy.assumptions import Q, ask from sympy.core import Add, Basic, Expr, Mul, Pow, S from sympy.core.numbers import (AlgebraicNumber, ComplexInfinity, Exp1, Float, GoldenRatio, ImaginaryUnit, Infinity, Integer, NaN, NegativeInfinity, Number, NumberSymbol, Pi, pi, Rational, TribonacciConstant, E) from sympy.core.logic import fuzzy_bool from sympy.functions import (Abs, acos, acot, asin, atan, cos, cot, exp, im, log, re, sin, tan) from sympy.core.numbers import I from sympy.core.relational import Eq from sympy.functions.elementary.complexes import conjugate from sympy.matrices import Determinant, MatrixBase, Trace from sympy.matrices.expressions.matexpr import MatrixElement
from sympy.multipledispatch import MDNotImplementedError
from .common import test_closed_group from ..predicates.sets import (IntegerPredicate, RationalPredicate, IrrationalPredicate, RealPredicate, ExtendedRealPredicate, HermitianPredicate, ComplexPredicate, ImaginaryPredicate, AntihermitianPredicate, AlgebraicPredicate)
# IntegerPredicate
def _IntegerPredicate_number(expr, assumptions): # helper function try: i = int(expr.round()) if not (expr - i).equals(0): raise TypeError return True except TypeError: return False
@IntegerPredicate.register_many(int, Integer) # type:ignore def _(expr, assumptions): return True
@IntegerPredicate.register_many(Exp1, GoldenRatio, ImaginaryUnit, Infinity, NegativeInfinity, Pi, Rational, TribonacciConstant) def _(expr, assumptions): return False
@IntegerPredicate.register(Expr) def _(expr, assumptions): ret = expr.is_integer if ret is None: raise MDNotImplementedError return ret
@IntegerPredicate.register_many(Add, Pow) def _(expr, assumptions): """
* Integer + Integer -> Integer * Integer + !Integer -> !Integer * !Integer + !Integer -> ? """
if expr.is_number: return _IntegerPredicate_number(expr, assumptions) return test_closed_group(expr, assumptions, Q.integer)
@IntegerPredicate.register(Mul) def _(expr, assumptions): """
* Integer*Integer -> Integer * Integer*Irrational -> !Integer * Odd/Even -> !Integer * Integer*Rational -> ? """
if expr.is_number: return _IntegerPredicate_number(expr, assumptions) _output = True for arg in expr.args: if not ask(Q.integer(arg), assumptions): if arg.is_Rational: if arg.q == 2: return ask(Q.even(2*expr), assumptions) if ~(arg.q & 1): return None elif ask(Q.irrational(arg), assumptions): if _output: _output = False else: return else: return
return _output
@IntegerPredicate.register(Abs) def _(expr, assumptions): return ask(Q.integer(expr.args[0]), assumptions)
@IntegerPredicate.register_many(Determinant, MatrixElement, Trace) def _(expr, assumptions): return ask(Q.integer_elements(expr.args[0]), assumptions)
# RationalPredicate
@RationalPredicate.register(Rational) def _(expr, assumptions): return True
@RationalPredicate.register(Float) def _(expr, assumptions): return None
@RationalPredicate.register_many(Exp1, GoldenRatio, ImaginaryUnit, Infinity, NegativeInfinity, Pi, TribonacciConstant) def _(expr, assumptions): return False
@RationalPredicate.register(Expr) def _(expr, assumptions): ret = expr.is_rational if ret is None: raise MDNotImplementedError return ret
@RationalPredicate.register_many(Add, Mul) def _(expr, assumptions): """
* Rational + Rational -> Rational * Rational + !Rational -> !Rational * !Rational + !Rational -> ? """
if expr.is_number: if expr.as_real_imag()[1]: return False return test_closed_group(expr, assumptions, Q.rational)
@RationalPredicate.register(Pow) def _(expr, assumptions): """
* Rational ** Integer -> Rational * Irrational ** Rational -> Irrational * Rational ** Irrational -> ? """
if expr.base == E: x = expr.exp if ask(Q.rational(x), assumptions): return ask(~Q.nonzero(x), assumptions) return
if ask(Q.integer(expr.exp), assumptions): return ask(Q.rational(expr.base), assumptions) elif ask(Q.rational(expr.exp), assumptions): if ask(Q.prime(expr.base), assumptions): return False
@RationalPredicate.register_many(asin, atan, cos, sin, tan) def _(expr, assumptions): x = expr.args[0] if ask(Q.rational(x), assumptions): return ask(~Q.nonzero(x), assumptions)
@RationalPredicate.register(exp) def _(expr, assumptions): x = expr.exp if ask(Q.rational(x), assumptions): return ask(~Q.nonzero(x), assumptions)
@RationalPredicate.register_many(acot, cot) def _(expr, assumptions): x = expr.args[0] if ask(Q.rational(x), assumptions): return False
@RationalPredicate.register_many(acos, log) def _(expr, assumptions): x = expr.args[0] if ask(Q.rational(x), assumptions): return ask(~Q.nonzero(x - 1), assumptions)
# IrrationalPredicate
@IrrationalPredicate.register(Expr) def _(expr, assumptions): ret = expr.is_irrational if ret is None: raise MDNotImplementedError return ret
@IrrationalPredicate.register(Basic) def _(expr, assumptions): _real = ask(Q.real(expr), assumptions) if _real: _rational = ask(Q.rational(expr), assumptions) if _rational is None: return None return not _rational else: return _real
# RealPredicate
def _RealPredicate_number(expr, assumptions): # let as_real_imag() work first since the expression may # be simpler to evaluate i = expr.as_real_imag()[1].evalf(2) if i._prec != 1: return not i # allow None to be returned if we couldn't show for sure # that i was 0
@RealPredicate.register_many(Abs, Exp1, Float, GoldenRatio, im, Pi, Rational, re, TribonacciConstant) def _(expr, assumptions): return True
@RealPredicate.register_many(ImaginaryUnit, Infinity, NegativeInfinity) def _(expr, assumptions): return False
@RealPredicate.register(Expr) def _(expr, assumptions): ret = expr.is_real if ret is None: raise MDNotImplementedError return ret
@RealPredicate.register(Add) def _(expr, assumptions): """
* Real + Real -> Real * Real + (Complex & !Real) -> !Real """
if expr.is_number: return _RealPredicate_number(expr, assumptions) return test_closed_group(expr, assumptions, Q.real)
@RealPredicate.register(Mul) def _(expr, assumptions): """
* Real*Real -> Real * Real*Imaginary -> !Real * Imaginary*Imaginary -> Real """
if expr.is_number: return _RealPredicate_number(expr, assumptions) result = True for arg in expr.args: if ask(Q.real(arg), assumptions): pass elif ask(Q.imaginary(arg), assumptions): result = result ^ True else: break else: return result
@RealPredicate.register(Pow) def _(expr, assumptions): """
* Real**Integer -> Real * Positive**Real -> Real * Real**(Integer/Even) -> Real if base is nonnegative * Real**(Integer/Odd) -> Real * Imaginary**(Integer/Even) -> Real * Imaginary**(Integer/Odd) -> not Real * Imaginary**Real -> ? since Real could be 0 (giving real) or 1 (giving imaginary) * b**Imaginary -> Real if log(b) is imaginary and b != 0 and exponent != integer multiple of I*pi/log(b) * Real**Real -> ? e.g. sqrt(-1) is imaginary and sqrt(2) is not """
if expr.is_number: return _RealPredicate_number(expr, assumptions)
if expr.base == E: return ask( Q.integer(expr.exp/I/pi) | Q.real(expr.exp), assumptions )
if expr.base.func == exp or (expr.base.is_Pow and expr.base.base == E): if ask(Q.imaginary(expr.base.exp), assumptions): if ask(Q.imaginary(expr.exp), assumptions): return True # If the i = (exp's arg)/(I*pi) is an integer or half-integer # multiple of I*pi then 2*i will be an integer. In addition, # exp(i*I*pi) = (-1)**i so the overall realness of the expr # can be determined by replacing exp(i*I*pi) with (-1)**i. i = expr.base.exp/I/pi if ask(Q.integer(2*i), assumptions): return ask(Q.real((S.NegativeOne**i)**expr.exp), assumptions) return
if ask(Q.imaginary(expr.base), assumptions): if ask(Q.integer(expr.exp), assumptions): odd = ask(Q.odd(expr.exp), assumptions) if odd is not None: return not odd return
if ask(Q.imaginary(expr.exp), assumptions): imlog = ask(Q.imaginary(log(expr.base)), assumptions) if imlog is not None: # I**i -> real, log(I) is imag; # (2*I)**i -> complex, log(2*I) is not imag return imlog
if ask(Q.real(expr.base), assumptions): if ask(Q.real(expr.exp), assumptions): if expr.exp.is_Rational and \ ask(Q.even(expr.exp.q), assumptions): return ask(Q.positive(expr.base), assumptions) elif ask(Q.integer(expr.exp), assumptions): return True elif ask(Q.positive(expr.base), assumptions): return True elif ask(Q.negative(expr.base), assumptions): return False
@RealPredicate.register_many(cos, sin) def _(expr, assumptions): if ask(Q.real(expr.args[0]), assumptions): return True
@RealPredicate.register(exp) def _(expr, assumptions): return ask( Q.integer(expr.exp/I/pi) | Q.real(expr.exp), assumptions )
@RealPredicate.register(log) def _(expr, assumptions): return ask(Q.positive(expr.args[0]), assumptions)
@RealPredicate.register_many(Determinant, MatrixElement, Trace) def _(expr, assumptions): return ask(Q.real_elements(expr.args[0]), assumptions)
# ExtendedRealPredicate
@ExtendedRealPredicate.register(object) def _(expr, assumptions): return ask(Q.negative_infinite(expr) | Q.negative(expr) | Q.zero(expr) | Q.positive(expr) | Q.positive_infinite(expr), assumptions)
@ExtendedRealPredicate.register_many(Infinity, NegativeInfinity) def _(expr, assumptions): return True
@ExtendedRealPredicate.register_many(Add, Mul, Pow) # type:ignore def _(expr, assumptions): return test_closed_group(expr, assumptions, Q.extended_real)
# HermitianPredicate
@HermitianPredicate.register(object) # type:ignore def _(expr, assumptions): if isinstance(expr, MatrixBase): return None return ask(Q.real(expr), assumptions)
@HermitianPredicate.register(Add) # type:ignore def _(expr, assumptions): """
* Hermitian + Hermitian -> Hermitian * Hermitian + !Hermitian -> !Hermitian """
if expr.is_number: raise MDNotImplementedError return test_closed_group(expr, assumptions, Q.hermitian)
@HermitianPredicate.register(Mul) # type:ignore def _(expr, assumptions): """
As long as there is at most only one noncommutative term:
* Hermitian*Hermitian -> Hermitian * Hermitian*Antihermitian -> !Hermitian * Antihermitian*Antihermitian -> Hermitian """
if expr.is_number: raise MDNotImplementedError nccount = 0 result = True for arg in expr.args: if ask(Q.antihermitian(arg), assumptions): result = result ^ True elif not ask(Q.hermitian(arg), assumptions): break if ask(~Q.commutative(arg), assumptions): nccount += 1 if nccount > 1: break else: return result
@HermitianPredicate.register(Pow) # type:ignore def _(expr, assumptions): """
* Hermitian**Integer -> Hermitian """
if expr.is_number: raise MDNotImplementedError if expr.base == E: if ask(Q.hermitian(expr.exp), assumptions): return True raise MDNotImplementedError if ask(Q.hermitian(expr.base), assumptions): if ask(Q.integer(expr.exp), assumptions): return True raise MDNotImplementedError
@HermitianPredicate.register_many(cos, sin) # type:ignore def _(expr, assumptions): if ask(Q.hermitian(expr.args[0]), assumptions): return True raise MDNotImplementedError
@HermitianPredicate.register(exp) # type:ignore def _(expr, assumptions): if ask(Q.hermitian(expr.exp), assumptions): return True raise MDNotImplementedError
@HermitianPredicate.register(MatrixBase) # type:ignore def _(mat, assumptions): rows, cols = mat.shape ret_val = True for i in range(rows): for j in range(i, cols): cond = fuzzy_bool(Eq(mat[i, j], conjugate(mat[j, i]))) if cond is None: ret_val = None if cond == False: return False if ret_val is None: raise MDNotImplementedError return ret_val
# ComplexPredicate
@ComplexPredicate.register_many(Abs, cos, exp, im, ImaginaryUnit, log, Number, # type:ignore NumberSymbol, re, sin) def _(expr, assumptions): return True
@ComplexPredicate.register_many(Infinity, NegativeInfinity) # type:ignore def _(expr, assumptions): return False
@ComplexPredicate.register(Expr) # type:ignore def _(expr, assumptions): ret = expr.is_complex if ret is None: raise MDNotImplementedError return ret
@ComplexPredicate.register_many(Add, Mul) # type:ignore def _(expr, assumptions): return test_closed_group(expr, assumptions, Q.complex)
@ComplexPredicate.register(Pow) # type:ignore def _(expr, assumptions): if expr.base == E: return True return test_closed_group(expr, assumptions, Q.complex)
@ComplexPredicate.register_many(Determinant, MatrixElement, Trace) # type:ignore def _(expr, assumptions): return ask(Q.complex_elements(expr.args[0]), assumptions)
@ComplexPredicate.register(NaN) # type:ignore def _(expr, assumptions): return None
# ImaginaryPredicate
def _Imaginary_number(expr, assumptions): # let as_real_imag() work first since the expression may # be simpler to evaluate r = expr.as_real_imag()[0].evalf(2) if r._prec != 1: return not r # allow None to be returned if we couldn't show for sure # that r was 0
@ImaginaryPredicate.register(ImaginaryUnit) # type:ignore def _(expr, assumptions): return True
@ImaginaryPredicate.register(Expr) # type:ignore def _(expr, assumptions): ret = expr.is_imaginary if ret is None: raise MDNotImplementedError return ret
@ImaginaryPredicate.register(Add) # type:ignore def _(expr, assumptions): """
* Imaginary + Imaginary -> Imaginary * Imaginary + Complex -> ? * Imaginary + Real -> !Imaginary """
if expr.is_number: return _Imaginary_number(expr, assumptions)
reals = 0 for arg in expr.args: if ask(Q.imaginary(arg), assumptions): pass elif ask(Q.real(arg), assumptions): reals += 1 else: break else: if reals == 0: return True if reals in (1, len(expr.args)): # two reals could sum 0 thus giving an imaginary return False
@ImaginaryPredicate.register(Mul) # type:ignore def _(expr, assumptions): """
* Real*Imaginary -> Imaginary * Imaginary*Imaginary -> Real """
if expr.is_number: return _Imaginary_number(expr, assumptions) result = False reals = 0 for arg in expr.args: if ask(Q.imaginary(arg), assumptions): result = result ^ True elif not ask(Q.real(arg), assumptions): break else: if reals == len(expr.args): return False return result
@ImaginaryPredicate.register(Pow) # type:ignore def _(expr, assumptions): """
* Imaginary**Odd -> Imaginary * Imaginary**Even -> Real * b**Imaginary -> !Imaginary if exponent is an integer multiple of I*pi/log(b) * Imaginary**Real -> ? * Positive**Real -> Real * Negative**Integer -> Real * Negative**(Integer/2) -> Imaginary * Negative**Real -> not Imaginary if exponent is not Rational """
if expr.is_number: return _Imaginary_number(expr, assumptions)
if expr.base == E: a = expr.exp/I/pi return ask(Q.integer(2*a) & ~Q.integer(a), assumptions)
if expr.base.func == exp or (expr.base.is_Pow and expr.base.base == E): if ask(Q.imaginary(expr.base.exp), assumptions): if ask(Q.imaginary(expr.exp), assumptions): return False i = expr.base.exp/I/pi if ask(Q.integer(2*i), assumptions): return ask(Q.imaginary((S.NegativeOne**i)**expr.exp), assumptions)
if ask(Q.imaginary(expr.base), assumptions): if ask(Q.integer(expr.exp), assumptions): odd = ask(Q.odd(expr.exp), assumptions) if odd is not None: return odd return
if ask(Q.imaginary(expr.exp), assumptions): imlog = ask(Q.imaginary(log(expr.base)), assumptions) if imlog is not None: # I**i -> real; (2*I)**i -> complex ==> not imaginary return False
if ask(Q.real(expr.base) & Q.real(expr.exp), assumptions): if ask(Q.positive(expr.base), assumptions): return False else: rat = ask(Q.rational(expr.exp), assumptions) if not rat: return rat if ask(Q.integer(expr.exp), assumptions): return False else: half = ask(Q.integer(2*expr.exp), assumptions) if half: return ask(Q.negative(expr.base), assumptions) return half
@ImaginaryPredicate.register(log) # type:ignore def _(expr, assumptions): if ask(Q.real(expr.args[0]), assumptions): if ask(Q.positive(expr.args[0]), assumptions): return False return # XXX it should be enough to do # return ask(Q.nonpositive(expr.args[0]), assumptions) # but ask(Q.nonpositive(exp(x)), Q.imaginary(x)) -> None; # it should return True since exp(x) will be either 0 or complex if expr.args[0].func == exp or (expr.args[0].is_Pow and expr.args[0].base == E): if expr.args[0].exp in [I, -I]: return True im = ask(Q.imaginary(expr.args[0]), assumptions) if im is False: return False
@ImaginaryPredicate.register(exp) # type:ignore def _(expr, assumptions): a = expr.exp/I/pi return ask(Q.integer(2*a) & ~Q.integer(a), assumptions)
@ImaginaryPredicate.register_many(Number, NumberSymbol) # type:ignore def _(expr, assumptions): return not (expr.as_real_imag()[1] == 0)
@ImaginaryPredicate.register(NaN) # type:ignore def _(expr, assumptions): return None
# AntihermitianPredicate
@AntihermitianPredicate.register(object) # type:ignore def _(expr, assumptions): if isinstance(expr, MatrixBase): return None if ask(Q.zero(expr), assumptions): return True return ask(Q.imaginary(expr), assumptions)
@AntihermitianPredicate.register(Add) # type:ignore def _(expr, assumptions): """
* Antihermitian + Antihermitian -> Antihermitian * Antihermitian + !Antihermitian -> !Antihermitian """
if expr.is_number: raise MDNotImplementedError return test_closed_group(expr, assumptions, Q.antihermitian)
@AntihermitianPredicate.register(Mul) # type:ignore def _(expr, assumptions): """
As long as there is at most only one noncommutative term:
* Hermitian*Hermitian -> !Antihermitian * Hermitian*Antihermitian -> Antihermitian * Antihermitian*Antihermitian -> !Antihermitian """
if expr.is_number: raise MDNotImplementedError nccount = 0 result = False for arg in expr.args: if ask(Q.antihermitian(arg), assumptions): result = result ^ True elif not ask(Q.hermitian(arg), assumptions): break if ask(~Q.commutative(arg), assumptions): nccount += 1 if nccount > 1: break else: return result
@AntihermitianPredicate.register(Pow) # type:ignore def _(expr, assumptions): """
* Hermitian**Integer -> !Antihermitian * Antihermitian**Even -> !Antihermitian * Antihermitian**Odd -> Antihermitian """
if expr.is_number: raise MDNotImplementedError if ask(Q.hermitian(expr.base), assumptions): if ask(Q.integer(expr.exp), assumptions): return False elif ask(Q.antihermitian(expr.base), assumptions): if ask(Q.even(expr.exp), assumptions): return False elif ask(Q.odd(expr.exp), assumptions): return True raise MDNotImplementedError
@AntihermitianPredicate.register(MatrixBase) # type:ignore def _(mat, assumptions): rows, cols = mat.shape ret_val = True for i in range(rows): for j in range(i, cols): cond = fuzzy_bool(Eq(mat[i, j], -conjugate(mat[j, i]))) if cond is None: ret_val = None if cond == False: return False if ret_val is None: raise MDNotImplementedError return ret_val
# AlgebraicPredicate
@AlgebraicPredicate.register_many(AlgebraicNumber, Float, GoldenRatio, # type:ignore ImaginaryUnit, TribonacciConstant) def _(expr, assumptions): return True
@AlgebraicPredicate.register_many(ComplexInfinity, Exp1, Infinity, # type:ignore NegativeInfinity, Pi) def _(expr, assumptions): return False
@AlgebraicPredicate.register_many(Add, Mul) # type:ignore def _(expr, assumptions): return test_closed_group(expr, assumptions, Q.algebraic)
@AlgebraicPredicate.register(Pow) # type:ignore def _(expr, assumptions): if expr.base == E: if ask(Q.algebraic(expr.exp), assumptions): return ask(~Q.nonzero(expr.exp), assumptions) return return expr.exp.is_Rational and ask(Q.algebraic(expr.base), assumptions)
@AlgebraicPredicate.register(Rational) # type:ignore def _(expr, assumptions): return expr.q != 0
@AlgebraicPredicate.register_many(asin, atan, cos, sin, tan) # type:ignore def _(expr, assumptions): x = expr.args[0] if ask(Q.algebraic(x), assumptions): return ask(~Q.nonzero(x), assumptions)
@AlgebraicPredicate.register(exp) # type:ignore def _(expr, assumptions): x = expr.exp if ask(Q.algebraic(x), assumptions): return ask(~Q.nonzero(x), assumptions)
@AlgebraicPredicate.register_many(acot, cot) # type:ignore def _(expr, assumptions): x = expr.args[0] if ask(Q.algebraic(x), assumptions): return False
@AlgebraicPredicate.register_many(acos, log) # type:ignore def _(expr, assumptions): x = expr.args[0] if ask(Q.algebraic(x), assumptions): return ask(~Q.nonzero(x - 1), assumptions)
|