Spaces:
Sleeping
Sleeping
""" | |
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: | |
# XXX: exponent of varying sign not handled | |
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.''')) | |
########################################################################### | |
# DIFFERENTIAL CALCULUS METHODS # | |
########################################################################### | |
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 | |