switching to high quality piper tts and added label translations
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
"""
|
||||
Multipledispatch handlers for ``Predicate`` are implemented here.
|
||||
Handlers in this module are not directly imported to other modules in
|
||||
order to avoid circular import problem.
|
||||
"""
|
||||
|
||||
from .common import (AskHandler, CommonHandler,
|
||||
test_closed_group)
|
||||
|
||||
__all__ = [
|
||||
'AskHandler', 'CommonHandler',
|
||||
'test_closed_group'
|
||||
]
|
||||
@@ -0,0 +1,273 @@
|
||||
"""
|
||||
This module contains query handlers responsible for calculus queries:
|
||||
infinitesimal, finite, etc.
|
||||
"""
|
||||
|
||||
from sympy.assumptions import Q, ask
|
||||
from sympy.core import Expr, Add, Mul, Pow, Symbol
|
||||
from sympy.core.numbers import (NegativeInfinity, GoldenRatio,
|
||||
Infinity, Exp1, ComplexInfinity, ImaginaryUnit, NaN, Number, Pi, E,
|
||||
TribonacciConstant)
|
||||
from sympy.functions import cos, exp, log, sign, sin
|
||||
from sympy.logic.boolalg import conjuncts
|
||||
|
||||
from ..predicates.calculus import (FinitePredicate, InfinitePredicate,
|
||||
PositiveInfinitePredicate, NegativeInfinitePredicate)
|
||||
|
||||
|
||||
# FinitePredicate
|
||||
|
||||
|
||||
@FinitePredicate.register(Symbol)
|
||||
def _(expr, assumptions):
|
||||
"""
|
||||
Handles Symbol.
|
||||
"""
|
||||
if expr.is_finite is not None:
|
||||
return expr.is_finite
|
||||
if Q.finite(expr) in conjuncts(assumptions):
|
||||
return True
|
||||
return None
|
||||
|
||||
@FinitePredicate.register(Add)
|
||||
def _(expr, assumptions):
|
||||
"""
|
||||
Return True if expr is bounded, False if not and None if unknown.
|
||||
|
||||
Truth Table:
|
||||
|
||||
+-------+-----+-----------+-----------+
|
||||
| | | | |
|
||||
| | B | U | ? |
|
||||
| | | | |
|
||||
+-------+-----+---+---+---+---+---+---+
|
||||
| | | | | | | | |
|
||||
| | |'+'|'-'|'x'|'+'|'-'|'x'|
|
||||
| | | | | | | | |
|
||||
+-------+-----+---+---+---+---+---+---+
|
||||
| | | | |
|
||||
| B | B | U | ? |
|
||||
| | | | |
|
||||
+---+---+-----+---+---+---+---+---+---+
|
||||
| | | | | | | | | |
|
||||
| |'+'| | U | ? | ? | U | ? | ? |
|
||||
| | | | | | | | | |
|
||||
| +---+-----+---+---+---+---+---+---+
|
||||
| | | | | | | | | |
|
||||
| U |'-'| | ? | U | ? | ? | U | ? |
|
||||
| | | | | | | | | |
|
||||
| +---+-----+---+---+---+---+---+---+
|
||||
| | | | | |
|
||||
| |'x'| | ? | ? |
|
||||
| | | | | |
|
||||
+---+---+-----+---+---+---+---+---+---+
|
||||
| | | | |
|
||||
| ? | | | ? |
|
||||
| | | | |
|
||||
+-------+-----+-----------+---+---+---+
|
||||
|
||||
* 'B' = Bounded
|
||||
|
||||
* 'U' = Unbounded
|
||||
|
||||
* '?' = unknown boundedness
|
||||
|
||||
* '+' = positive sign
|
||||
|
||||
* '-' = negative sign
|
||||
|
||||
* 'x' = sign unknown
|
||||
|
||||
* All Bounded -> True
|
||||
|
||||
* 1 Unbounded and the rest Bounded -> False
|
||||
|
||||
* >1 Unbounded, all with same known sign -> False
|
||||
|
||||
* Any Unknown and unknown sign -> None
|
||||
|
||||
* Else -> None
|
||||
|
||||
When the signs are not the same you can have an undefined
|
||||
result as in oo - oo, hence 'bounded' is also undefined.
|
||||
"""
|
||||
sign = -1 # sign of unknown or infinite
|
||||
result = True
|
||||
for arg in expr.args:
|
||||
_bounded = ask(Q.finite(arg), assumptions)
|
||||
if _bounded:
|
||||
continue
|
||||
s = ask(Q.extended_positive(arg), assumptions)
|
||||
# if there has been more than one sign or if the sign of this arg
|
||||
# is None and Bounded is None or there was already
|
||||
# an unknown sign, return None
|
||||
if sign != -1 and s != sign or \
|
||||
s is None and None in (_bounded, sign):
|
||||
return None
|
||||
else:
|
||||
sign = s
|
||||
# once False, do not change
|
||||
if result is not False:
|
||||
result = _bounded
|
||||
return result
|
||||
|
||||
@FinitePredicate.register(Mul)
|
||||
def _(expr, assumptions):
|
||||
"""
|
||||
Return True if expr is bounded, False if not and None if unknown.
|
||||
|
||||
Truth Table:
|
||||
|
||||
+---+---+---+--------+
|
||||
| | | | |
|
||||
| | B | U | ? |
|
||||
| | | | |
|
||||
+---+---+---+---+----+
|
||||
| | | | | |
|
||||
| | | | s | /s |
|
||||
| | | | | |
|
||||
+---+---+---+---+----+
|
||||
| | | | |
|
||||
| B | B | U | ? |
|
||||
| | | | |
|
||||
+---+---+---+---+----+
|
||||
| | | | | |
|
||||
| U | | U | U | ? |
|
||||
| | | | | |
|
||||
+---+---+---+---+----+
|
||||
| | | | |
|
||||
| ? | | | ? |
|
||||
| | | | |
|
||||
+---+---+---+---+----+
|
||||
|
||||
* B = Bounded
|
||||
|
||||
* U = Unbounded
|
||||
|
||||
* ? = unknown boundedness
|
||||
|
||||
* s = signed (hence nonzero)
|
||||
|
||||
* /s = not signed
|
||||
"""
|
||||
result = True
|
||||
possible_zero = False
|
||||
for arg in expr.args:
|
||||
_bounded = ask(Q.finite(arg), assumptions)
|
||||
if _bounded:
|
||||
if ask(Q.zero(arg), assumptions) is not False:
|
||||
if result is False:
|
||||
return None
|
||||
possible_zero = True
|
||||
elif _bounded is None:
|
||||
if result is None:
|
||||
return None
|
||||
if ask(Q.extended_nonzero(arg), assumptions) is None:
|
||||
return None
|
||||
if result is not False:
|
||||
result = None
|
||||
else:
|
||||
if possible_zero:
|
||||
return None
|
||||
result = False
|
||||
return result
|
||||
|
||||
@FinitePredicate.register(Pow)
|
||||
def _(expr, assumptions):
|
||||
"""
|
||||
* Unbounded ** NonZero -> Unbounded
|
||||
|
||||
* Bounded ** Bounded -> Bounded
|
||||
|
||||
* Abs()<=1 ** Positive -> Bounded
|
||||
|
||||
* Abs()>=1 ** Negative -> Bounded
|
||||
|
||||
* Otherwise unknown
|
||||
"""
|
||||
if expr.base == E:
|
||||
return ask(Q.finite(expr.exp), assumptions)
|
||||
|
||||
base_bounded = ask(Q.finite(expr.base), assumptions)
|
||||
exp_bounded = ask(Q.finite(expr.exp), assumptions)
|
||||
if base_bounded is None and exp_bounded is None: # Common Case
|
||||
return None
|
||||
if base_bounded is False and ask(Q.extended_nonzero(expr.exp), assumptions):
|
||||
return False
|
||||
if base_bounded and exp_bounded:
|
||||
is_base_zero = ask(Q.zero(expr.base),assumptions)
|
||||
is_exp_negative = ask(Q.negative(expr.exp),assumptions)
|
||||
if is_base_zero is True and is_exp_negative is True:
|
||||
return False
|
||||
if is_base_zero is not False and is_exp_negative is not False:
|
||||
return None
|
||||
return True
|
||||
if (abs(expr.base) <= 1) == True and ask(Q.extended_positive(expr.exp), assumptions):
|
||||
return True
|
||||
if (abs(expr.base) >= 1) == True and ask(Q.extended_negative(expr.exp), assumptions):
|
||||
return True
|
||||
if (abs(expr.base) >= 1) == True and exp_bounded is False:
|
||||
return False
|
||||
return None
|
||||
|
||||
@FinitePredicate.register(exp)
|
||||
def _(expr, assumptions):
|
||||
return ask(Q.finite(expr.exp), assumptions)
|
||||
|
||||
@FinitePredicate.register(log)
|
||||
def _(expr, assumptions):
|
||||
# After complex -> finite fact is registered to new assumption system,
|
||||
# querying Q.infinite may be removed.
|
||||
if ask(Q.infinite(expr.args[0]), assumptions):
|
||||
return False
|
||||
return ask(~Q.zero(expr.args[0]), assumptions)
|
||||
|
||||
@FinitePredicate.register_many(cos, sin, Number, Pi, Exp1, GoldenRatio,
|
||||
TribonacciConstant, ImaginaryUnit, sign)
|
||||
def _(expr, assumptions):
|
||||
return True
|
||||
|
||||
@FinitePredicate.register_many(ComplexInfinity, Infinity, NegativeInfinity)
|
||||
def _(expr, assumptions):
|
||||
return False
|
||||
|
||||
@FinitePredicate.register(NaN)
|
||||
def _(expr, assumptions):
|
||||
return None
|
||||
|
||||
|
||||
# InfinitePredicate
|
||||
|
||||
|
||||
@InfinitePredicate.register(Expr)
|
||||
def _(expr, assumptions):
|
||||
is_finite = Q.finite(expr)._eval_ask(assumptions)
|
||||
if is_finite is None:
|
||||
return None
|
||||
return not is_finite
|
||||
|
||||
|
||||
# PositiveInfinitePredicate
|
||||
|
||||
|
||||
@PositiveInfinitePredicate.register(Infinity)
|
||||
def _(expr, assumptions):
|
||||
return True
|
||||
|
||||
|
||||
@PositiveInfinitePredicate.register_many(NegativeInfinity, ComplexInfinity)
|
||||
def _(expr, assumptions):
|
||||
return False
|
||||
|
||||
|
||||
# NegativeInfinitePredicate
|
||||
|
||||
|
||||
@NegativeInfinitePredicate.register(NegativeInfinity)
|
||||
def _(expr, assumptions):
|
||||
return True
|
||||
|
||||
|
||||
@NegativeInfinitePredicate.register_many(Infinity, ComplexInfinity)
|
||||
def _(expr, assumptions):
|
||||
return False
|
||||
@@ -0,0 +1,164 @@
|
||||
"""
|
||||
This module defines base class for handlers and some core handlers:
|
||||
``Q.commutative`` and ``Q.is_true``.
|
||||
"""
|
||||
|
||||
from sympy.assumptions import Q, ask, AppliedPredicate
|
||||
from sympy.core import Basic, Symbol
|
||||
from sympy.core.logic import _fuzzy_group, fuzzy_and, fuzzy_or
|
||||
from sympy.core.numbers import NaN, Number
|
||||
from sympy.logic.boolalg import (And, BooleanTrue, BooleanFalse, conjuncts,
|
||||
Equivalent, Implies, Not, Or)
|
||||
from sympy.utilities.exceptions import sympy_deprecation_warning
|
||||
|
||||
from ..predicates.common import CommutativePredicate, IsTruePredicate
|
||||
|
||||
|
||||
class AskHandler:
|
||||
"""Base class that all Ask Handlers must inherit."""
|
||||
def __new__(cls, *args, **kwargs):
|
||||
sympy_deprecation_warning(
|
||||
"""
|
||||
The AskHandler system is deprecated. The AskHandler class should
|
||||
be replaced with the multipledispatch handler of Predicate
|
||||
""",
|
||||
deprecated_since_version="1.8",
|
||||
active_deprecations_target='deprecated-askhandler',
|
||||
)
|
||||
return super().__new__(cls, *args, **kwargs)
|
||||
|
||||
|
||||
class CommonHandler(AskHandler):
|
||||
# Deprecated
|
||||
"""Defines some useful methods common to most Handlers. """
|
||||
|
||||
@staticmethod
|
||||
def AlwaysTrue(expr, assumptions):
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def AlwaysFalse(expr, assumptions):
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def AlwaysNone(expr, assumptions):
|
||||
return None
|
||||
|
||||
NaN = AlwaysFalse
|
||||
|
||||
|
||||
# CommutativePredicate
|
||||
|
||||
@CommutativePredicate.register(Symbol)
|
||||
def _(expr, assumptions):
|
||||
"""Objects are expected to be commutative unless otherwise stated"""
|
||||
assumps = conjuncts(assumptions)
|
||||
if expr.is_commutative is not None:
|
||||
return expr.is_commutative and not ~Q.commutative(expr) in assumps
|
||||
if Q.commutative(expr) in assumps:
|
||||
return True
|
||||
elif ~Q.commutative(expr) in assumps:
|
||||
return False
|
||||
return True
|
||||
|
||||
@CommutativePredicate.register(Basic)
|
||||
def _(expr, assumptions):
|
||||
for arg in expr.args:
|
||||
if not ask(Q.commutative(arg), assumptions):
|
||||
return False
|
||||
return True
|
||||
|
||||
@CommutativePredicate.register(Number)
|
||||
def _(expr, assumptions):
|
||||
return True
|
||||
|
||||
@CommutativePredicate.register(NaN)
|
||||
def _(expr, assumptions):
|
||||
return True
|
||||
|
||||
|
||||
# IsTruePredicate
|
||||
|
||||
@IsTruePredicate.register(bool)
|
||||
def _(expr, assumptions):
|
||||
return expr
|
||||
|
||||
@IsTruePredicate.register(BooleanTrue)
|
||||
def _(expr, assumptions):
|
||||
return True
|
||||
|
||||
@IsTruePredicate.register(BooleanFalse)
|
||||
def _(expr, assumptions):
|
||||
return False
|
||||
|
||||
@IsTruePredicate.register(AppliedPredicate)
|
||||
def _(expr, assumptions):
|
||||
return ask(expr, assumptions)
|
||||
|
||||
@IsTruePredicate.register(Not)
|
||||
def _(expr, assumptions):
|
||||
arg = expr.args[0]
|
||||
if arg.is_Symbol:
|
||||
# symbol used as abstract boolean object
|
||||
return None
|
||||
value = ask(arg, assumptions=assumptions)
|
||||
if value in (True, False):
|
||||
return not value
|
||||
else:
|
||||
return None
|
||||
|
||||
@IsTruePredicate.register(Or)
|
||||
def _(expr, assumptions):
|
||||
result = False
|
||||
for arg in expr.args:
|
||||
p = ask(arg, assumptions=assumptions)
|
||||
if p is True:
|
||||
return True
|
||||
if p is None:
|
||||
result = None
|
||||
return result
|
||||
|
||||
@IsTruePredicate.register(And)
|
||||
def _(expr, assumptions):
|
||||
result = True
|
||||
for arg in expr.args:
|
||||
p = ask(arg, assumptions=assumptions)
|
||||
if p is False:
|
||||
return False
|
||||
if p is None:
|
||||
result = None
|
||||
return result
|
||||
|
||||
@IsTruePredicate.register(Implies)
|
||||
def _(expr, assumptions):
|
||||
p, q = expr.args
|
||||
return ask(~p | q, assumptions=assumptions)
|
||||
|
||||
@IsTruePredicate.register(Equivalent)
|
||||
def _(expr, assumptions):
|
||||
p, q = expr.args
|
||||
pt = ask(p, assumptions=assumptions)
|
||||
if pt is None:
|
||||
return None
|
||||
qt = ask(q, assumptions=assumptions)
|
||||
if qt is None:
|
||||
return None
|
||||
return pt == qt
|
||||
|
||||
|
||||
#### Helper methods
|
||||
def test_closed_group(expr, assumptions, key):
|
||||
"""
|
||||
Test for membership in a group with respect
|
||||
to the current operation.
|
||||
"""
|
||||
return _fuzzy_group(
|
||||
(ask(key(a), assumptions) for a in expr.args), quick_exit=True)
|
||||
|
||||
def ask_all(*queries, assumptions):
|
||||
return fuzzy_and(
|
||||
(ask(query, assumptions) for query in queries))
|
||||
|
||||
def ask_any(*queries, assumptions):
|
||||
return fuzzy_or(
|
||||
(ask(query, assumptions) for query in queries))
|
||||
@@ -0,0 +1,716 @@
|
||||
"""
|
||||
This module contains query handlers responsible for Matrices queries:
|
||||
Square, Symmetric, Invertible etc.
|
||||
"""
|
||||
|
||||
from sympy.logic.boolalg import conjuncts
|
||||
from sympy.assumptions import Q, ask
|
||||
from sympy.assumptions.handlers import test_closed_group
|
||||
from sympy.matrices import MatrixBase
|
||||
from sympy.matrices.expressions import (BlockMatrix, BlockDiagMatrix, Determinant,
|
||||
DiagMatrix, DiagonalMatrix, HadamardProduct, Identity, Inverse, MatAdd, MatMul,
|
||||
MatPow, MatrixExpr, MatrixSlice, MatrixSymbol, OneMatrix, Trace, Transpose,
|
||||
ZeroMatrix)
|
||||
from sympy.matrices.expressions.blockmatrix import reblock_2x2
|
||||
from sympy.matrices.expressions.factorizations import Factorization
|
||||
from sympy.matrices.expressions.fourier import DFT
|
||||
from sympy.core.logic import fuzzy_and
|
||||
from sympy.utilities.iterables import sift
|
||||
from sympy.core import Basic
|
||||
|
||||
from ..predicates.matrices import (SquarePredicate, SymmetricPredicate,
|
||||
InvertiblePredicate, OrthogonalPredicate, UnitaryPredicate,
|
||||
FullRankPredicate, PositiveDefinitePredicate, UpperTriangularPredicate,
|
||||
LowerTriangularPredicate, DiagonalPredicate, IntegerElementsPredicate,
|
||||
RealElementsPredicate, ComplexElementsPredicate)
|
||||
|
||||
|
||||
def _Factorization(predicate, expr, assumptions):
|
||||
if predicate in expr.predicates:
|
||||
return True
|
||||
|
||||
|
||||
# SquarePredicate
|
||||
|
||||
@SquarePredicate.register(MatrixExpr)
|
||||
def _(expr, assumptions):
|
||||
return expr.shape[0] == expr.shape[1]
|
||||
|
||||
|
||||
# SymmetricPredicate
|
||||
|
||||
@SymmetricPredicate.register(MatMul)
|
||||
def _(expr, assumptions):
|
||||
factor, mmul = expr.as_coeff_mmul()
|
||||
if all(ask(Q.symmetric(arg), assumptions) for arg in mmul.args):
|
||||
return True
|
||||
# TODO: implement sathandlers system for the matrices.
|
||||
# Now it duplicates the general fact: Implies(Q.diagonal, Q.symmetric).
|
||||
if ask(Q.diagonal(expr), assumptions):
|
||||
return True
|
||||
if len(mmul.args) >= 2 and mmul.args[0] == mmul.args[-1].T:
|
||||
if len(mmul.args) == 2:
|
||||
return True
|
||||
return ask(Q.symmetric(MatMul(*mmul.args[1:-1])), assumptions)
|
||||
|
||||
@SymmetricPredicate.register(MatPow)
|
||||
def _(expr, assumptions):
|
||||
# only for integer powers
|
||||
base, exp = expr.args
|
||||
int_exp = ask(Q.integer(exp), assumptions)
|
||||
if not int_exp:
|
||||
return None
|
||||
non_negative = ask(~Q.negative(exp), assumptions)
|
||||
if (non_negative or non_negative == False
|
||||
and ask(Q.invertible(base), assumptions)):
|
||||
return ask(Q.symmetric(base), assumptions)
|
||||
return None
|
||||
|
||||
@SymmetricPredicate.register(MatAdd)
|
||||
def _(expr, assumptions):
|
||||
return all(ask(Q.symmetric(arg), assumptions) for arg in expr.args)
|
||||
|
||||
@SymmetricPredicate.register(MatrixSymbol)
|
||||
def _(expr, assumptions):
|
||||
if not expr.is_square:
|
||||
return False
|
||||
# TODO: implement sathandlers system for the matrices.
|
||||
# Now it duplicates the general fact: Implies(Q.diagonal, Q.symmetric).
|
||||
if ask(Q.diagonal(expr), assumptions):
|
||||
return True
|
||||
if Q.symmetric(expr) in conjuncts(assumptions):
|
||||
return True
|
||||
|
||||
@SymmetricPredicate.register_many(OneMatrix, ZeroMatrix)
|
||||
def _(expr, assumptions):
|
||||
return ask(Q.square(expr), assumptions)
|
||||
|
||||
@SymmetricPredicate.register_many(Inverse, Transpose)
|
||||
def _(expr, assumptions):
|
||||
return ask(Q.symmetric(expr.arg), assumptions)
|
||||
|
||||
@SymmetricPredicate.register(MatrixSlice)
|
||||
def _(expr, assumptions):
|
||||
# TODO: implement sathandlers system for the matrices.
|
||||
# Now it duplicates the general fact: Implies(Q.diagonal, Q.symmetric).
|
||||
if ask(Q.diagonal(expr), assumptions):
|
||||
return True
|
||||
if not expr.on_diag:
|
||||
return None
|
||||
else:
|
||||
return ask(Q.symmetric(expr.parent), assumptions)
|
||||
|
||||
@SymmetricPredicate.register(Identity)
|
||||
def _(expr, assumptions):
|
||||
return True
|
||||
|
||||
|
||||
# InvertiblePredicate
|
||||
|
||||
@InvertiblePredicate.register(MatMul)
|
||||
def _(expr, assumptions):
|
||||
factor, mmul = expr.as_coeff_mmul()
|
||||
if all(ask(Q.invertible(arg), assumptions) for arg in mmul.args):
|
||||
return True
|
||||
if any(ask(Q.invertible(arg), assumptions) is False
|
||||
for arg in mmul.args):
|
||||
return False
|
||||
|
||||
@InvertiblePredicate.register(MatPow)
|
||||
def _(expr, assumptions):
|
||||
# only for integer powers
|
||||
base, exp = expr.args
|
||||
int_exp = ask(Q.integer(exp), assumptions)
|
||||
if not int_exp:
|
||||
return None
|
||||
if exp.is_negative == False:
|
||||
return ask(Q.invertible(base), assumptions)
|
||||
return None
|
||||
|
||||
@InvertiblePredicate.register(MatAdd)
|
||||
def _(expr, assumptions):
|
||||
return None
|
||||
|
||||
@InvertiblePredicate.register(MatrixSymbol)
|
||||
def _(expr, assumptions):
|
||||
if not expr.is_square:
|
||||
return False
|
||||
if Q.invertible(expr) in conjuncts(assumptions):
|
||||
return True
|
||||
|
||||
@InvertiblePredicate.register_many(Identity, Inverse)
|
||||
def _(expr, assumptions):
|
||||
return True
|
||||
|
||||
@InvertiblePredicate.register(ZeroMatrix)
|
||||
def _(expr, assumptions):
|
||||
return False
|
||||
|
||||
@InvertiblePredicate.register(OneMatrix)
|
||||
def _(expr, assumptions):
|
||||
return expr.shape[0] == 1 and expr.shape[1] == 1
|
||||
|
||||
@InvertiblePredicate.register(Transpose)
|
||||
def _(expr, assumptions):
|
||||
return ask(Q.invertible(expr.arg), assumptions)
|
||||
|
||||
@InvertiblePredicate.register(MatrixSlice)
|
||||
def _(expr, assumptions):
|
||||
if not expr.on_diag:
|
||||
return None
|
||||
else:
|
||||
return ask(Q.invertible(expr.parent), assumptions)
|
||||
|
||||
@InvertiblePredicate.register(MatrixBase)
|
||||
def _(expr, assumptions):
|
||||
if not expr.is_square:
|
||||
return False
|
||||
return expr.rank() == expr.rows
|
||||
|
||||
@InvertiblePredicate.register(MatrixExpr)
|
||||
def _(expr, assumptions):
|
||||
if not expr.is_square:
|
||||
return False
|
||||
return None
|
||||
|
||||
@InvertiblePredicate.register(BlockMatrix)
|
||||
def _(expr, assumptions):
|
||||
if not expr.is_square:
|
||||
return False
|
||||
if expr.blockshape == (1, 1):
|
||||
return ask(Q.invertible(expr.blocks[0, 0]), assumptions)
|
||||
expr = reblock_2x2(expr)
|
||||
if expr.blockshape == (2, 2):
|
||||
[[A, B], [C, D]] = expr.blocks.tolist()
|
||||
if ask(Q.invertible(A), assumptions) == True:
|
||||
invertible = ask(Q.invertible(D - C * A.I * B), assumptions)
|
||||
if invertible is not None:
|
||||
return invertible
|
||||
if ask(Q.invertible(B), assumptions) == True:
|
||||
invertible = ask(Q.invertible(C - D * B.I * A), assumptions)
|
||||
if invertible is not None:
|
||||
return invertible
|
||||
if ask(Q.invertible(C), assumptions) == True:
|
||||
invertible = ask(Q.invertible(B - A * C.I * D), assumptions)
|
||||
if invertible is not None:
|
||||
return invertible
|
||||
if ask(Q.invertible(D), assumptions) == True:
|
||||
invertible = ask(Q.invertible(A - B * D.I * C), assumptions)
|
||||
if invertible is not None:
|
||||
return invertible
|
||||
return None
|
||||
|
||||
@InvertiblePredicate.register(BlockDiagMatrix)
|
||||
def _(expr, assumptions):
|
||||
if expr.rowblocksizes != expr.colblocksizes:
|
||||
return None
|
||||
return fuzzy_and([ask(Q.invertible(a), assumptions) for a in expr.diag])
|
||||
|
||||
|
||||
# OrthogonalPredicate
|
||||
|
||||
@OrthogonalPredicate.register(MatMul)
|
||||
def _(expr, assumptions):
|
||||
factor, mmul = expr.as_coeff_mmul()
|
||||
if (all(ask(Q.orthogonal(arg), assumptions) for arg in mmul.args) and
|
||||
factor == 1):
|
||||
return True
|
||||
if any(ask(Q.invertible(arg), assumptions) is False
|
||||
for arg in mmul.args):
|
||||
return False
|
||||
|
||||
@OrthogonalPredicate.register(MatPow)
|
||||
def _(expr, assumptions):
|
||||
# only for integer powers
|
||||
base, exp = expr.args
|
||||
int_exp = ask(Q.integer(exp), assumptions)
|
||||
if int_exp:
|
||||
return ask(Q.orthogonal(base), assumptions)
|
||||
return None
|
||||
|
||||
@OrthogonalPredicate.register(MatAdd)
|
||||
def _(expr, assumptions):
|
||||
if (len(expr.args) == 1 and
|
||||
ask(Q.orthogonal(expr.args[0]), assumptions)):
|
||||
return True
|
||||
|
||||
@OrthogonalPredicate.register(MatrixSymbol)
|
||||
def _(expr, assumptions):
|
||||
if (not expr.is_square or
|
||||
ask(Q.invertible(expr), assumptions) is False):
|
||||
return False
|
||||
if Q.orthogonal(expr) in conjuncts(assumptions):
|
||||
return True
|
||||
|
||||
@OrthogonalPredicate.register(Identity)
|
||||
def _(expr, assumptions):
|
||||
return True
|
||||
|
||||
@OrthogonalPredicate.register(ZeroMatrix)
|
||||
def _(expr, assumptions):
|
||||
return False
|
||||
|
||||
@OrthogonalPredicate.register_many(Inverse, Transpose)
|
||||
def _(expr, assumptions):
|
||||
return ask(Q.orthogonal(expr.arg), assumptions)
|
||||
|
||||
@OrthogonalPredicate.register(MatrixSlice)
|
||||
def _(expr, assumptions):
|
||||
if not expr.on_diag:
|
||||
return None
|
||||
else:
|
||||
return ask(Q.orthogonal(expr.parent), assumptions)
|
||||
|
||||
@OrthogonalPredicate.register(Factorization)
|
||||
def _(expr, assumptions):
|
||||
return _Factorization(Q.orthogonal, expr, assumptions)
|
||||
|
||||
|
||||
# UnitaryPredicate
|
||||
|
||||
@UnitaryPredicate.register(MatMul)
|
||||
def _(expr, assumptions):
|
||||
factor, mmul = expr.as_coeff_mmul()
|
||||
if (all(ask(Q.unitary(arg), assumptions) for arg in mmul.args) and
|
||||
abs(factor) == 1):
|
||||
return True
|
||||
if any(ask(Q.invertible(arg), assumptions) is False
|
||||
for arg in mmul.args):
|
||||
return False
|
||||
|
||||
@UnitaryPredicate.register(MatPow)
|
||||
def _(expr, assumptions):
|
||||
# only for integer powers
|
||||
base, exp = expr.args
|
||||
int_exp = ask(Q.integer(exp), assumptions)
|
||||
if int_exp:
|
||||
return ask(Q.unitary(base), assumptions)
|
||||
return None
|
||||
|
||||
@UnitaryPredicate.register(MatrixSymbol)
|
||||
def _(expr, assumptions):
|
||||
if (not expr.is_square or
|
||||
ask(Q.invertible(expr), assumptions) is False):
|
||||
return False
|
||||
if Q.unitary(expr) in conjuncts(assumptions):
|
||||
return True
|
||||
|
||||
@UnitaryPredicate.register_many(Inverse, Transpose)
|
||||
def _(expr, assumptions):
|
||||
return ask(Q.unitary(expr.arg), assumptions)
|
||||
|
||||
@UnitaryPredicate.register(MatrixSlice)
|
||||
def _(expr, assumptions):
|
||||
if not expr.on_diag:
|
||||
return None
|
||||
else:
|
||||
return ask(Q.unitary(expr.parent), assumptions)
|
||||
|
||||
@UnitaryPredicate.register_many(DFT, Identity)
|
||||
def _(expr, assumptions):
|
||||
return True
|
||||
|
||||
@UnitaryPredicate.register(ZeroMatrix)
|
||||
def _(expr, assumptions):
|
||||
return False
|
||||
|
||||
@UnitaryPredicate.register(Factorization)
|
||||
def _(expr, assumptions):
|
||||
return _Factorization(Q.unitary, expr, assumptions)
|
||||
|
||||
|
||||
# FullRankPredicate
|
||||
|
||||
@FullRankPredicate.register(MatMul)
|
||||
def _(expr, assumptions):
|
||||
if all(ask(Q.fullrank(arg), assumptions) for arg in expr.args):
|
||||
return True
|
||||
|
||||
@FullRankPredicate.register(MatPow)
|
||||
def _(expr, assumptions):
|
||||
# only for integer powers
|
||||
base, exp = expr.args
|
||||
int_exp = ask(Q.integer(exp), assumptions)
|
||||
if int_exp and ask(~Q.negative(exp), assumptions):
|
||||
return ask(Q.fullrank(base), assumptions)
|
||||
return None
|
||||
|
||||
@FullRankPredicate.register(Identity)
|
||||
def _(expr, assumptions):
|
||||
return True
|
||||
|
||||
@FullRankPredicate.register(ZeroMatrix)
|
||||
def _(expr, assumptions):
|
||||
return False
|
||||
|
||||
@FullRankPredicate.register(OneMatrix)
|
||||
def _(expr, assumptions):
|
||||
return expr.shape[0] == 1 and expr.shape[1] == 1
|
||||
|
||||
@FullRankPredicate.register_many(Inverse, Transpose)
|
||||
def _(expr, assumptions):
|
||||
return ask(Q.fullrank(expr.arg), assumptions)
|
||||
|
||||
@FullRankPredicate.register(MatrixSlice)
|
||||
def _(expr, assumptions):
|
||||
if ask(Q.orthogonal(expr.parent), assumptions):
|
||||
return True
|
||||
|
||||
|
||||
# PositiveDefinitePredicate
|
||||
|
||||
@PositiveDefinitePredicate.register(MatMul)
|
||||
def _(expr, assumptions):
|
||||
factor, mmul = expr.as_coeff_mmul()
|
||||
if (all(ask(Q.positive_definite(arg), assumptions)
|
||||
for arg in mmul.args) and factor > 0):
|
||||
return True
|
||||
if (len(mmul.args) >= 2
|
||||
and mmul.args[0] == mmul.args[-1].T
|
||||
and ask(Q.fullrank(mmul.args[0]), assumptions)):
|
||||
return ask(Q.positive_definite(
|
||||
MatMul(*mmul.args[1:-1])), assumptions)
|
||||
|
||||
@PositiveDefinitePredicate.register(MatPow)
|
||||
def _(expr, assumptions):
|
||||
# a power of a positive definite matrix is positive definite
|
||||
if ask(Q.positive_definite(expr.args[0]), assumptions):
|
||||
return True
|
||||
|
||||
@PositiveDefinitePredicate.register(MatAdd)
|
||||
def _(expr, assumptions):
|
||||
if all(ask(Q.positive_definite(arg), assumptions)
|
||||
for arg in expr.args):
|
||||
return True
|
||||
|
||||
@PositiveDefinitePredicate.register(MatrixSymbol)
|
||||
def _(expr, assumptions):
|
||||
if not expr.is_square:
|
||||
return False
|
||||
if Q.positive_definite(expr) in conjuncts(assumptions):
|
||||
return True
|
||||
|
||||
@PositiveDefinitePredicate.register(Identity)
|
||||
def _(expr, assumptions):
|
||||
return True
|
||||
|
||||
@PositiveDefinitePredicate.register(ZeroMatrix)
|
||||
def _(expr, assumptions):
|
||||
return False
|
||||
|
||||
@PositiveDefinitePredicate.register(OneMatrix)
|
||||
def _(expr, assumptions):
|
||||
return expr.shape[0] == 1 and expr.shape[1] == 1
|
||||
|
||||
@PositiveDefinitePredicate.register_many(Inverse, Transpose)
|
||||
def _(expr, assumptions):
|
||||
return ask(Q.positive_definite(expr.arg), assumptions)
|
||||
|
||||
@PositiveDefinitePredicate.register(MatrixSlice)
|
||||
def _(expr, assumptions):
|
||||
if not expr.on_diag:
|
||||
return None
|
||||
else:
|
||||
return ask(Q.positive_definite(expr.parent), assumptions)
|
||||
|
||||
|
||||
# UpperTriangularPredicate
|
||||
|
||||
@UpperTriangularPredicate.register(MatMul)
|
||||
def _(expr, assumptions):
|
||||
factor, matrices = expr.as_coeff_matrices()
|
||||
if all(ask(Q.upper_triangular(m), assumptions) for m in matrices):
|
||||
return True
|
||||
|
||||
@UpperTriangularPredicate.register(MatAdd)
|
||||
def _(expr, assumptions):
|
||||
if all(ask(Q.upper_triangular(arg), assumptions) for arg in expr.args):
|
||||
return True
|
||||
|
||||
@UpperTriangularPredicate.register(MatPow)
|
||||
def _(expr, assumptions):
|
||||
# only for integer powers
|
||||
base, exp = expr.args
|
||||
int_exp = ask(Q.integer(exp), assumptions)
|
||||
if not int_exp:
|
||||
return None
|
||||
non_negative = ask(~Q.negative(exp), assumptions)
|
||||
if (non_negative or non_negative == False
|
||||
and ask(Q.invertible(base), assumptions)):
|
||||
return ask(Q.upper_triangular(base), assumptions)
|
||||
return None
|
||||
|
||||
@UpperTriangularPredicate.register(MatrixSymbol)
|
||||
def _(expr, assumptions):
|
||||
if Q.upper_triangular(expr) in conjuncts(assumptions):
|
||||
return True
|
||||
|
||||
@UpperTriangularPredicate.register_many(Identity, ZeroMatrix)
|
||||
def _(expr, assumptions):
|
||||
return True
|
||||
|
||||
@UpperTriangularPredicate.register(OneMatrix)
|
||||
def _(expr, assumptions):
|
||||
return expr.shape[0] == 1 and expr.shape[1] == 1
|
||||
|
||||
@UpperTriangularPredicate.register(Transpose)
|
||||
def _(expr, assumptions):
|
||||
return ask(Q.lower_triangular(expr.arg), assumptions)
|
||||
|
||||
@UpperTriangularPredicate.register(Inverse)
|
||||
def _(expr, assumptions):
|
||||
return ask(Q.upper_triangular(expr.arg), assumptions)
|
||||
|
||||
@UpperTriangularPredicate.register(MatrixSlice)
|
||||
def _(expr, assumptions):
|
||||
if not expr.on_diag:
|
||||
return None
|
||||
else:
|
||||
return ask(Q.upper_triangular(expr.parent), assumptions)
|
||||
|
||||
@UpperTriangularPredicate.register(Factorization)
|
||||
def _(expr, assumptions):
|
||||
return _Factorization(Q.upper_triangular, expr, assumptions)
|
||||
|
||||
# LowerTriangularPredicate
|
||||
|
||||
@LowerTriangularPredicate.register(MatMul)
|
||||
def _(expr, assumptions):
|
||||
factor, matrices = expr.as_coeff_matrices()
|
||||
if all(ask(Q.lower_triangular(m), assumptions) for m in matrices):
|
||||
return True
|
||||
|
||||
@LowerTriangularPredicate.register(MatAdd)
|
||||
def _(expr, assumptions):
|
||||
if all(ask(Q.lower_triangular(arg), assumptions) for arg in expr.args):
|
||||
return True
|
||||
|
||||
@LowerTriangularPredicate.register(MatPow)
|
||||
def _(expr, assumptions):
|
||||
# only for integer powers
|
||||
base, exp = expr.args
|
||||
int_exp = ask(Q.integer(exp), assumptions)
|
||||
if not int_exp:
|
||||
return None
|
||||
non_negative = ask(~Q.negative(exp), assumptions)
|
||||
if (non_negative or non_negative == False
|
||||
and ask(Q.invertible(base), assumptions)):
|
||||
return ask(Q.lower_triangular(base), assumptions)
|
||||
return None
|
||||
|
||||
@LowerTriangularPredicate.register(MatrixSymbol)
|
||||
def _(expr, assumptions):
|
||||
if Q.lower_triangular(expr) in conjuncts(assumptions):
|
||||
return True
|
||||
|
||||
@LowerTriangularPredicate.register_many(Identity, ZeroMatrix)
|
||||
def _(expr, assumptions):
|
||||
return True
|
||||
|
||||
@LowerTriangularPredicate.register(OneMatrix)
|
||||
def _(expr, assumptions):
|
||||
return expr.shape[0] == 1 and expr.shape[1] == 1
|
||||
|
||||
@LowerTriangularPredicate.register(Transpose)
|
||||
def _(expr, assumptions):
|
||||
return ask(Q.upper_triangular(expr.arg), assumptions)
|
||||
|
||||
@LowerTriangularPredicate.register(Inverse)
|
||||
def _(expr, assumptions):
|
||||
return ask(Q.lower_triangular(expr.arg), assumptions)
|
||||
|
||||
@LowerTriangularPredicate.register(MatrixSlice)
|
||||
def _(expr, assumptions):
|
||||
if not expr.on_diag:
|
||||
return None
|
||||
else:
|
||||
return ask(Q.lower_triangular(expr.parent), assumptions)
|
||||
|
||||
@LowerTriangularPredicate.register(Factorization)
|
||||
def _(expr, assumptions):
|
||||
return _Factorization(Q.lower_triangular, expr, assumptions)
|
||||
|
||||
|
||||
# DiagonalPredicate
|
||||
|
||||
def _is_empty_or_1x1(expr):
|
||||
return expr.shape in ((0, 0), (1, 1))
|
||||
|
||||
@DiagonalPredicate.register(MatMul)
|
||||
def _(expr, assumptions):
|
||||
if _is_empty_or_1x1(expr):
|
||||
return True
|
||||
factor, matrices = expr.as_coeff_matrices()
|
||||
if all(ask(Q.diagonal(m), assumptions) for m in matrices):
|
||||
return True
|
||||
|
||||
@DiagonalPredicate.register(MatPow)
|
||||
def _(expr, assumptions):
|
||||
# only for integer powers
|
||||
base, exp = expr.args
|
||||
int_exp = ask(Q.integer(exp), assumptions)
|
||||
if not int_exp:
|
||||
return None
|
||||
non_negative = ask(~Q.negative(exp), assumptions)
|
||||
if (non_negative or non_negative == False
|
||||
and ask(Q.invertible(base), assumptions)):
|
||||
return ask(Q.diagonal(base), assumptions)
|
||||
return None
|
||||
|
||||
@DiagonalPredicate.register(MatAdd)
|
||||
def _(expr, assumptions):
|
||||
if all(ask(Q.diagonal(arg), assumptions) for arg in expr.args):
|
||||
return True
|
||||
|
||||
@DiagonalPredicate.register(MatrixSymbol)
|
||||
def _(expr, assumptions):
|
||||
if _is_empty_or_1x1(expr):
|
||||
return True
|
||||
if Q.diagonal(expr) in conjuncts(assumptions):
|
||||
return True
|
||||
|
||||
@DiagonalPredicate.register(OneMatrix)
|
||||
def _(expr, assumptions):
|
||||
return expr.shape[0] == 1 and expr.shape[1] == 1
|
||||
|
||||
@DiagonalPredicate.register_many(Inverse, Transpose)
|
||||
def _(expr, assumptions):
|
||||
return ask(Q.diagonal(expr.arg), assumptions)
|
||||
|
||||
@DiagonalPredicate.register(MatrixSlice)
|
||||
def _(expr, assumptions):
|
||||
if _is_empty_or_1x1(expr):
|
||||
return True
|
||||
if not expr.on_diag:
|
||||
return None
|
||||
else:
|
||||
return ask(Q.diagonal(expr.parent), assumptions)
|
||||
|
||||
@DiagonalPredicate.register_many(DiagonalMatrix, DiagMatrix, Identity, ZeroMatrix)
|
||||
def _(expr, assumptions):
|
||||
return True
|
||||
|
||||
@DiagonalPredicate.register(Factorization)
|
||||
def _(expr, assumptions):
|
||||
return _Factorization(Q.diagonal, expr, assumptions)
|
||||
|
||||
|
||||
# IntegerElementsPredicate
|
||||
|
||||
def BM_elements(predicate, expr, assumptions):
|
||||
""" Block Matrix elements. """
|
||||
return all(ask(predicate(b), assumptions) for b in expr.blocks)
|
||||
|
||||
def MS_elements(predicate, expr, assumptions):
|
||||
""" Matrix Slice elements. """
|
||||
return ask(predicate(expr.parent), assumptions)
|
||||
|
||||
def MatMul_elements(matrix_predicate, scalar_predicate, expr, assumptions):
|
||||
d = sift(expr.args, lambda x: isinstance(x, MatrixExpr))
|
||||
factors, matrices = d[False], d[True]
|
||||
return fuzzy_and([
|
||||
test_closed_group(Basic(*factors), assumptions, scalar_predicate),
|
||||
test_closed_group(Basic(*matrices), assumptions, matrix_predicate)])
|
||||
|
||||
|
||||
@IntegerElementsPredicate.register_many(Determinant, HadamardProduct, MatAdd,
|
||||
Trace, Transpose)
|
||||
def _(expr, assumptions):
|
||||
return test_closed_group(expr, assumptions, Q.integer_elements)
|
||||
|
||||
@IntegerElementsPredicate.register(MatPow)
|
||||
def _(expr, assumptions):
|
||||
# only for integer powers
|
||||
base, exp = expr.args
|
||||
int_exp = ask(Q.integer(exp), assumptions)
|
||||
if not int_exp:
|
||||
return None
|
||||
if exp.is_negative == False:
|
||||
return ask(Q.integer_elements(base), assumptions)
|
||||
return None
|
||||
|
||||
@IntegerElementsPredicate.register_many(Identity, OneMatrix, ZeroMatrix)
|
||||
def _(expr, assumptions):
|
||||
return True
|
||||
|
||||
@IntegerElementsPredicate.register(MatMul)
|
||||
def _(expr, assumptions):
|
||||
return MatMul_elements(Q.integer_elements, Q.integer, expr, assumptions)
|
||||
|
||||
@IntegerElementsPredicate.register(MatrixSlice)
|
||||
def _(expr, assumptions):
|
||||
return MS_elements(Q.integer_elements, expr, assumptions)
|
||||
|
||||
@IntegerElementsPredicate.register(BlockMatrix)
|
||||
def _(expr, assumptions):
|
||||
return BM_elements(Q.integer_elements, expr, assumptions)
|
||||
|
||||
|
||||
# RealElementsPredicate
|
||||
|
||||
@RealElementsPredicate.register_many(Determinant, Factorization, HadamardProduct,
|
||||
MatAdd, Trace, Transpose)
|
||||
def _(expr, assumptions):
|
||||
return test_closed_group(expr, assumptions, Q.real_elements)
|
||||
|
||||
@RealElementsPredicate.register(MatPow)
|
||||
def _(expr, assumptions):
|
||||
# only for integer powers
|
||||
base, exp = expr.args
|
||||
int_exp = ask(Q.integer(exp), assumptions)
|
||||
if not int_exp:
|
||||
return None
|
||||
non_negative = ask(~Q.negative(exp), assumptions)
|
||||
if (non_negative or non_negative == False
|
||||
and ask(Q.invertible(base), assumptions)):
|
||||
return ask(Q.real_elements(base), assumptions)
|
||||
return None
|
||||
|
||||
@RealElementsPredicate.register(MatMul)
|
||||
def _(expr, assumptions):
|
||||
return MatMul_elements(Q.real_elements, Q.real, expr, assumptions)
|
||||
|
||||
@RealElementsPredicate.register(MatrixSlice)
|
||||
def _(expr, assumptions):
|
||||
return MS_elements(Q.real_elements, expr, assumptions)
|
||||
|
||||
@RealElementsPredicate.register(BlockMatrix)
|
||||
def _(expr, assumptions):
|
||||
return BM_elements(Q.real_elements, expr, assumptions)
|
||||
|
||||
|
||||
# ComplexElementsPredicate
|
||||
|
||||
@ComplexElementsPredicate.register_many(Determinant, Factorization, HadamardProduct,
|
||||
Inverse, MatAdd, Trace, Transpose)
|
||||
def _(expr, assumptions):
|
||||
return test_closed_group(expr, assumptions, Q.complex_elements)
|
||||
|
||||
@ComplexElementsPredicate.register(MatPow)
|
||||
def _(expr, assumptions):
|
||||
# only for integer powers
|
||||
base, exp = expr.args
|
||||
int_exp = ask(Q.integer(exp), assumptions)
|
||||
if not int_exp:
|
||||
return None
|
||||
non_negative = ask(~Q.negative(exp), assumptions)
|
||||
if (non_negative or non_negative == False
|
||||
and ask(Q.invertible(base), assumptions)):
|
||||
return ask(Q.complex_elements(base), assumptions)
|
||||
return None
|
||||
|
||||
@ComplexElementsPredicate.register(MatMul)
|
||||
def _(expr, assumptions):
|
||||
return MatMul_elements(Q.complex_elements, Q.complex, expr, assumptions)
|
||||
|
||||
@ComplexElementsPredicate.register(MatrixSlice)
|
||||
def _(expr, assumptions):
|
||||
return MS_elements(Q.complex_elements, expr, assumptions)
|
||||
|
||||
@ComplexElementsPredicate.register(BlockMatrix)
|
||||
def _(expr, assumptions):
|
||||
return BM_elements(Q.complex_elements, expr, assumptions)
|
||||
|
||||
@ComplexElementsPredicate.register(DFT)
|
||||
def _(expr, assumptions):
|
||||
return True
|
||||
@@ -0,0 +1,279 @@
|
||||
"""
|
||||
Handlers for keys related to number theory: prime, even, odd, etc.
|
||||
"""
|
||||
|
||||
from sympy.assumptions import Q, ask
|
||||
from sympy.core import Add, Basic, Expr, Float, Mul, Pow, S
|
||||
from sympy.core.numbers import (ImaginaryUnit, Infinity, Integer, NaN,
|
||||
NegativeInfinity, NumberSymbol, Rational, int_valued)
|
||||
from sympy.functions import Abs, im, re
|
||||
from sympy.ntheory import isprime
|
||||
|
||||
from sympy.multipledispatch import MDNotImplementedError
|
||||
|
||||
from ..predicates.ntheory import (PrimePredicate, CompositePredicate,
|
||||
EvenPredicate, OddPredicate)
|
||||
|
||||
|
||||
# PrimePredicate
|
||||
|
||||
def _PrimePredicate_number(expr, assumptions):
|
||||
# helper method
|
||||
exact = not expr.atoms(Float)
|
||||
try:
|
||||
i = int(expr.round())
|
||||
if (expr - i).equals(0) is False:
|
||||
raise TypeError
|
||||
except TypeError:
|
||||
return False
|
||||
if exact:
|
||||
return isprime(i)
|
||||
# when not exact, we won't give a True or False
|
||||
# since the number represents an approximate value
|
||||
|
||||
@PrimePredicate.register(Expr)
|
||||
def _(expr, assumptions):
|
||||
ret = expr.is_prime
|
||||
if ret is None:
|
||||
raise MDNotImplementedError
|
||||
return ret
|
||||
|
||||
@PrimePredicate.register(Basic)
|
||||
def _(expr, assumptions):
|
||||
if expr.is_number:
|
||||
return _PrimePredicate_number(expr, assumptions)
|
||||
|
||||
@PrimePredicate.register(Mul)
|
||||
def _(expr, assumptions):
|
||||
if expr.is_number:
|
||||
return _PrimePredicate_number(expr, assumptions)
|
||||
for arg in expr.args:
|
||||
if not ask(Q.integer(arg), assumptions):
|
||||
return None
|
||||
for arg in expr.args:
|
||||
if arg.is_number and arg.is_composite:
|
||||
return False
|
||||
|
||||
@PrimePredicate.register(Pow)
|
||||
def _(expr, assumptions):
|
||||
"""
|
||||
Integer**Integer -> !Prime
|
||||
"""
|
||||
if expr.is_number:
|
||||
return _PrimePredicate_number(expr, assumptions)
|
||||
if ask(Q.integer(expr.exp), assumptions) and \
|
||||
ask(Q.integer(expr.base), assumptions):
|
||||
prime_base = ask(Q.prime(expr.base), assumptions)
|
||||
if prime_base is False:
|
||||
return False
|
||||
is_exp_one = ask(Q.eq(expr.exp, 1), assumptions)
|
||||
if is_exp_one is False:
|
||||
return False
|
||||
if prime_base is True and is_exp_one is True:
|
||||
return True
|
||||
|
||||
@PrimePredicate.register(Integer)
|
||||
def _(expr, assumptions):
|
||||
return isprime(expr)
|
||||
|
||||
@PrimePredicate.register_many(Rational, Infinity, NegativeInfinity, ImaginaryUnit)
|
||||
def _(expr, assumptions):
|
||||
return False
|
||||
|
||||
@PrimePredicate.register(Float)
|
||||
def _(expr, assumptions):
|
||||
return _PrimePredicate_number(expr, assumptions)
|
||||
|
||||
@PrimePredicate.register(NumberSymbol)
|
||||
def _(expr, assumptions):
|
||||
return _PrimePredicate_number(expr, assumptions)
|
||||
|
||||
@PrimePredicate.register(NaN)
|
||||
def _(expr, assumptions):
|
||||
return None
|
||||
|
||||
|
||||
# CompositePredicate
|
||||
|
||||
@CompositePredicate.register(Expr)
|
||||
def _(expr, assumptions):
|
||||
ret = expr.is_composite
|
||||
if ret is None:
|
||||
raise MDNotImplementedError
|
||||
return ret
|
||||
|
||||
@CompositePredicate.register(Basic)
|
||||
def _(expr, assumptions):
|
||||
_positive = ask(Q.positive(expr), assumptions)
|
||||
if _positive:
|
||||
_integer = ask(Q.integer(expr), assumptions)
|
||||
if _integer:
|
||||
_prime = ask(Q.prime(expr), assumptions)
|
||||
if _prime is None:
|
||||
return
|
||||
# Positive integer which is not prime is not
|
||||
# necessarily composite
|
||||
_is_one = ask(Q.eq(expr, 1), assumptions)
|
||||
if _is_one:
|
||||
return False
|
||||
if _is_one is None:
|
||||
return None
|
||||
return not _prime
|
||||
else:
|
||||
return _integer
|
||||
else:
|
||||
return _positive
|
||||
|
||||
|
||||
# EvenPredicate
|
||||
|
||||
def _EvenPredicate_number(expr, assumptions):
|
||||
# helper method
|
||||
if isinstance(expr, (float, Float)):
|
||||
if int_valued(expr):
|
||||
return None
|
||||
return False
|
||||
try:
|
||||
i = int(expr.round())
|
||||
except TypeError:
|
||||
return False
|
||||
if not (expr - i).equals(0):
|
||||
return False
|
||||
return i % 2 == 0
|
||||
|
||||
@EvenPredicate.register(Expr)
|
||||
def _(expr, assumptions):
|
||||
ret = expr.is_even
|
||||
if ret is None:
|
||||
raise MDNotImplementedError
|
||||
return ret
|
||||
|
||||
@EvenPredicate.register(Basic)
|
||||
def _(expr, assumptions):
|
||||
if expr.is_number:
|
||||
return _EvenPredicate_number(expr, assumptions)
|
||||
|
||||
@EvenPredicate.register(Mul)
|
||||
def _(expr, assumptions):
|
||||
"""
|
||||
Even * Integer -> Even
|
||||
Even * Odd -> Even
|
||||
Integer * Odd -> ?
|
||||
Odd * Odd -> Odd
|
||||
Even * Even -> Even
|
||||
Integer * Integer -> Even if Integer + Integer = Odd
|
||||
otherwise -> ?
|
||||
"""
|
||||
if expr.is_number:
|
||||
return _EvenPredicate_number(expr, assumptions)
|
||||
even, odd, irrational, acc = False, 0, False, 1
|
||||
for arg in expr.args:
|
||||
# check for all integers and at least one even
|
||||
if ask(Q.integer(arg), assumptions):
|
||||
if ask(Q.even(arg), assumptions):
|
||||
even = True
|
||||
elif ask(Q.odd(arg), assumptions):
|
||||
odd += 1
|
||||
elif not even and acc != 1:
|
||||
if ask(Q.odd(acc + arg), assumptions):
|
||||
even = True
|
||||
elif ask(Q.irrational(arg), assumptions):
|
||||
# one irrational makes the result False
|
||||
# two makes it undefined
|
||||
if irrational:
|
||||
break
|
||||
irrational = True
|
||||
else:
|
||||
break
|
||||
acc = arg
|
||||
else:
|
||||
if irrational:
|
||||
return False
|
||||
if even:
|
||||
return True
|
||||
if odd == len(expr.args):
|
||||
return False
|
||||
|
||||
@EvenPredicate.register(Add)
|
||||
def _(expr, assumptions):
|
||||
"""
|
||||
Even + Odd -> Odd
|
||||
Even + Even -> Even
|
||||
Odd + Odd -> Even
|
||||
|
||||
"""
|
||||
if expr.is_number:
|
||||
return _EvenPredicate_number(expr, assumptions)
|
||||
_result = True
|
||||
for arg in expr.args:
|
||||
if ask(Q.even(arg), assumptions):
|
||||
pass
|
||||
elif ask(Q.odd(arg), assumptions):
|
||||
_result = not _result
|
||||
else:
|
||||
break
|
||||
else:
|
||||
return _result
|
||||
|
||||
@EvenPredicate.register(Pow)
|
||||
def _(expr, assumptions):
|
||||
if expr.is_number:
|
||||
return _EvenPredicate_number(expr, assumptions)
|
||||
if ask(Q.integer(expr.exp), assumptions):
|
||||
if ask(Q.positive(expr.exp), assumptions):
|
||||
return ask(Q.even(expr.base), assumptions)
|
||||
elif ask(~Q.negative(expr.exp) & Q.odd(expr.base), assumptions):
|
||||
return False
|
||||
elif expr.base is S.NegativeOne:
|
||||
return False
|
||||
|
||||
@EvenPredicate.register(Integer)
|
||||
def _(expr, assumptions):
|
||||
return not bool(expr.p & 1)
|
||||
|
||||
@EvenPredicate.register_many(Rational, Infinity, NegativeInfinity, ImaginaryUnit)
|
||||
def _(expr, assumptions):
|
||||
return False
|
||||
|
||||
@EvenPredicate.register(NumberSymbol)
|
||||
def _(expr, assumptions):
|
||||
return _EvenPredicate_number(expr, assumptions)
|
||||
|
||||
@EvenPredicate.register(Abs)
|
||||
def _(expr, assumptions):
|
||||
if ask(Q.real(expr.args[0]), assumptions):
|
||||
return ask(Q.even(expr.args[0]), assumptions)
|
||||
|
||||
@EvenPredicate.register(re)
|
||||
def _(expr, assumptions):
|
||||
if ask(Q.real(expr.args[0]), assumptions):
|
||||
return ask(Q.even(expr.args[0]), assumptions)
|
||||
|
||||
@EvenPredicate.register(im)
|
||||
def _(expr, assumptions):
|
||||
if ask(Q.real(expr.args[0]), assumptions):
|
||||
return True
|
||||
|
||||
@EvenPredicate.register(NaN)
|
||||
def _(expr, assumptions):
|
||||
return None
|
||||
|
||||
|
||||
# OddPredicate
|
||||
|
||||
@OddPredicate.register(Expr)
|
||||
def _(expr, assumptions):
|
||||
ret = expr.is_odd
|
||||
if ret is None:
|
||||
raise MDNotImplementedError
|
||||
return ret
|
||||
|
||||
@OddPredicate.register(Basic)
|
||||
def _(expr, assumptions):
|
||||
_integer = ask(Q.integer(expr), assumptions)
|
||||
if _integer:
|
||||
_even = ask(Q.even(expr), assumptions)
|
||||
if _even is None:
|
||||
return None
|
||||
return not _even
|
||||
return _integer
|
||||
@@ -0,0 +1,440 @@
|
||||
"""
|
||||
Handlers related to order relations: positive, negative, etc.
|
||||
"""
|
||||
|
||||
from sympy.assumptions import Q, ask
|
||||
from sympy.core import Add, Basic, Expr, Mul, Pow, S
|
||||
from sympy.core.logic import fuzzy_not, fuzzy_and, fuzzy_or
|
||||
from sympy.core.numbers import E, ImaginaryUnit, NaN, I, pi
|
||||
from sympy.functions import Abs, acos, acot, asin, atan, exp, factorial, log
|
||||
from sympy.matrices import Determinant, Trace
|
||||
from sympy.matrices.expressions.matexpr import MatrixElement
|
||||
|
||||
from sympy.multipledispatch import MDNotImplementedError
|
||||
|
||||
from ..predicates.order import (NegativePredicate, NonNegativePredicate,
|
||||
NonZeroPredicate, ZeroPredicate, NonPositivePredicate, PositivePredicate,
|
||||
ExtendedNegativePredicate, ExtendedNonNegativePredicate,
|
||||
ExtendedNonPositivePredicate, ExtendedNonZeroPredicate,
|
||||
ExtendedPositivePredicate,)
|
||||
|
||||
|
||||
# NegativePredicate
|
||||
|
||||
def _NegativePredicate_number(expr, assumptions):
|
||||
r, i = expr.as_real_imag()
|
||||
|
||||
if r == S.NaN or i == S.NaN:
|
||||
return None
|
||||
|
||||
# If the imaginary part can symbolically be shown to be zero then
|
||||
# we just evaluate the real part; otherwise we evaluate the imaginary
|
||||
# part to see if it actually evaluates to zero and if it does then
|
||||
# we make the comparison between the real part and zero.
|
||||
if not i:
|
||||
r = r.evalf(2)
|
||||
if r._prec != 1:
|
||||
return r < 0
|
||||
else:
|
||||
i = i.evalf(2)
|
||||
if i._prec != 1:
|
||||
if i != 0:
|
||||
return False
|
||||
r = r.evalf(2)
|
||||
if r._prec != 1:
|
||||
return r < 0
|
||||
|
||||
@NegativePredicate.register(Basic)
|
||||
def _(expr, assumptions):
|
||||
if expr.is_number:
|
||||
return _NegativePredicate_number(expr, assumptions)
|
||||
|
||||
@NegativePredicate.register(Expr)
|
||||
def _(expr, assumptions):
|
||||
ret = expr.is_negative
|
||||
if ret is None:
|
||||
raise MDNotImplementedError
|
||||
return ret
|
||||
|
||||
@NegativePredicate.register(Add)
|
||||
def _(expr, assumptions):
|
||||
"""
|
||||
Positive + Positive -> Positive,
|
||||
Negative + Negative -> Negative
|
||||
"""
|
||||
if expr.is_number:
|
||||
return _NegativePredicate_number(expr, assumptions)
|
||||
|
||||
r = ask(Q.real(expr), assumptions)
|
||||
if r is not True:
|
||||
return r
|
||||
|
||||
nonpos = 0
|
||||
for arg in expr.args:
|
||||
if ask(Q.negative(arg), assumptions) is not True:
|
||||
if ask(Q.positive(arg), assumptions) is False:
|
||||
nonpos += 1
|
||||
else:
|
||||
break
|
||||
else:
|
||||
if nonpos < len(expr.args):
|
||||
return True
|
||||
|
||||
@NegativePredicate.register(Mul)
|
||||
def _(expr, assumptions):
|
||||
if expr.is_number:
|
||||
return _NegativePredicate_number(expr, assumptions)
|
||||
result = None
|
||||
for arg in expr.args:
|
||||
if result is None:
|
||||
result = False
|
||||
if ask(Q.negative(arg), assumptions):
|
||||
result = not result
|
||||
elif ask(Q.positive(arg), assumptions):
|
||||
pass
|
||||
else:
|
||||
return
|
||||
return result
|
||||
|
||||
@NegativePredicate.register(Pow)
|
||||
def _(expr, assumptions):
|
||||
"""
|
||||
Real ** Even -> NonNegative
|
||||
Real ** Odd -> same_as_base
|
||||
NonNegative ** Positive -> NonNegative
|
||||
"""
|
||||
if expr.base == E:
|
||||
# Exponential is always positive:
|
||||
if ask(Q.real(expr.exp), assumptions):
|
||||
return False
|
||||
return
|
||||
|
||||
if expr.is_number:
|
||||
return _NegativePredicate_number(expr, assumptions)
|
||||
if ask(Q.real(expr.base), assumptions):
|
||||
if ask(Q.positive(expr.base), assumptions):
|
||||
if ask(Q.real(expr.exp), assumptions):
|
||||
return False
|
||||
if ask(Q.even(expr.exp), assumptions):
|
||||
return False
|
||||
if ask(Q.odd(expr.exp), assumptions):
|
||||
return ask(Q.negative(expr.base), assumptions)
|
||||
|
||||
@NegativePredicate.register_many(Abs, ImaginaryUnit)
|
||||
def _(expr, assumptions):
|
||||
return False
|
||||
|
||||
@NegativePredicate.register(exp)
|
||||
def _(expr, assumptions):
|
||||
if ask(Q.real(expr.exp), assumptions):
|
||||
return False
|
||||
raise MDNotImplementedError
|
||||
|
||||
|
||||
# NonNegativePredicate
|
||||
|
||||
@NonNegativePredicate.register(Basic)
|
||||
def _(expr, assumptions):
|
||||
if expr.is_number:
|
||||
notnegative = fuzzy_not(_NegativePredicate_number(expr, assumptions))
|
||||
if notnegative:
|
||||
return ask(Q.real(expr), assumptions)
|
||||
else:
|
||||
return notnegative
|
||||
|
||||
@NonNegativePredicate.register(Expr)
|
||||
def _(expr, assumptions):
|
||||
ret = expr.is_nonnegative
|
||||
if ret is None:
|
||||
raise MDNotImplementedError
|
||||
return ret
|
||||
|
||||
|
||||
# NonZeroPredicate
|
||||
|
||||
@NonZeroPredicate.register(Expr)
|
||||
def _(expr, assumptions):
|
||||
ret = expr.is_nonzero
|
||||
if ret is None:
|
||||
raise MDNotImplementedError
|
||||
return ret
|
||||
|
||||
@NonZeroPredicate.register(Basic)
|
||||
def _(expr, assumptions):
|
||||
if ask(Q.real(expr)) is False:
|
||||
return False
|
||||
if expr.is_number:
|
||||
# if there are no symbols just evalf
|
||||
i = expr.evalf(2)
|
||||
def nonz(i):
|
||||
if i._prec != 1:
|
||||
return i != 0
|
||||
return fuzzy_or(nonz(i) for i in i.as_real_imag())
|
||||
|
||||
@NonZeroPredicate.register(Add)
|
||||
def _(expr, assumptions):
|
||||
if all(ask(Q.positive(x), assumptions) for x in expr.args) \
|
||||
or all(ask(Q.negative(x), assumptions) for x in expr.args):
|
||||
return True
|
||||
|
||||
@NonZeroPredicate.register(Mul)
|
||||
def _(expr, assumptions):
|
||||
for arg in expr.args:
|
||||
result = ask(Q.nonzero(arg), assumptions)
|
||||
if result:
|
||||
continue
|
||||
return result
|
||||
return True
|
||||
|
||||
@NonZeroPredicate.register(Pow)
|
||||
def _(expr, assumptions):
|
||||
return ask(Q.nonzero(expr.base), assumptions)
|
||||
|
||||
@NonZeroPredicate.register(Abs)
|
||||
def _(expr, assumptions):
|
||||
return ask(Q.nonzero(expr.args[0]), assumptions)
|
||||
|
||||
@NonZeroPredicate.register(NaN)
|
||||
def _(expr, assumptions):
|
||||
return None
|
||||
|
||||
|
||||
# ZeroPredicate
|
||||
|
||||
@ZeroPredicate.register(Expr)
|
||||
def _(expr, assumptions):
|
||||
ret = expr.is_zero
|
||||
if ret is None:
|
||||
raise MDNotImplementedError
|
||||
return ret
|
||||
|
||||
@ZeroPredicate.register(Basic)
|
||||
def _(expr, assumptions):
|
||||
return fuzzy_and([fuzzy_not(ask(Q.nonzero(expr), assumptions)),
|
||||
ask(Q.real(expr), assumptions)])
|
||||
|
||||
@ZeroPredicate.register(Mul)
|
||||
def _(expr, assumptions):
|
||||
# TODO: This should be deducible from the nonzero handler
|
||||
return fuzzy_or(ask(Q.zero(arg), assumptions) for arg in expr.args)
|
||||
|
||||
|
||||
# NonPositivePredicate
|
||||
|
||||
@NonPositivePredicate.register(Expr)
|
||||
def _(expr, assumptions):
|
||||
ret = expr.is_nonpositive
|
||||
if ret is None:
|
||||
raise MDNotImplementedError
|
||||
return ret
|
||||
|
||||
@NonPositivePredicate.register(Basic)
|
||||
def _(expr, assumptions):
|
||||
if expr.is_number:
|
||||
notpositive = fuzzy_not(_PositivePredicate_number(expr, assumptions))
|
||||
if notpositive:
|
||||
return ask(Q.real(expr), assumptions)
|
||||
else:
|
||||
return notpositive
|
||||
|
||||
|
||||
# PositivePredicate
|
||||
|
||||
def _PositivePredicate_number(expr, assumptions):
|
||||
r, i = expr.as_real_imag()
|
||||
# If the imaginary part can symbolically be shown to be zero then
|
||||
# we just evaluate the real part; otherwise we evaluate the imaginary
|
||||
# part to see if it actually evaluates to zero and if it does then
|
||||
# we make the comparison between the real part and zero.
|
||||
if not i:
|
||||
r = r.evalf(2)
|
||||
if r._prec != 1:
|
||||
return r > 0
|
||||
else:
|
||||
i = i.evalf(2)
|
||||
if i._prec != 1:
|
||||
if i != 0:
|
||||
return False
|
||||
r = r.evalf(2)
|
||||
if r._prec != 1:
|
||||
return r > 0
|
||||
|
||||
@PositivePredicate.register(Expr)
|
||||
def _(expr, assumptions):
|
||||
ret = expr.is_positive
|
||||
if ret is None:
|
||||
raise MDNotImplementedError
|
||||
return ret
|
||||
|
||||
@PositivePredicate.register(Basic)
|
||||
def _(expr, assumptions):
|
||||
if expr.is_number:
|
||||
return _PositivePredicate_number(expr, assumptions)
|
||||
|
||||
@PositivePredicate.register(Mul)
|
||||
def _(expr, assumptions):
|
||||
if expr.is_number:
|
||||
return _PositivePredicate_number(expr, assumptions)
|
||||
result = True
|
||||
for arg in expr.args:
|
||||
if ask(Q.positive(arg), assumptions):
|
||||
continue
|
||||
elif ask(Q.negative(arg), assumptions):
|
||||
result = result ^ True
|
||||
else:
|
||||
return
|
||||
return result
|
||||
|
||||
@PositivePredicate.register(Add)
|
||||
def _(expr, assumptions):
|
||||
if expr.is_number:
|
||||
return _PositivePredicate_number(expr, assumptions)
|
||||
|
||||
r = ask(Q.real(expr), assumptions)
|
||||
if r is not True:
|
||||
return r
|
||||
|
||||
nonneg = 0
|
||||
for arg in expr.args:
|
||||
if ask(Q.positive(arg), assumptions) is not True:
|
||||
if ask(Q.negative(arg), assumptions) is False:
|
||||
nonneg += 1
|
||||
else:
|
||||
break
|
||||
else:
|
||||
if nonneg < len(expr.args):
|
||||
return True
|
||||
|
||||
@PositivePredicate.register(Pow)
|
||||
def _(expr, assumptions):
|
||||
if expr.base == E:
|
||||
if ask(Q.real(expr.exp), assumptions):
|
||||
return True
|
||||
if ask(Q.imaginary(expr.exp), assumptions):
|
||||
return ask(Q.even(expr.exp/(I*pi)), assumptions)
|
||||
return
|
||||
|
||||
if expr.is_number:
|
||||
return _PositivePredicate_number(expr, assumptions)
|
||||
if ask(Q.positive(expr.base), assumptions):
|
||||
if ask(Q.real(expr.exp), assumptions):
|
||||
return True
|
||||
if ask(Q.negative(expr.base), assumptions):
|
||||
if ask(Q.even(expr.exp), assumptions):
|
||||
return True
|
||||
if ask(Q.odd(expr.exp), assumptions):
|
||||
return False
|
||||
|
||||
@PositivePredicate.register(exp)
|
||||
def _(expr, assumptions):
|
||||
if ask(Q.real(expr.exp), assumptions):
|
||||
return True
|
||||
if ask(Q.imaginary(expr.exp), assumptions):
|
||||
return ask(Q.even(expr.exp/(I*pi)), assumptions)
|
||||
|
||||
@PositivePredicate.register(log)
|
||||
def _(expr, assumptions):
|
||||
r = ask(Q.real(expr.args[0]), assumptions)
|
||||
if r is not True:
|
||||
return r
|
||||
if ask(Q.positive(expr.args[0] - 1), assumptions):
|
||||
return True
|
||||
if ask(Q.negative(expr.args[0] - 1), assumptions):
|
||||
return False
|
||||
|
||||
@PositivePredicate.register(factorial)
|
||||
def _(expr, assumptions):
|
||||
x = expr.args[0]
|
||||
if ask(Q.integer(x) & Q.positive(x), assumptions):
|
||||
return True
|
||||
|
||||
@PositivePredicate.register(ImaginaryUnit)
|
||||
def _(expr, assumptions):
|
||||
return False
|
||||
|
||||
@PositivePredicate.register(Abs)
|
||||
def _(expr, assumptions):
|
||||
return ask(Q.nonzero(expr), assumptions)
|
||||
|
||||
@PositivePredicate.register(Trace)
|
||||
def _(expr, assumptions):
|
||||
if ask(Q.positive_definite(expr.arg), assumptions):
|
||||
return True
|
||||
|
||||
@PositivePredicate.register(Determinant)
|
||||
def _(expr, assumptions):
|
||||
if ask(Q.positive_definite(expr.arg), assumptions):
|
||||
return True
|
||||
|
||||
@PositivePredicate.register(MatrixElement)
|
||||
def _(expr, assumptions):
|
||||
if (expr.i == expr.j
|
||||
and ask(Q.positive_definite(expr.parent), assumptions)):
|
||||
return True
|
||||
|
||||
@PositivePredicate.register(atan)
|
||||
def _(expr, assumptions):
|
||||
return ask(Q.positive(expr.args[0]), assumptions)
|
||||
|
||||
@PositivePredicate.register(asin)
|
||||
def _(expr, assumptions):
|
||||
x = expr.args[0]
|
||||
if ask(Q.positive(x) & Q.nonpositive(x - 1), assumptions):
|
||||
return True
|
||||
if ask(Q.negative(x) & Q.nonnegative(x + 1), assumptions):
|
||||
return False
|
||||
|
||||
@PositivePredicate.register(acos)
|
||||
def _(expr, assumptions):
|
||||
x = expr.args[0]
|
||||
if ask(Q.nonpositive(x - 1) & Q.nonnegative(x + 1), assumptions):
|
||||
return True
|
||||
|
||||
@PositivePredicate.register(acot)
|
||||
def _(expr, assumptions):
|
||||
return ask(Q.real(expr.args[0]), assumptions)
|
||||
|
||||
@PositivePredicate.register(NaN)
|
||||
def _(expr, assumptions):
|
||||
return None
|
||||
|
||||
|
||||
# ExtendedNegativePredicate
|
||||
|
||||
@ExtendedNegativePredicate.register(object)
|
||||
def _(expr, assumptions):
|
||||
return ask(Q.negative(expr) | Q.negative_infinite(expr), assumptions)
|
||||
|
||||
|
||||
# ExtendedPositivePredicate
|
||||
|
||||
@ExtendedPositivePredicate.register(object)
|
||||
def _(expr, assumptions):
|
||||
return ask(Q.positive(expr) | Q.positive_infinite(expr), assumptions)
|
||||
|
||||
|
||||
# ExtendedNonZeroPredicate
|
||||
|
||||
@ExtendedNonZeroPredicate.register(object)
|
||||
def _(expr, assumptions):
|
||||
return ask(
|
||||
Q.negative_infinite(expr) | Q.negative(expr) | Q.positive(expr) | Q.positive_infinite(expr),
|
||||
assumptions)
|
||||
|
||||
|
||||
# ExtendedNonPositivePredicate
|
||||
|
||||
@ExtendedNonPositivePredicate.register(object)
|
||||
def _(expr, assumptions):
|
||||
return ask(
|
||||
Q.negative_infinite(expr) | Q.negative(expr) | Q.zero(expr),
|
||||
assumptions)
|
||||
|
||||
|
||||
# ExtendedNonNegativePredicate
|
||||
|
||||
@ExtendedNonNegativePredicate.register(object)
|
||||
def _(expr, assumptions):
|
||||
return ask(
|
||||
Q.zero(expr) | Q.positive(expr) | Q.positive_infinite(expr),
|
||||
assumptions)
|
||||
@@ -0,0 +1,816 @@
|
||||
"""
|
||||
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, ask_all, ask_any
|
||||
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(Add)
|
||||
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(Pow)
|
||||
def _(expr,assumptions):
|
||||
if expr.is_number:
|
||||
return _IntegerPredicate_number(expr, assumptions)
|
||||
if ask_all(~Q.zero(expr.base), Q.finite(expr.base), Q.zero(expr.exp), assumptions=assumptions):
|
||||
return True
|
||||
if ask_all(Q.integer(expr.base), Q.integer(expr.exp), assumptions=assumptions):
|
||||
if ask_any(Q.positive(expr.exp), Q.nonnegative(expr.exp) & ~Q.zero(expr.base), Q.zero(expr.base-1), Q.zero(expr.base+1), assumptions=assumptions):
|
||||
return True
|
||||
|
||||
@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):
|
||||
if ask(Q.integer(expr.args[0]), assumptions):
|
||||
return True
|
||||
|
||||
@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.zero(x), assumptions)
|
||||
return
|
||||
|
||||
is_exp_integer = ask(Q.integer(expr.exp), assumptions)
|
||||
if is_exp_integer:
|
||||
is_base_rational = ask(Q.rational(expr.base),assumptions)
|
||||
if is_base_rational:
|
||||
is_base_zero = ask(Q.zero(expr.base),assumptions)
|
||||
if is_base_zero is False:
|
||||
return True
|
||||
if is_base_zero and ask(Q.positive(expr.exp)):
|
||||
return True
|
||||
if ask(Q.algebraic(expr.base),assumptions) is False:
|
||||
return ask(Q.zero(expr.exp), assumptions)
|
||||
if ask(Q.irrational(expr.base),assumptions) and ask(Q.eq(expr.exp,-1)):
|
||||
return False
|
||||
return
|
||||
elif ask(Q.rational(expr.exp), assumptions):
|
||||
if ask(Q.prime(expr.base), assumptions) and is_exp_integer is False:
|
||||
return False
|
||||
if ask(Q.zero(expr.base)) and ask(Q.positive(expr.exp)):
|
||||
return True
|
||||
if ask(Q.eq(expr.base,1)):
|
||||
return True
|
||||
|
||||
@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
|
||||
* Negative**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 ask(Q.zero(expr.base), assumptions) is not False:
|
||||
if ask(Q.positive(expr.exp), assumptions):
|
||||
return True
|
||||
return
|
||||
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
|
||||
|
||||
@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
|
||||
if expr.base == pi:
|
||||
if ask(Q.integer(expr.exp), assumptions) and ask(Q.positive(expr.exp), assumptions):
|
||||
return False
|
||||
return
|
||||
exp_rational = ask(Q.rational(expr.exp), assumptions)
|
||||
base_algebraic = ask(Q.algebraic(expr.base), assumptions)
|
||||
exp_algebraic = ask(Q.algebraic(expr.exp),assumptions)
|
||||
if base_algebraic and exp_algebraic:
|
||||
if exp_rational:
|
||||
return True
|
||||
# Check based on the Gelfond-Schneider theorem:
|
||||
# If the base is algebraic and not equal to 0 or 1, and the exponent
|
||||
# is irrational,then the result is transcendental.
|
||||
if ask(Q.ne(expr.base,0) & Q.ne(expr.base,1)) and exp_rational is False:
|
||||
return False
|
||||
|
||||
@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)
|
||||
Reference in New Issue
Block a user