Spaces:
Sleeping
Sleeping
""" | |
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 | |
# type:ignore | |
def _(expr, assumptions): | |
return True | |
def _(expr, assumptions): | |
return False | |
def _(expr, assumptions): | |
ret = expr.is_integer | |
if ret is None: | |
raise MDNotImplementedError | |
return ret | |
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) | |
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 | |
def _(expr, assumptions): | |
return ask(Q.integer(expr.args[0]), assumptions) | |
def _(expr, assumptions): | |
return ask(Q.integer_elements(expr.args[0]), assumptions) | |
# RationalPredicate | |
def _(expr, assumptions): | |
return True | |
def _(expr, assumptions): | |
return None | |
def _(expr, assumptions): | |
return False | |
def _(expr, assumptions): | |
ret = expr.is_rational | |
if ret is None: | |
raise MDNotImplementedError | |
return ret | |
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) | |
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 | |
def _(expr, assumptions): | |
x = expr.args[0] | |
if ask(Q.rational(x), assumptions): | |
return ask(~Q.nonzero(x), assumptions) | |
def _(expr, assumptions): | |
x = expr.exp | |
if ask(Q.rational(x), assumptions): | |
return ask(~Q.nonzero(x), assumptions) | |
def _(expr, assumptions): | |
x = expr.args[0] | |
if ask(Q.rational(x), assumptions): | |
return False | |
def _(expr, assumptions): | |
x = expr.args[0] | |
if ask(Q.rational(x), assumptions): | |
return ask(~Q.nonzero(x - 1), assumptions) | |
# IrrationalPredicate | |
def _(expr, assumptions): | |
ret = expr.is_irrational | |
if ret is None: | |
raise MDNotImplementedError | |
return ret | |
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 | |
def _(expr, assumptions): | |
return True | |
def _(expr, assumptions): | |
return False | |
def _(expr, assumptions): | |
ret = expr.is_real | |
if ret is None: | |
raise MDNotImplementedError | |
return ret | |
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) | |
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 | |
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 | |
def _(expr, assumptions): | |
if ask(Q.real(expr.args[0]), assumptions): | |
return True | |
def _(expr, assumptions): | |
return ask( | |
Q.integer(expr.exp/I/pi) | Q.real(expr.exp), assumptions | |
) | |
def _(expr, assumptions): | |
return ask(Q.positive(expr.args[0]), assumptions) | |
def _(expr, assumptions): | |
return ask(Q.real_elements(expr.args[0]), assumptions) | |
# ExtendedRealPredicate | |
def _(expr, assumptions): | |
return ask(Q.negative_infinite(expr) | |
| Q.negative(expr) | |
| Q.zero(expr) | |
| Q.positive(expr) | |
| Q.positive_infinite(expr), | |
assumptions) | |
def _(expr, assumptions): | |
return True | |
# type:ignore | |
def _(expr, assumptions): | |
return test_closed_group(expr, assumptions, Q.extended_real) | |
# HermitianPredicate | |
# type:ignore | |
def _(expr, assumptions): | |
if isinstance(expr, MatrixBase): | |
return None | |
return ask(Q.real(expr), assumptions) | |
# 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) | |
# 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 | |
# 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 | |
# type:ignore | |
def _(expr, assumptions): | |
if ask(Q.hermitian(expr.args[0]), assumptions): | |
return True | |
raise MDNotImplementedError | |
# type:ignore | |
def _(expr, assumptions): | |
if ask(Q.hermitian(expr.exp), assumptions): | |
return True | |
raise MDNotImplementedError | |
# 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 | |
def _(expr, assumptions): | |
return True | |
# type:ignore | |
def _(expr, assumptions): | |
return False | |
# type:ignore | |
def _(expr, assumptions): | |
ret = expr.is_complex | |
if ret is None: | |
raise MDNotImplementedError | |
return ret | |
# type:ignore | |
def _(expr, assumptions): | |
return test_closed_group(expr, assumptions, Q.complex) | |
# type:ignore | |
def _(expr, assumptions): | |
if expr.base == E: | |
return True | |
return test_closed_group(expr, assumptions, Q.complex) | |
# type:ignore | |
def _(expr, assumptions): | |
return ask(Q.complex_elements(expr.args[0]), assumptions) | |
# 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 | |
# type:ignore | |
def _(expr, assumptions): | |
return True | |
# type:ignore | |
def _(expr, assumptions): | |
ret = expr.is_imaginary | |
if ret is None: | |
raise MDNotImplementedError | |
return ret | |
# 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 | |
# 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 | |
# 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 | |
# 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 | |
# type:ignore | |
def _(expr, assumptions): | |
a = expr.exp/I/pi | |
return ask(Q.integer(2*a) & ~Q.integer(a), assumptions) | |
# type:ignore | |
def _(expr, assumptions): | |
return not (expr.as_real_imag()[1] == 0) | |
# type:ignore | |
def _(expr, assumptions): | |
return None | |
# AntihermitianPredicate | |
# 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) | |
# 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) | |
# 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 | |
# 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 | |
# 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 | |
def _(expr, assumptions): | |
return True | |
def _(expr, assumptions): | |
return False | |
# type:ignore | |
def _(expr, assumptions): | |
return test_closed_group(expr, assumptions, Q.algebraic) | |
# 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) | |
# type:ignore | |
def _(expr, assumptions): | |
return expr.q != 0 | |
# type:ignore | |
def _(expr, assumptions): | |
x = expr.args[0] | |
if ask(Q.algebraic(x), assumptions): | |
return ask(~Q.nonzero(x), assumptions) | |
# type:ignore | |
def _(expr, assumptions): | |
x = expr.exp | |
if ask(Q.algebraic(x), assumptions): | |
return ask(~Q.nonzero(x), assumptions) | |
# type:ignore | |
def _(expr, assumptions): | |
x = expr.args[0] | |
if ask(Q.algebraic(x), assumptions): | |
return False | |
# type:ignore | |
def _(expr, assumptions): | |
x = expr.args[0] | |
if ask(Q.algebraic(x), assumptions): | |
return ask(~Q.nonzero(x - 1), assumptions) | |