|
""" |
|
Singularities |
|
============= |
|
|
|
This module implements algorithms for finding singularities for a function |
|
and identifying types of functions. |
|
|
|
The differential calculus methods in this module include methods to identify |
|
the following function types in the given ``Interval``: |
|
- Increasing |
|
- Strictly Increasing |
|
- Decreasing |
|
- Strictly Decreasing |
|
- Monotonic |
|
|
|
""" |
|
|
|
from sympy.core.power import Pow |
|
from sympy.core.singleton import S |
|
from sympy.core.symbol import Symbol |
|
from sympy.core.sympify import sympify |
|
from sympy.functions.elementary.exponential import log |
|
from sympy.functions.elementary.trigonometric import sec, csc, cot, tan, cos |
|
from sympy.functions.elementary.hyperbolic import ( |
|
sech, csch, coth, tanh, cosh, asech, acsch, atanh, acoth) |
|
from sympy.utilities.misc import filldedent |
|
|
|
|
|
def singularities(expression, symbol, domain=None): |
|
""" |
|
Find singularities of a given function. |
|
|
|
Parameters |
|
========== |
|
|
|
expression : Expr |
|
The target function in which singularities need to be found. |
|
symbol : Symbol |
|
The symbol over the values of which the singularity in |
|
expression in being searched for. |
|
|
|
Returns |
|
======= |
|
|
|
Set |
|
A set of values for ``symbol`` for which ``expression`` has a |
|
singularity. An ``EmptySet`` is returned if ``expression`` has no |
|
singularities for any given value of ``Symbol``. |
|
|
|
Raises |
|
====== |
|
|
|
NotImplementedError |
|
Methods for determining the singularities of this function have |
|
not been developed. |
|
|
|
Notes |
|
===== |
|
|
|
This function does not find non-isolated singularities |
|
nor does it find branch points of the expression. |
|
|
|
Currently supported functions are: |
|
- univariate continuous (real or complex) functions |
|
|
|
References |
|
========== |
|
|
|
.. [1] https://en.wikipedia.org/wiki/Mathematical_singularity |
|
|
|
Examples |
|
======== |
|
|
|
>>> from sympy import singularities, Symbol, log |
|
>>> x = Symbol('x', real=True) |
|
>>> y = Symbol('y', real=False) |
|
>>> singularities(x**2 + x + 1, x) |
|
EmptySet |
|
>>> singularities(1/(x + 1), x) |
|
{-1} |
|
>>> singularities(1/(y**2 + 1), y) |
|
{-I, I} |
|
>>> singularities(1/(y**3 + 1), y) |
|
{-1, 1/2 - sqrt(3)*I/2, 1/2 + sqrt(3)*I/2} |
|
>>> singularities(log(x), x) |
|
{0} |
|
|
|
""" |
|
from sympy.solvers.solveset import solveset |
|
|
|
if domain is None: |
|
domain = S.Reals if symbol.is_real else S.Complexes |
|
try: |
|
sings = S.EmptySet |
|
e = expression.rewrite([sec, csc, cot, tan], cos) |
|
e = e.rewrite([sech, csch, coth, tanh], cosh) |
|
for i in e.atoms(Pow): |
|
if i.exp.is_infinite: |
|
raise NotImplementedError |
|
if i.exp.is_negative: |
|
|
|
sings += solveset(i.base, symbol, domain) |
|
for i in expression.atoms(log, asech, acsch): |
|
sings += solveset(i.args[0], symbol, domain) |
|
for i in expression.atoms(atanh, acoth): |
|
sings += solveset(i.args[0] - 1, symbol, domain) |
|
sings += solveset(i.args[0] + 1, symbol, domain) |
|
return sings |
|
except NotImplementedError: |
|
raise NotImplementedError(filldedent(''' |
|
Methods for determining the singularities |
|
of this function have not been developed.''')) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def monotonicity_helper(expression, predicate, interval=S.Reals, symbol=None): |
|
""" |
|
Helper function for functions checking function monotonicity. |
|
|
|
Parameters |
|
========== |
|
|
|
expression : Expr |
|
The target function which is being checked |
|
predicate : function |
|
The property being tested for. The function takes in an integer |
|
and returns a boolean. The integer input is the derivative and |
|
the boolean result should be true if the property is being held, |
|
and false otherwise. |
|
interval : Set, optional |
|
The range of values in which we are testing, defaults to all reals. |
|
symbol : Symbol, optional |
|
The symbol present in expression which gets varied over the given range. |
|
|
|
It returns a boolean indicating whether the interval in which |
|
the function's derivative satisfies given predicate is a superset |
|
of the given interval. |
|
|
|
Returns |
|
======= |
|
|
|
Boolean |
|
True if ``predicate`` is true for all the derivatives when ``symbol`` |
|
is varied in ``range``, False otherwise. |
|
|
|
""" |
|
from sympy.solvers.solveset import solveset |
|
|
|
expression = sympify(expression) |
|
free = expression.free_symbols |
|
|
|
if symbol is None: |
|
if len(free) > 1: |
|
raise NotImplementedError( |
|
'The function has not yet been implemented' |
|
' for all multivariate expressions.' |
|
) |
|
|
|
variable = symbol or (free.pop() if free else Symbol('x')) |
|
derivative = expression.diff(variable) |
|
predicate_interval = solveset(predicate(derivative), variable, S.Reals) |
|
return interval.is_subset(predicate_interval) |
|
|
|
|
|
def is_increasing(expression, interval=S.Reals, symbol=None): |
|
""" |
|
Return whether the function is increasing in the given interval. |
|
|
|
Parameters |
|
========== |
|
|
|
expression : Expr |
|
The target function which is being checked. |
|
interval : Set, optional |
|
The range of values in which we are testing (defaults to set of |
|
all real numbers). |
|
symbol : Symbol, optional |
|
The symbol present in expression which gets varied over the given range. |
|
|
|
Returns |
|
======= |
|
|
|
Boolean |
|
True if ``expression`` is increasing (either strictly increasing or |
|
constant) in the given ``interval``, False otherwise. |
|
|
|
Examples |
|
======== |
|
|
|
>>> from sympy import is_increasing |
|
>>> from sympy.abc import x, y |
|
>>> from sympy import S, Interval, oo |
|
>>> is_increasing(x**3 - 3*x**2 + 4*x, S.Reals) |
|
True |
|
>>> is_increasing(-x**2, Interval(-oo, 0)) |
|
True |
|
>>> is_increasing(-x**2, Interval(0, oo)) |
|
False |
|
>>> is_increasing(4*x**3 - 6*x**2 - 72*x + 30, Interval(-2, 3)) |
|
False |
|
>>> is_increasing(x**2 + y, Interval(1, 2), x) |
|
True |
|
|
|
""" |
|
return monotonicity_helper(expression, lambda x: x >= 0, interval, symbol) |
|
|
|
|
|
def is_strictly_increasing(expression, interval=S.Reals, symbol=None): |
|
""" |
|
Return whether the function is strictly increasing in the given interval. |
|
|
|
Parameters |
|
========== |
|
|
|
expression : Expr |
|
The target function which is being checked. |
|
interval : Set, optional |
|
The range of values in which we are testing (defaults to set of |
|
all real numbers). |
|
symbol : Symbol, optional |
|
The symbol present in expression which gets varied over the given range. |
|
|
|
Returns |
|
======= |
|
|
|
Boolean |
|
True if ``expression`` is strictly increasing in the given ``interval``, |
|
False otherwise. |
|
|
|
Examples |
|
======== |
|
|
|
>>> from sympy import is_strictly_increasing |
|
>>> from sympy.abc import x, y |
|
>>> from sympy import Interval, oo |
|
>>> is_strictly_increasing(4*x**3 - 6*x**2 - 72*x + 30, Interval.Ropen(-oo, -2)) |
|
True |
|
>>> is_strictly_increasing(4*x**3 - 6*x**2 - 72*x + 30, Interval.Lopen(3, oo)) |
|
True |
|
>>> is_strictly_increasing(4*x**3 - 6*x**2 - 72*x + 30, Interval.open(-2, 3)) |
|
False |
|
>>> is_strictly_increasing(-x**2, Interval(0, oo)) |
|
False |
|
>>> is_strictly_increasing(-x**2 + y, Interval(-oo, 0), x) |
|
False |
|
|
|
""" |
|
return monotonicity_helper(expression, lambda x: x > 0, interval, symbol) |
|
|
|
|
|
def is_decreasing(expression, interval=S.Reals, symbol=None): |
|
""" |
|
Return whether the function is decreasing in the given interval. |
|
|
|
Parameters |
|
========== |
|
|
|
expression : Expr |
|
The target function which is being checked. |
|
interval : Set, optional |
|
The range of values in which we are testing (defaults to set of |
|
all real numbers). |
|
symbol : Symbol, optional |
|
The symbol present in expression which gets varied over the given range. |
|
|
|
Returns |
|
======= |
|
|
|
Boolean |
|
True if ``expression`` is decreasing (either strictly decreasing or |
|
constant) in the given ``interval``, False otherwise. |
|
|
|
Examples |
|
======== |
|
|
|
>>> from sympy import is_decreasing |
|
>>> from sympy.abc import x, y |
|
>>> from sympy import S, Interval, oo |
|
>>> is_decreasing(1/(x**2 - 3*x), Interval.open(S(3)/2, 3)) |
|
True |
|
>>> is_decreasing(1/(x**2 - 3*x), Interval.open(1.5, 3)) |
|
True |
|
>>> is_decreasing(1/(x**2 - 3*x), Interval.Lopen(3, oo)) |
|
True |
|
>>> is_decreasing(1/(x**2 - 3*x), Interval.Ropen(-oo, S(3)/2)) |
|
False |
|
>>> is_decreasing(1/(x**2 - 3*x), Interval.Ropen(-oo, 1.5)) |
|
False |
|
>>> is_decreasing(-x**2, Interval(-oo, 0)) |
|
False |
|
>>> is_decreasing(-x**2 + y, Interval(-oo, 0), x) |
|
False |
|
|
|
""" |
|
return monotonicity_helper(expression, lambda x: x <= 0, interval, symbol) |
|
|
|
|
|
def is_strictly_decreasing(expression, interval=S.Reals, symbol=None): |
|
""" |
|
Return whether the function is strictly decreasing in the given interval. |
|
|
|
Parameters |
|
========== |
|
|
|
expression : Expr |
|
The target function which is being checked. |
|
interval : Set, optional |
|
The range of values in which we are testing (defaults to set of |
|
all real numbers). |
|
symbol : Symbol, optional |
|
The symbol present in expression which gets varied over the given range. |
|
|
|
Returns |
|
======= |
|
|
|
Boolean |
|
True if ``expression`` is strictly decreasing in the given ``interval``, |
|
False otherwise. |
|
|
|
Examples |
|
======== |
|
|
|
>>> from sympy import is_strictly_decreasing |
|
>>> from sympy.abc import x, y |
|
>>> from sympy import S, Interval, oo |
|
>>> is_strictly_decreasing(1/(x**2 - 3*x), Interval.Lopen(3, oo)) |
|
True |
|
>>> is_strictly_decreasing(1/(x**2 - 3*x), Interval.Ropen(-oo, S(3)/2)) |
|
False |
|
>>> is_strictly_decreasing(1/(x**2 - 3*x), Interval.Ropen(-oo, 1.5)) |
|
False |
|
>>> is_strictly_decreasing(-x**2, Interval(-oo, 0)) |
|
False |
|
>>> is_strictly_decreasing(-x**2 + y, Interval(-oo, 0), x) |
|
False |
|
|
|
""" |
|
return monotonicity_helper(expression, lambda x: x < 0, interval, symbol) |
|
|
|
|
|
def is_monotonic(expression, interval=S.Reals, symbol=None): |
|
""" |
|
Return whether the function is monotonic in the given interval. |
|
|
|
Parameters |
|
========== |
|
|
|
expression : Expr |
|
The target function which is being checked. |
|
interval : Set, optional |
|
The range of values in which we are testing (defaults to set of |
|
all real numbers). |
|
symbol : Symbol, optional |
|
The symbol present in expression which gets varied over the given range. |
|
|
|
Returns |
|
======= |
|
|
|
Boolean |
|
True if ``expression`` is monotonic in the given ``interval``, |
|
False otherwise. |
|
|
|
Raises |
|
====== |
|
|
|
NotImplementedError |
|
Monotonicity check has not been implemented for the queried function. |
|
|
|
Examples |
|
======== |
|
|
|
>>> from sympy import is_monotonic |
|
>>> from sympy.abc import x, y |
|
>>> from sympy import S, Interval, oo |
|
>>> is_monotonic(1/(x**2 - 3*x), Interval.open(S(3)/2, 3)) |
|
True |
|
>>> is_monotonic(1/(x**2 - 3*x), Interval.open(1.5, 3)) |
|
True |
|
>>> is_monotonic(1/(x**2 - 3*x), Interval.Lopen(3, oo)) |
|
True |
|
>>> is_monotonic(x**3 - 3*x**2 + 4*x, S.Reals) |
|
True |
|
>>> is_monotonic(-x**2, S.Reals) |
|
False |
|
>>> is_monotonic(x**2 + y + 1, Interval(1, 2), x) |
|
True |
|
|
|
""" |
|
from sympy.solvers.solveset import solveset |
|
|
|
expression = sympify(expression) |
|
|
|
free = expression.free_symbols |
|
if symbol is None and len(free) > 1: |
|
raise NotImplementedError( |
|
'is_monotonic has not yet been implemented' |
|
' for all multivariate expressions.' |
|
) |
|
|
|
variable = symbol or (free.pop() if free else Symbol('x')) |
|
turning_points = solveset(expression.diff(variable), variable, interval) |
|
return interval.intersection(turning_points) is S.EmptySet |
|
|