916 lines
27 KiB
Python
916 lines
27 KiB
Python
from sympy.core import Function, S, sympify, NumberKind
|
|
from sympy.utilities.iterables import sift
|
|
from sympy.core.add import Add
|
|
from sympy.core.containers import Tuple
|
|
from sympy.core.operations import LatticeOp, ShortCircuit
|
|
from sympy.core.function import (Application, Lambda,
|
|
ArgumentIndexError)
|
|
from sympy.core.expr import Expr
|
|
from sympy.core.exprtools import factor_terms
|
|
from sympy.core.mod import Mod
|
|
from sympy.core.mul import Mul
|
|
from sympy.core.numbers import Rational
|
|
from sympy.core.power import Pow
|
|
from sympy.core.relational import Eq, Relational
|
|
from sympy.core.singleton import Singleton
|
|
from sympy.core.sorting import ordered
|
|
from sympy.core.symbol import Dummy
|
|
from sympy.core.rules import Transform
|
|
from sympy.core.logic import fuzzy_and, fuzzy_or, _torf
|
|
from sympy.core.traversal import walk
|
|
from sympy.core.numbers import Integer
|
|
from sympy.logic.boolalg import And, Or
|
|
|
|
|
|
def _minmax_as_Piecewise(op, *args):
|
|
# helper for Min/Max rewrite as Piecewise
|
|
from sympy.functions.elementary.piecewise import Piecewise
|
|
ec = []
|
|
for i, a in enumerate(args):
|
|
c = [Relational(a, args[j], op) for j in range(i + 1, len(args))]
|
|
ec.append((a, And(*c)))
|
|
return Piecewise(*ec)
|
|
|
|
|
|
class IdentityFunction(Lambda, metaclass=Singleton):
|
|
"""
|
|
The identity function
|
|
|
|
Examples
|
|
========
|
|
|
|
>>> from sympy import Id, Symbol
|
|
>>> x = Symbol('x')
|
|
>>> Id(x)
|
|
x
|
|
|
|
"""
|
|
|
|
_symbol = Dummy('x')
|
|
|
|
@property
|
|
def signature(self):
|
|
return Tuple(self._symbol)
|
|
|
|
@property
|
|
def expr(self):
|
|
return self._symbol
|
|
|
|
|
|
Id = S.IdentityFunction
|
|
|
|
###############################################################################
|
|
############################# ROOT and SQUARE ROOT FUNCTION ###################
|
|
###############################################################################
|
|
|
|
|
|
def sqrt(arg, evaluate=None):
|
|
"""Returns the principal square root.
|
|
|
|
Parameters
|
|
==========
|
|
|
|
evaluate : bool, optional
|
|
The parameter determines if the expression should be evaluated.
|
|
If ``None``, its value is taken from
|
|
``global_parameters.evaluate``.
|
|
|
|
Examples
|
|
========
|
|
|
|
>>> from sympy import sqrt, Symbol, S
|
|
>>> x = Symbol('x')
|
|
|
|
>>> sqrt(x)
|
|
sqrt(x)
|
|
|
|
>>> sqrt(x)**2
|
|
x
|
|
|
|
Note that sqrt(x**2) does not simplify to x.
|
|
|
|
>>> sqrt(x**2)
|
|
sqrt(x**2)
|
|
|
|
This is because the two are not equal to each other in general.
|
|
For example, consider x == -1:
|
|
|
|
>>> from sympy import Eq
|
|
>>> Eq(sqrt(x**2), x).subs(x, -1)
|
|
False
|
|
|
|
This is because sqrt computes the principal square root, so the square may
|
|
put the argument in a different branch. This identity does hold if x is
|
|
positive:
|
|
|
|
>>> y = Symbol('y', positive=True)
|
|
>>> sqrt(y**2)
|
|
y
|
|
|
|
You can force this simplification by using the powdenest() function with
|
|
the force option set to True:
|
|
|
|
>>> from sympy import powdenest
|
|
>>> sqrt(x**2)
|
|
sqrt(x**2)
|
|
>>> powdenest(sqrt(x**2), force=True)
|
|
x
|
|
|
|
To get both branches of the square root you can use the rootof function:
|
|
|
|
>>> from sympy import rootof
|
|
|
|
>>> [rootof(x**2-3,i) for i in (0,1)]
|
|
[-sqrt(3), sqrt(3)]
|
|
|
|
Although ``sqrt`` is printed, there is no ``sqrt`` function so looking for
|
|
``sqrt`` in an expression will fail:
|
|
|
|
>>> from sympy.utilities.misc import func_name
|
|
>>> func_name(sqrt(x))
|
|
'Pow'
|
|
>>> sqrt(x).has(sqrt)
|
|
False
|
|
|
|
To find ``sqrt`` look for ``Pow`` with an exponent of ``1/2``:
|
|
|
|
>>> (x + 1/sqrt(x)).find(lambda i: i.is_Pow and abs(i.exp) is S.Half)
|
|
{1/sqrt(x)}
|
|
|
|
See Also
|
|
========
|
|
|
|
sympy.polys.rootoftools.rootof, root, real_root
|
|
|
|
References
|
|
==========
|
|
|
|
.. [1] https://en.wikipedia.org/wiki/Square_root
|
|
.. [2] https://en.wikipedia.org/wiki/Principal_value
|
|
"""
|
|
# arg = sympify(arg) is handled by Pow
|
|
return Pow(arg, S.Half, evaluate=evaluate)
|
|
|
|
|
|
def cbrt(arg, evaluate=None):
|
|
"""Returns the principal cube root.
|
|
|
|
Parameters
|
|
==========
|
|
|
|
evaluate : bool, optional
|
|
The parameter determines if the expression should be evaluated.
|
|
If ``None``, its value is taken from
|
|
``global_parameters.evaluate``.
|
|
|
|
Examples
|
|
========
|
|
|
|
>>> from sympy import cbrt, Symbol
|
|
>>> x = Symbol('x')
|
|
|
|
>>> cbrt(x)
|
|
x**(1/3)
|
|
|
|
>>> cbrt(x)**3
|
|
x
|
|
|
|
Note that cbrt(x**3) does not simplify to x.
|
|
|
|
>>> cbrt(x**3)
|
|
(x**3)**(1/3)
|
|
|
|
This is because the two are not equal to each other in general.
|
|
For example, consider `x == -1`:
|
|
|
|
>>> from sympy import Eq
|
|
>>> Eq(cbrt(x**3), x).subs(x, -1)
|
|
False
|
|
|
|
This is because cbrt computes the principal cube root, this
|
|
identity does hold if `x` is positive:
|
|
|
|
>>> y = Symbol('y', positive=True)
|
|
>>> cbrt(y**3)
|
|
y
|
|
|
|
See Also
|
|
========
|
|
|
|
sympy.polys.rootoftools.rootof, root, real_root
|
|
|
|
References
|
|
==========
|
|
|
|
.. [1] https://en.wikipedia.org/wiki/Cube_root
|
|
.. [2] https://en.wikipedia.org/wiki/Principal_value
|
|
|
|
"""
|
|
return Pow(arg, Rational(1, 3), evaluate=evaluate)
|
|
|
|
|
|
def root(arg, n, k=0, evaluate=None):
|
|
r"""Returns the *k*-th *n*-th root of ``arg``.
|
|
|
|
Parameters
|
|
==========
|
|
|
|
k : int, optional
|
|
Should be an integer in $\{0, 1, ..., n-1\}$.
|
|
Defaults to the principal root if $0$.
|
|
|
|
evaluate : bool, optional
|
|
The parameter determines if the expression should be evaluated.
|
|
If ``None``, its value is taken from
|
|
``global_parameters.evaluate``.
|
|
|
|
Examples
|
|
========
|
|
|
|
>>> from sympy import root, Rational
|
|
>>> from sympy.abc import x, n
|
|
|
|
>>> root(x, 2)
|
|
sqrt(x)
|
|
|
|
>>> root(x, 3)
|
|
x**(1/3)
|
|
|
|
>>> root(x, n)
|
|
x**(1/n)
|
|
|
|
>>> root(x, -Rational(2, 3))
|
|
x**(-3/2)
|
|
|
|
To get the k-th n-th root, specify k:
|
|
|
|
>>> root(-2, 3, 2)
|
|
-(-1)**(2/3)*2**(1/3)
|
|
|
|
To get all n n-th roots you can use the rootof function.
|
|
The following examples show the roots of unity for n
|
|
equal 2, 3 and 4:
|
|
|
|
>>> from sympy import rootof
|
|
|
|
>>> [rootof(x**2 - 1, i) for i in range(2)]
|
|
[-1, 1]
|
|
|
|
>>> [rootof(x**3 - 1,i) for i in range(3)]
|
|
[1, -1/2 - sqrt(3)*I/2, -1/2 + sqrt(3)*I/2]
|
|
|
|
>>> [rootof(x**4 - 1,i) for i in range(4)]
|
|
[-1, 1, -I, I]
|
|
|
|
SymPy, like other symbolic algebra systems, returns the
|
|
complex root of negative numbers. This is the principal
|
|
root and differs from the text-book result that one might
|
|
be expecting. For example, the cube root of -8 does not
|
|
come back as -2:
|
|
|
|
>>> root(-8, 3)
|
|
2*(-1)**(1/3)
|
|
|
|
The real_root function can be used to either make the principal
|
|
result real (or simply to return the real root directly):
|
|
|
|
>>> from sympy import real_root
|
|
>>> real_root(_)
|
|
-2
|
|
>>> real_root(-32, 5)
|
|
-2
|
|
|
|
Alternatively, the n//2-th n-th root of a negative number can be
|
|
computed with root:
|
|
|
|
>>> root(-32, 5, 5//2)
|
|
-2
|
|
|
|
See Also
|
|
========
|
|
|
|
sympy.polys.rootoftools.rootof
|
|
sympy.core.power.integer_nthroot
|
|
sqrt, real_root
|
|
|
|
References
|
|
==========
|
|
|
|
.. [1] https://en.wikipedia.org/wiki/Square_root
|
|
.. [2] https://en.wikipedia.org/wiki/Real_root
|
|
.. [3] https://en.wikipedia.org/wiki/Root_of_unity
|
|
.. [4] https://en.wikipedia.org/wiki/Principal_value
|
|
.. [5] https://mathworld.wolfram.com/CubeRoot.html
|
|
|
|
"""
|
|
n = sympify(n)
|
|
if k:
|
|
return Mul(Pow(arg, S.One/n, evaluate=evaluate), S.NegativeOne**(2*k/n), evaluate=evaluate)
|
|
return Pow(arg, 1/n, evaluate=evaluate)
|
|
|
|
|
|
def real_root(arg, n=None, evaluate=None):
|
|
r"""Return the real *n*'th-root of *arg* if possible.
|
|
|
|
Parameters
|
|
==========
|
|
|
|
n : int or None, optional
|
|
If *n* is ``None``, then all instances of
|
|
$(-n)^{1/\text{odd}}$ will be changed to $-n^{1/\text{odd}}$.
|
|
This will only create a real root of a principal root.
|
|
The presence of other factors may cause the result to not be
|
|
real.
|
|
|
|
evaluate : bool, optional
|
|
The parameter determines if the expression should be evaluated.
|
|
If ``None``, its value is taken from
|
|
``global_parameters.evaluate``.
|
|
|
|
Examples
|
|
========
|
|
|
|
>>> from sympy import root, real_root
|
|
|
|
>>> real_root(-8, 3)
|
|
-2
|
|
>>> root(-8, 3)
|
|
2*(-1)**(1/3)
|
|
>>> real_root(_)
|
|
-2
|
|
|
|
If one creates a non-principal root and applies real_root, the
|
|
result will not be real (so use with caution):
|
|
|
|
>>> root(-8, 3, 2)
|
|
-2*(-1)**(2/3)
|
|
>>> real_root(_)
|
|
-2*(-1)**(2/3)
|
|
|
|
See Also
|
|
========
|
|
|
|
sympy.polys.rootoftools.rootof
|
|
sympy.core.power.integer_nthroot
|
|
root, sqrt
|
|
"""
|
|
from sympy.functions.elementary.complexes import Abs, im, sign
|
|
from sympy.functions.elementary.piecewise import Piecewise
|
|
if n is not None:
|
|
return Piecewise(
|
|
(root(arg, n, evaluate=evaluate), Or(Eq(n, S.One), Eq(n, S.NegativeOne))),
|
|
(Mul(sign(arg), root(Abs(arg), n, evaluate=evaluate), evaluate=evaluate),
|
|
And(Eq(im(arg), S.Zero), Eq(Mod(n, 2), S.One))),
|
|
(root(arg, n, evaluate=evaluate), True))
|
|
rv = sympify(arg)
|
|
n1pow = Transform(lambda x: -(-x.base)**x.exp,
|
|
lambda x:
|
|
x.is_Pow and
|
|
x.base.is_negative and
|
|
x.exp.is_Rational and
|
|
x.exp.p == 1 and x.exp.q % 2)
|
|
return rv.xreplace(n1pow)
|
|
|
|
###############################################################################
|
|
############################# MINIMUM and MAXIMUM #############################
|
|
###############################################################################
|
|
|
|
|
|
class MinMaxBase(Expr, LatticeOp):
|
|
def __new__(cls, *args, **assumptions):
|
|
from sympy.core.parameters import global_parameters
|
|
evaluate = assumptions.pop('evaluate', global_parameters.evaluate)
|
|
args = (sympify(arg) for arg in args)
|
|
|
|
# first standard filter, for cls.zero and cls.identity
|
|
# also reshape Max(a, Max(b, c)) to Max(a, b, c)
|
|
|
|
if evaluate:
|
|
try:
|
|
args = frozenset(cls._new_args_filter(args))
|
|
except ShortCircuit:
|
|
return cls.zero
|
|
# remove redundant args that are easily identified
|
|
args = cls._collapse_arguments(args, **assumptions)
|
|
# find local zeros
|
|
args = cls._find_localzeros(args, **assumptions)
|
|
args = frozenset(args)
|
|
|
|
if not args:
|
|
return cls.identity
|
|
|
|
if len(args) == 1:
|
|
return list(args).pop()
|
|
|
|
# base creation
|
|
obj = Expr.__new__(cls, *ordered(args), **assumptions)
|
|
obj._argset = args
|
|
return obj
|
|
|
|
@classmethod
|
|
def _collapse_arguments(cls, args, **assumptions):
|
|
"""Remove redundant args.
|
|
|
|
Examples
|
|
========
|
|
|
|
>>> from sympy import Min, Max
|
|
>>> from sympy.abc import a, b, c, d, e
|
|
|
|
Any arg in parent that appears in any
|
|
parent-like function in any of the flat args
|
|
of parent can be removed from that sub-arg:
|
|
|
|
>>> Min(a, Max(b, Min(a, c, d)))
|
|
Min(a, Max(b, Min(c, d)))
|
|
|
|
If the arg of parent appears in an opposite-than parent
|
|
function in any of the flat args of parent that function
|
|
can be replaced with the arg:
|
|
|
|
>>> Min(a, Max(b, Min(c, d, Max(a, e))))
|
|
Min(a, Max(b, Min(a, c, d)))
|
|
"""
|
|
if not args:
|
|
return args
|
|
args = list(ordered(args))
|
|
if cls == Min:
|
|
other = Max
|
|
else:
|
|
other = Min
|
|
|
|
# find global comparable max of Max and min of Min if a new
|
|
# value is being introduced in these args at position 0 of
|
|
# the ordered args
|
|
if args[0].is_number:
|
|
sifted = mins, maxs = [], []
|
|
for i in args:
|
|
for v in walk(i, Min, Max):
|
|
if v.args[0].is_comparable:
|
|
sifted[isinstance(v, Max)].append(v)
|
|
small = Min.identity
|
|
for i in mins:
|
|
v = i.args[0]
|
|
if v.is_number and (v < small) == True:
|
|
small = v
|
|
big = Max.identity
|
|
for i in maxs:
|
|
v = i.args[0]
|
|
if v.is_number and (v > big) == True:
|
|
big = v
|
|
# at the point when this function is called from __new__,
|
|
# there may be more than one numeric arg present since
|
|
# local zeros have not been handled yet, so look through
|
|
# more than the first arg
|
|
if cls == Min:
|
|
for arg in args:
|
|
if not arg.is_number:
|
|
break
|
|
if (arg < small) == True:
|
|
small = arg
|
|
elif cls == Max:
|
|
for arg in args:
|
|
if not arg.is_number:
|
|
break
|
|
if (arg > big) == True:
|
|
big = arg
|
|
T = None
|
|
if cls == Min:
|
|
if small != Min.identity:
|
|
other = Max
|
|
T = small
|
|
elif big != Max.identity:
|
|
other = Min
|
|
T = big
|
|
if T is not None:
|
|
# remove numerical redundancy
|
|
for i in range(len(args)):
|
|
a = args[i]
|
|
if isinstance(a, other):
|
|
a0 = a.args[0]
|
|
if ((a0 > T) if other == Max else (a0 < T)) == True:
|
|
args[i] = cls.identity
|
|
|
|
# remove redundant symbolic args
|
|
def do(ai, a):
|
|
if not isinstance(ai, (Min, Max)):
|
|
return ai
|
|
cond = a in ai.args
|
|
if not cond:
|
|
return ai.func(*[do(i, a) for i in ai.args],
|
|
evaluate=False)
|
|
if isinstance(ai, cls):
|
|
return ai.func(*[do(i, a) for i in ai.args if i != a],
|
|
evaluate=False)
|
|
return a
|
|
for i, a in enumerate(args):
|
|
args[i + 1:] = [do(ai, a) for ai in args[i + 1:]]
|
|
|
|
# factor out common elements as for
|
|
# Min(Max(x, y), Max(x, z)) -> Max(x, Min(y, z))
|
|
# and vice versa when swapping Min/Max -- do this only for the
|
|
# easy case where all functions contain something in common;
|
|
# trying to find some optimal subset of args to modify takes
|
|
# too long
|
|
|
|
def factor_minmax(args):
|
|
is_other = lambda arg: isinstance(arg, other)
|
|
other_args, remaining_args = sift(args, is_other, binary=True)
|
|
if not other_args:
|
|
return args
|
|
|
|
# Min(Max(x, y, z), Max(x, y, u, v)) -> {x,y}, ({z}, {u,v})
|
|
arg_sets = [set(arg.args) for arg in other_args]
|
|
common = set.intersection(*arg_sets)
|
|
if not common:
|
|
return args
|
|
|
|
new_other_args = list(common)
|
|
arg_sets_diff = [arg_set - common for arg_set in arg_sets]
|
|
|
|
# If any set is empty after removing common then all can be
|
|
# discarded e.g. Min(Max(a, b, c), Max(a, b)) -> Max(a, b)
|
|
if all(arg_sets_diff):
|
|
other_args_diff = [other(*s, evaluate=False) for s in arg_sets_diff]
|
|
new_other_args.append(cls(*other_args_diff, evaluate=False))
|
|
|
|
other_args_factored = other(*new_other_args, evaluate=False)
|
|
return remaining_args + [other_args_factored]
|
|
|
|
if len(args) > 1:
|
|
args = factor_minmax(args)
|
|
|
|
return args
|
|
|
|
@classmethod
|
|
def _new_args_filter(cls, arg_sequence):
|
|
"""
|
|
Generator filtering args.
|
|
|
|
first standard filter, for cls.zero and cls.identity.
|
|
Also reshape ``Max(a, Max(b, c))`` to ``Max(a, b, c)``,
|
|
and check arguments for comparability
|
|
"""
|
|
for arg in arg_sequence:
|
|
# pre-filter, checking comparability of arguments
|
|
if not isinstance(arg, Expr) or arg.is_extended_real is False or (
|
|
arg.is_number and
|
|
not arg.is_comparable):
|
|
raise ValueError("The argument '%s' is not comparable." % arg)
|
|
|
|
if arg == cls.zero:
|
|
raise ShortCircuit(arg)
|
|
elif arg == cls.identity:
|
|
continue
|
|
elif arg.func == cls:
|
|
yield from arg.args
|
|
else:
|
|
yield arg
|
|
|
|
@classmethod
|
|
def _find_localzeros(cls, values, **options):
|
|
"""
|
|
Sequentially allocate values to localzeros.
|
|
|
|
When a value is identified as being more extreme than another member it
|
|
replaces that member; if this is never true, then the value is simply
|
|
appended to the localzeros.
|
|
"""
|
|
localzeros = set()
|
|
for v in values:
|
|
is_newzero = True
|
|
localzeros_ = list(localzeros)
|
|
for z in localzeros_:
|
|
if id(v) == id(z):
|
|
is_newzero = False
|
|
else:
|
|
con = cls._is_connected(v, z)
|
|
if con:
|
|
is_newzero = False
|
|
if con is True or con == cls:
|
|
localzeros.remove(z)
|
|
localzeros.update([v])
|
|
if is_newzero:
|
|
localzeros.update([v])
|
|
return localzeros
|
|
|
|
@classmethod
|
|
def _is_connected(cls, x, y):
|
|
"""
|
|
Check if x and y are connected somehow.
|
|
"""
|
|
for i in range(2):
|
|
if x == y:
|
|
return True
|
|
t, f = Max, Min
|
|
for op in "><":
|
|
for j in range(2):
|
|
try:
|
|
if op == ">":
|
|
v = x >= y
|
|
else:
|
|
v = x <= y
|
|
except TypeError:
|
|
return False # non-real arg
|
|
if not v.is_Relational:
|
|
return t if v else f
|
|
t, f = f, t
|
|
x, y = y, x
|
|
x, y = y, x # run next pass with reversed order relative to start
|
|
# simplification can be expensive, so be conservative
|
|
# in what is attempted
|
|
x = factor_terms(x - y)
|
|
y = S.Zero
|
|
|
|
return False
|
|
|
|
def _eval_derivative(self, s):
|
|
# f(x).diff(s) -> x.diff(s) * f.fdiff(1)(s)
|
|
i = 0
|
|
l = []
|
|
for a in self.args:
|
|
i += 1
|
|
da = a.diff(s)
|
|
if da.is_zero:
|
|
continue
|
|
try:
|
|
df = self.fdiff(i)
|
|
except ArgumentIndexError:
|
|
df = Function.fdiff(self, i)
|
|
l.append(df * da)
|
|
return Add(*l)
|
|
|
|
def _eval_rewrite_as_Abs(self, *args, **kwargs):
|
|
from sympy.functions.elementary.complexes import Abs
|
|
s = (args[0] + self.func(*args[1:]))/2
|
|
d = abs(args[0] - self.func(*args[1:]))/2
|
|
return (s + d if isinstance(self, Max) else s - d).rewrite(Abs)
|
|
|
|
def evalf(self, n=15, **options):
|
|
return self.func(*[a.evalf(n, **options) for a in self.args])
|
|
|
|
def n(self, *args, **kwargs):
|
|
return self.evalf(*args, **kwargs)
|
|
|
|
_eval_is_algebraic = lambda s: _torf(i.is_algebraic for i in s.args)
|
|
_eval_is_antihermitian = lambda s: _torf(i.is_antihermitian for i in s.args)
|
|
_eval_is_commutative = lambda s: _torf(i.is_commutative for i in s.args)
|
|
_eval_is_complex = lambda s: _torf(i.is_complex for i in s.args)
|
|
_eval_is_composite = lambda s: _torf(i.is_composite for i in s.args)
|
|
_eval_is_even = lambda s: _torf(i.is_even for i in s.args)
|
|
_eval_is_finite = lambda s: _torf(i.is_finite for i in s.args)
|
|
_eval_is_hermitian = lambda s: _torf(i.is_hermitian for i in s.args)
|
|
_eval_is_imaginary = lambda s: _torf(i.is_imaginary for i in s.args)
|
|
_eval_is_infinite = lambda s: _torf(i.is_infinite for i in s.args)
|
|
_eval_is_integer = lambda s: _torf(i.is_integer for i in s.args)
|
|
_eval_is_irrational = lambda s: _torf(i.is_irrational for i in s.args)
|
|
_eval_is_negative = lambda s: _torf(i.is_negative for i in s.args)
|
|
_eval_is_noninteger = lambda s: _torf(i.is_noninteger for i in s.args)
|
|
_eval_is_nonnegative = lambda s: _torf(i.is_nonnegative for i in s.args)
|
|
_eval_is_nonpositive = lambda s: _torf(i.is_nonpositive for i in s.args)
|
|
_eval_is_nonzero = lambda s: _torf(i.is_nonzero for i in s.args)
|
|
_eval_is_odd = lambda s: _torf(i.is_odd for i in s.args)
|
|
_eval_is_polar = lambda s: _torf(i.is_polar for i in s.args)
|
|
_eval_is_positive = lambda s: _torf(i.is_positive for i in s.args)
|
|
_eval_is_prime = lambda s: _torf(i.is_prime for i in s.args)
|
|
_eval_is_rational = lambda s: _torf(i.is_rational for i in s.args)
|
|
_eval_is_real = lambda s: _torf(i.is_real for i in s.args)
|
|
_eval_is_extended_real = lambda s: _torf(i.is_extended_real for i in s.args)
|
|
_eval_is_transcendental = lambda s: _torf(i.is_transcendental for i in s.args)
|
|
_eval_is_zero = lambda s: _torf(i.is_zero for i in s.args)
|
|
|
|
|
|
class Max(MinMaxBase, Application):
|
|
r"""
|
|
Return, if possible, the maximum value of the list.
|
|
|
|
When number of arguments is equal one, then
|
|
return this argument.
|
|
|
|
When number of arguments is equal two, then
|
|
return, if possible, the value from (a, b) that is $\ge$ the other.
|
|
|
|
In common case, when the length of list greater than 2, the task
|
|
is more complicated. Return only the arguments, which are greater
|
|
than others, if it is possible to determine directional relation.
|
|
|
|
If is not possible to determine such a relation, return a partially
|
|
evaluated result.
|
|
|
|
Assumptions are used to make the decision too.
|
|
|
|
Also, only comparable arguments are permitted.
|
|
|
|
It is named ``Max`` and not ``max`` to avoid conflicts
|
|
with the built-in function ``max``.
|
|
|
|
|
|
Examples
|
|
========
|
|
|
|
>>> from sympy import Max, Symbol, oo
|
|
>>> from sympy.abc import x, y, z
|
|
>>> p = Symbol('p', positive=True)
|
|
>>> n = Symbol('n', negative=True)
|
|
|
|
>>> Max(x, -2)
|
|
Max(-2, x)
|
|
>>> Max(x, -2).subs(x, 3)
|
|
3
|
|
>>> Max(p, -2)
|
|
p
|
|
>>> Max(x, y)
|
|
Max(x, y)
|
|
>>> Max(x, y) == Max(y, x)
|
|
True
|
|
>>> Max(x, Max(y, z))
|
|
Max(x, y, z)
|
|
>>> Max(n, 8, p, 7, -oo)
|
|
Max(8, p)
|
|
>>> Max (1, x, oo)
|
|
oo
|
|
|
|
* Algorithm
|
|
|
|
The task can be considered as searching of supremums in the
|
|
directed complete partial orders [1]_.
|
|
|
|
The source values are sequentially allocated by the isolated subsets
|
|
in which supremums are searched and result as Max arguments.
|
|
|
|
If the resulted supremum is single, then it is returned.
|
|
|
|
The isolated subsets are the sets of values which are only the comparable
|
|
with each other in the current set. E.g. natural numbers are comparable with
|
|
each other, but not comparable with the `x` symbol. Another example: the
|
|
symbol `x` with negative assumption is comparable with a natural number.
|
|
|
|
Also there are "least" elements, which are comparable with all others,
|
|
and have a zero property (maximum or minimum for all elements).
|
|
For example, in case of $\infty$, the allocation operation is terminated
|
|
and only this value is returned.
|
|
|
|
Assumption:
|
|
- if $A > B > C$ then $A > C$
|
|
- if $A = B$ then $B$ can be removed
|
|
|
|
References
|
|
==========
|
|
|
|
.. [1] https://en.wikipedia.org/wiki/Directed_complete_partial_order
|
|
.. [2] https://en.wikipedia.org/wiki/Lattice_%28order%29
|
|
|
|
See Also
|
|
========
|
|
|
|
Min : find minimum values
|
|
"""
|
|
zero = S.Infinity
|
|
identity = S.NegativeInfinity
|
|
|
|
def fdiff( self, argindex ):
|
|
from sympy.functions.special.delta_functions import Heaviside
|
|
n = len(self.args)
|
|
if 0 < argindex and argindex <= n:
|
|
argindex -= 1
|
|
if n == 2:
|
|
return Heaviside(self.args[argindex] - self.args[1 - argindex])
|
|
newargs = tuple([self.args[i] for i in range(n) if i != argindex])
|
|
return Heaviside(self.args[argindex] - Max(*newargs))
|
|
else:
|
|
raise ArgumentIndexError(self, argindex)
|
|
|
|
def _eval_rewrite_as_Heaviside(self, *args, **kwargs):
|
|
from sympy.functions.special.delta_functions import Heaviside
|
|
return Add(*[j*Mul(*[Heaviside(j - i) for i in args if i!=j]) \
|
|
for j in args])
|
|
|
|
def _eval_rewrite_as_Piecewise(self, *args, **kwargs):
|
|
return _minmax_as_Piecewise('>=', *args)
|
|
|
|
def _eval_is_positive(self):
|
|
return fuzzy_or(a.is_positive for a in self.args)
|
|
|
|
def _eval_is_nonnegative(self):
|
|
return fuzzy_or(a.is_nonnegative for a in self.args)
|
|
|
|
def _eval_is_negative(self):
|
|
return fuzzy_and(a.is_negative for a in self.args)
|
|
|
|
|
|
class Min(MinMaxBase, Application):
|
|
"""
|
|
Return, if possible, the minimum value of the list.
|
|
It is named ``Min`` and not ``min`` to avoid conflicts
|
|
with the built-in function ``min``.
|
|
|
|
Examples
|
|
========
|
|
|
|
>>> from sympy import Min, Symbol, oo
|
|
>>> from sympy.abc import x, y
|
|
>>> p = Symbol('p', positive=True)
|
|
>>> n = Symbol('n', negative=True)
|
|
|
|
>>> Min(x, -2)
|
|
Min(-2, x)
|
|
>>> Min(x, -2).subs(x, 3)
|
|
-2
|
|
>>> Min(p, -3)
|
|
-3
|
|
>>> Min(x, y)
|
|
Min(x, y)
|
|
>>> Min(n, 8, p, -7, p, oo)
|
|
Min(-7, n)
|
|
|
|
See Also
|
|
========
|
|
|
|
Max : find maximum values
|
|
"""
|
|
zero = S.NegativeInfinity
|
|
identity = S.Infinity
|
|
|
|
def fdiff( self, argindex ):
|
|
from sympy.functions.special.delta_functions import Heaviside
|
|
n = len(self.args)
|
|
if 0 < argindex and argindex <= n:
|
|
argindex -= 1
|
|
if n == 2:
|
|
return Heaviside( self.args[1-argindex] - self.args[argindex] )
|
|
newargs = tuple([ self.args[i] for i in range(n) if i != argindex])
|
|
return Heaviside( Min(*newargs) - self.args[argindex] )
|
|
else:
|
|
raise ArgumentIndexError(self, argindex)
|
|
|
|
def _eval_rewrite_as_Heaviside(self, *args, **kwargs):
|
|
from sympy.functions.special.delta_functions import Heaviside
|
|
return Add(*[j*Mul(*[Heaviside(i-j) for i in args if i!=j]) \
|
|
for j in args])
|
|
|
|
def _eval_rewrite_as_Piecewise(self, *args, **kwargs):
|
|
return _minmax_as_Piecewise('<=', *args)
|
|
|
|
def _eval_is_positive(self):
|
|
return fuzzy_and(a.is_positive for a in self.args)
|
|
|
|
def _eval_is_nonnegative(self):
|
|
return fuzzy_and(a.is_nonnegative for a in self.args)
|
|
|
|
def _eval_is_negative(self):
|
|
return fuzzy_or(a.is_negative for a in self.args)
|
|
|
|
|
|
class Rem(Function):
|
|
"""Returns the remainder when ``p`` is divided by ``q`` where ``p`` is finite
|
|
and ``q`` is not equal to zero. The result, ``p - int(p/q)*q``, has the same sign
|
|
as the divisor.
|
|
|
|
Parameters
|
|
==========
|
|
|
|
p : Expr
|
|
Dividend.
|
|
|
|
q : Expr
|
|
Divisor.
|
|
|
|
Notes
|
|
=====
|
|
|
|
``Rem`` corresponds to the ``%`` operator in C.
|
|
|
|
Examples
|
|
========
|
|
|
|
>>> from sympy.abc import x, y
|
|
>>> from sympy import Rem
|
|
>>> Rem(x**3, y)
|
|
Rem(x**3, y)
|
|
>>> Rem(x**3, y).subs({x: -5, y: 3})
|
|
-2
|
|
|
|
See Also
|
|
========
|
|
|
|
Mod
|
|
"""
|
|
kind = NumberKind
|
|
|
|
@classmethod
|
|
def eval(cls, p, q):
|
|
"""Return the function remainder if both p, q are numbers and q is not
|
|
zero.
|
|
"""
|
|
|
|
if q.is_zero:
|
|
raise ZeroDivisionError("Division by zero")
|
|
if p is S.NaN or q is S.NaN or p.is_finite is False or q.is_finite is False:
|
|
return S.NaN
|
|
if p is S.Zero or p in (q, -q) or (p.is_integer and q == 1):
|
|
return S.Zero
|
|
|
|
if q.is_Number:
|
|
if p.is_Number:
|
|
return p - Integer(p/q)*q
|