I am done

This commit is contained in:
2024-10-30 22:14:35 +01:00
parent 720dc28c09
commit 40e2a747cf
36901 changed files with 5011519 additions and 0 deletions

View File

@ -0,0 +1,25 @@
"""Calculus-related methods."""
from .euler import euler_equations
from .singularities import (singularities, is_increasing,
is_strictly_increasing, is_decreasing,
is_strictly_decreasing, is_monotonic)
from .finite_diff import finite_diff_weights, apply_finite_diff, differentiate_finite
from .util import (periodicity, not_empty_in, is_convex,
stationary_points, minimum, maximum)
from .accumulationbounds import AccumBounds
__all__ = [
'euler_equations',
'singularities', 'is_increasing',
'is_strictly_increasing', 'is_decreasing',
'is_strictly_decreasing', 'is_monotonic',
'finite_diff_weights', 'apply_finite_diff', 'differentiate_finite',
'periodicity', 'not_empty_in', 'is_convex', 'stationary_points',
'minimum', 'maximum',
'AccumBounds'
]

View File

@ -0,0 +1,804 @@
from sympy.core import Add, Mul, Pow, S
from sympy.core.basic import Basic
from sympy.core.expr import Expr
from sympy.core.numbers import _sympifyit, oo, zoo
from sympy.core.relational import is_le, is_lt, is_ge, is_gt
from sympy.core.sympify import _sympify
from sympy.functions.elementary.miscellaneous import Min, Max
from sympy.logic.boolalg import And
from sympy.multipledispatch import dispatch
from sympy.series.order import Order
from sympy.sets.sets import FiniteSet
class AccumulationBounds(Expr):
r"""An accumulation bounds.
# Note AccumulationBounds has an alias: AccumBounds
AccumulationBounds represent an interval `[a, b]`, which is always closed
at the ends. Here `a` and `b` can be any value from extended real numbers.
The intended meaning of AccummulationBounds is to give an approximate
location of the accumulation points of a real function at a limit point.
Let `a` and `b` be reals such that `a \le b`.
`\left\langle a, b\right\rangle = \{x \in \mathbb{R} \mid a \le x \le b\}`
`\left\langle -\infty, b\right\rangle = \{x \in \mathbb{R} \mid x \le b\} \cup \{-\infty, \infty\}`
`\left\langle a, \infty \right\rangle = \{x \in \mathbb{R} \mid a \le x\} \cup \{-\infty, \infty\}`
`\left\langle -\infty, \infty \right\rangle = \mathbb{R} \cup \{-\infty, \infty\}`
``oo`` and ``-oo`` are added to the second and third definition respectively,
since if either ``-oo`` or ``oo`` is an argument, then the other one should
be included (though not as an end point). This is forced, since we have,
for example, ``1/AccumBounds(0, 1) = AccumBounds(1, oo)``, and the limit at
`0` is not one-sided. As `x` tends to `0-`, then `1/x \rightarrow -\infty`, so `-\infty`
should be interpreted as belonging to ``AccumBounds(1, oo)`` though it need
not appear explicitly.
In many cases it suffices to know that the limit set is bounded.
However, in some other cases more exact information could be useful.
For example, all accumulation values of `\cos(x) + 1` are non-negative.
(``AccumBounds(-1, 1) + 1 = AccumBounds(0, 2)``)
A AccumulationBounds object is defined to be real AccumulationBounds,
if its end points are finite reals.
Let `X`, `Y` be real AccumulationBounds, then their sum, difference,
product are defined to be the following sets:
`X + Y = \{ x+y \mid x \in X \cap y \in Y\}`
`X - Y = \{ x-y \mid x \in X \cap y \in Y\}`
`X \times Y = \{ x \times y \mid x \in X \cap y \in Y\}`
When an AccumBounds is raised to a negative power, if 0 is contained
between the bounds then an infinite range is returned, otherwise if an
endpoint is 0 then a semi-infinite range with consistent sign will be returned.
AccumBounds in expressions behave a lot like Intervals but the
semantics are not necessarily the same. Division (or exponentiation
to a negative integer power) could be handled with *intervals* by
returning a union of the results obtained after splitting the
bounds between negatives and positives, but that is not done with
AccumBounds. In addition, bounds are assumed to be independent of
each other; if the same bound is used in more than one place in an
expression, the result may not be the supremum or infimum of the
expression (see below). Finally, when a boundary is ``1``,
exponentiation to the power of ``oo`` yields ``oo``, neither
``1`` nor ``nan``.
Examples
========
>>> from sympy import AccumBounds, sin, exp, log, pi, E, S, oo
>>> from sympy.abc import x
>>> AccumBounds(0, 1) + AccumBounds(1, 2)
AccumBounds(1, 3)
>>> AccumBounds(0, 1) - AccumBounds(0, 2)
AccumBounds(-2, 1)
>>> AccumBounds(-2, 3)*AccumBounds(-1, 1)
AccumBounds(-3, 3)
>>> AccumBounds(1, 2)*AccumBounds(3, 5)
AccumBounds(3, 10)
The exponentiation of AccumulationBounds is defined
as follows:
If 0 does not belong to `X` or `n > 0` then
`X^n = \{ x^n \mid x \in X\}`
>>> AccumBounds(1, 4)**(S(1)/2)
AccumBounds(1, 2)
otherwise, an infinite or semi-infinite result is obtained:
>>> 1/AccumBounds(-1, 1)
AccumBounds(-oo, oo)
>>> 1/AccumBounds(0, 2)
AccumBounds(1/2, oo)
>>> 1/AccumBounds(-oo, 0)
AccumBounds(-oo, 0)
A boundary of 1 will always generate all nonnegatives:
>>> AccumBounds(1, 2)**oo
AccumBounds(0, oo)
>>> AccumBounds(0, 1)**oo
AccumBounds(0, oo)
If the exponent is itself an AccumulationBounds or is not an
integer then unevaluated results will be returned unless the base
values are positive:
>>> AccumBounds(2, 3)**AccumBounds(-1, 2)
AccumBounds(1/3, 9)
>>> AccumBounds(-2, 3)**AccumBounds(-1, 2)
AccumBounds(-2, 3)**AccumBounds(-1, 2)
>>> AccumBounds(-2, -1)**(S(1)/2)
sqrt(AccumBounds(-2, -1))
Note: `\left\langle a, b\right\rangle^2` is not same as `\left\langle a, b\right\rangle \times \left\langle a, b\right\rangle`
>>> AccumBounds(-1, 1)**2
AccumBounds(0, 1)
>>> AccumBounds(1, 3) < 4
True
>>> AccumBounds(1, 3) < -1
False
Some elementary functions can also take AccumulationBounds as input.
A function `f` evaluated for some real AccumulationBounds `\left\langle a, b \right\rangle`
is defined as `f(\left\langle a, b\right\rangle) = \{ f(x) \mid a \le x \le b \}`
>>> sin(AccumBounds(pi/6, pi/3))
AccumBounds(1/2, sqrt(3)/2)
>>> exp(AccumBounds(0, 1))
AccumBounds(1, E)
>>> log(AccumBounds(1, E))
AccumBounds(0, 1)
Some symbol in an expression can be substituted for a AccumulationBounds
object. But it does not necessarily evaluate the AccumulationBounds for
that expression.
The same expression can be evaluated to different values depending upon
the form it is used for substitution since each instance of an
AccumulationBounds is considered independent. For example:
>>> (x**2 + 2*x + 1).subs(x, AccumBounds(-1, 1))
AccumBounds(-1, 4)
>>> ((x + 1)**2).subs(x, AccumBounds(-1, 1))
AccumBounds(0, 4)
References
==========
.. [1] https://en.wikipedia.org/wiki/Interval_arithmetic
.. [2] https://fab.cba.mit.edu/classes/S62.12/docs/Hickey_interval.pdf
Notes
=====
Do not use ``AccumulationBounds`` for floating point interval arithmetic
calculations, use ``mpmath.iv`` instead.
"""
is_extended_real = True
is_number = False
def __new__(cls, min, max):
min = _sympify(min)
max = _sympify(max)
# Only allow real intervals (use symbols with 'is_extended_real=True').
if not min.is_extended_real or not max.is_extended_real:
raise ValueError("Only real AccumulationBounds are supported")
if max == min:
return max
# Make sure that the created AccumBounds object will be valid.
if max.is_number and min.is_number:
bad = max.is_comparable and min.is_comparable and max < min
else:
bad = (max - min).is_extended_negative
if bad:
raise ValueError(
"Lower limit should be smaller than upper limit")
return Basic.__new__(cls, min, max)
# setting the operation priority
_op_priority = 11.0
def _eval_is_real(self):
if self.min.is_real and self.max.is_real:
return True
@property
def min(self):
"""
Returns the minimum possible value attained by AccumulationBounds
object.
Examples
========
>>> from sympy import AccumBounds
>>> AccumBounds(1, 3).min
1
"""
return self.args[0]
@property
def max(self):
"""
Returns the maximum possible value attained by AccumulationBounds
object.
Examples
========
>>> from sympy import AccumBounds
>>> AccumBounds(1, 3).max
3
"""
return self.args[1]
@property
def delta(self):
"""
Returns the difference of maximum possible value attained by
AccumulationBounds object and minimum possible value attained
by AccumulationBounds object.
Examples
========
>>> from sympy import AccumBounds
>>> AccumBounds(1, 3).delta
2
"""
return self.max - self.min
@property
def mid(self):
"""
Returns the mean of maximum possible value attained by
AccumulationBounds object and minimum possible value
attained by AccumulationBounds object.
Examples
========
>>> from sympy import AccumBounds
>>> AccumBounds(1, 3).mid
2
"""
return (self.min + self.max) / 2
@_sympifyit('other', NotImplemented)
def _eval_power(self, other):
return self.__pow__(other)
@_sympifyit('other', NotImplemented)
def __add__(self, other):
if isinstance(other, Expr):
if isinstance(other, AccumBounds):
return AccumBounds(
Add(self.min, other.min),
Add(self.max, other.max))
if other is S.Infinity and self.min is S.NegativeInfinity or \
other is S.NegativeInfinity and self.max is S.Infinity:
return AccumBounds(-oo, oo)
elif other.is_extended_real:
if self.min is S.NegativeInfinity and self.max is S.Infinity:
return AccumBounds(-oo, oo)
elif self.min is S.NegativeInfinity:
return AccumBounds(-oo, self.max + other)
elif self.max is S.Infinity:
return AccumBounds(self.min + other, oo)
else:
return AccumBounds(Add(self.min, other), Add(self.max, other))
return Add(self, other, evaluate=False)
return NotImplemented
__radd__ = __add__
def __neg__(self):
return AccumBounds(-self.max, -self.min)
@_sympifyit('other', NotImplemented)
def __sub__(self, other):
if isinstance(other, Expr):
if isinstance(other, AccumBounds):
return AccumBounds(
Add(self.min, -other.max),
Add(self.max, -other.min))
if other is S.NegativeInfinity and self.min is S.NegativeInfinity or \
other is S.Infinity and self.max is S.Infinity:
return AccumBounds(-oo, oo)
elif other.is_extended_real:
if self.min is S.NegativeInfinity and self.max is S.Infinity:
return AccumBounds(-oo, oo)
elif self.min is S.NegativeInfinity:
return AccumBounds(-oo, self.max - other)
elif self.max is S.Infinity:
return AccumBounds(self.min - other, oo)
else:
return AccumBounds(
Add(self.min, -other),
Add(self.max, -other))
return Add(self, -other, evaluate=False)
return NotImplemented
@_sympifyit('other', NotImplemented)
def __rsub__(self, other):
return self.__neg__() + other
@_sympifyit('other', NotImplemented)
def __mul__(self, other):
if self.args == (-oo, oo):
return self
if isinstance(other, Expr):
if isinstance(other, AccumBounds):
if other.args == (-oo, oo):
return other
v = set()
for a in self.args:
vi = other*a
v.update(vi.args or (vi,))
return AccumBounds(Min(*v), Max(*v))
if other is S.Infinity:
if self.min.is_zero:
return AccumBounds(0, oo)
if self.max.is_zero:
return AccumBounds(-oo, 0)
if other is S.NegativeInfinity:
if self.min.is_zero:
return AccumBounds(-oo, 0)
if self.max.is_zero:
return AccumBounds(0, oo)
if other.is_extended_real:
if other.is_zero:
if self.max is S.Infinity:
return AccumBounds(0, oo)
if self.min is S.NegativeInfinity:
return AccumBounds(-oo, 0)
return S.Zero
if other.is_extended_positive:
return AccumBounds(
Mul(self.min, other),
Mul(self.max, other))
elif other.is_extended_negative:
return AccumBounds(
Mul(self.max, other),
Mul(self.min, other))
if isinstance(other, Order):
return other
return Mul(self, other, evaluate=False)
return NotImplemented
__rmul__ = __mul__
@_sympifyit('other', NotImplemented)
def __truediv__(self, other):
if isinstance(other, Expr):
if isinstance(other, AccumBounds):
if other.min.is_positive or other.max.is_negative:
return self * AccumBounds(1/other.max, 1/other.min)
if (self.min.is_extended_nonpositive and self.max.is_extended_nonnegative and
other.min.is_extended_nonpositive and other.max.is_extended_nonnegative):
if self.min.is_zero and other.min.is_zero:
return AccumBounds(0, oo)
if self.max.is_zero and other.min.is_zero:
return AccumBounds(-oo, 0)
return AccumBounds(-oo, oo)
if self.max.is_extended_negative:
if other.min.is_extended_negative:
if other.max.is_zero:
return AccumBounds(self.max / other.min, oo)
if other.max.is_extended_positive:
# if we were dealing with intervals we would return
# Union(Interval(-oo, self.max/other.max),
# Interval(self.max/other.min, oo))
return AccumBounds(-oo, oo)
if other.min.is_zero and other.max.is_extended_positive:
return AccumBounds(-oo, self.max / other.max)
if self.min.is_extended_positive:
if other.min.is_extended_negative:
if other.max.is_zero:
return AccumBounds(-oo, self.min / other.min)
if other.max.is_extended_positive:
# if we were dealing with intervals we would return
# Union(Interval(-oo, self.min/other.min),
# Interval(self.min/other.max, oo))
return AccumBounds(-oo, oo)
if other.min.is_zero and other.max.is_extended_positive:
return AccumBounds(self.min / other.max, oo)
elif other.is_extended_real:
if other in (S.Infinity, S.NegativeInfinity):
if self == AccumBounds(-oo, oo):
return AccumBounds(-oo, oo)
if self.max is S.Infinity:
return AccumBounds(Min(0, other), Max(0, other))
if self.min is S.NegativeInfinity:
return AccumBounds(Min(0, -other), Max(0, -other))
if other.is_extended_positive:
return AccumBounds(self.min / other, self.max / other)
elif other.is_extended_negative:
return AccumBounds(self.max / other, self.min / other)
if (1 / other) is S.ComplexInfinity:
return Mul(self, 1 / other, evaluate=False)
else:
return Mul(self, 1 / other)
return NotImplemented
@_sympifyit('other', NotImplemented)
def __rtruediv__(self, other):
if isinstance(other, Expr):
if other.is_extended_real:
if other.is_zero:
return S.Zero
if (self.min.is_extended_nonpositive and self.max.is_extended_nonnegative):
if self.min.is_zero:
if other.is_extended_positive:
return AccumBounds(Mul(other, 1 / self.max), oo)
if other.is_extended_negative:
return AccumBounds(-oo, Mul(other, 1 / self.max))
if self.max.is_zero:
if other.is_extended_positive:
return AccumBounds(-oo, Mul(other, 1 / self.min))
if other.is_extended_negative:
return AccumBounds(Mul(other, 1 / self.min), oo)
return AccumBounds(-oo, oo)
else:
return AccumBounds(Min(other / self.min, other / self.max),
Max(other / self.min, other / self.max))
return Mul(other, 1 / self, evaluate=False)
else:
return NotImplemented
@_sympifyit('other', NotImplemented)
def __pow__(self, other):
if isinstance(other, Expr):
if other is S.Infinity:
if self.min.is_extended_nonnegative:
if self.max < 1:
return S.Zero
if self.min > 1:
return S.Infinity
return AccumBounds(0, oo)
elif self.max.is_extended_negative:
if self.min > -1:
return S.Zero
if self.max < -1:
return zoo
return S.NaN
else:
if self.min > -1:
if self.max < 1:
return S.Zero
return AccumBounds(0, oo)
return AccumBounds(-oo, oo)
if other is S.NegativeInfinity:
return (1/self)**oo
# generically true
if (self.max - self.min).is_nonnegative:
# well defined
if self.min.is_nonnegative:
# no 0 to worry about
if other.is_nonnegative:
# no infinity to worry about
return self.func(self.min**other, self.max**other)
if other.is_zero:
return S.One # x**0 = 1
if other.is_Integer or other.is_integer:
if self.min.is_extended_positive:
return AccumBounds(
Min(self.min**other, self.max**other),
Max(self.min**other, self.max**other))
elif self.max.is_extended_negative:
return AccumBounds(
Min(self.max**other, self.min**other),
Max(self.max**other, self.min**other))
if other % 2 == 0:
if other.is_extended_negative:
if self.min.is_zero:
return AccumBounds(self.max**other, oo)
if self.max.is_zero:
return AccumBounds(self.min**other, oo)
return (1/self)**(-other)
return AccumBounds(
S.Zero, Max(self.min**other, self.max**other))
elif other % 2 == 1:
if other.is_extended_negative:
if self.min.is_zero:
return AccumBounds(self.max**other, oo)
if self.max.is_zero:
return AccumBounds(-oo, self.min**other)
return (1/self)**(-other)
return AccumBounds(self.min**other, self.max**other)
# non-integer exponent
# 0**neg or neg**frac yields complex
if (other.is_number or other.is_rational) and (
self.min.is_extended_nonnegative or (
other.is_extended_nonnegative and
self.min.is_extended_nonnegative)):
num, den = other.as_numer_denom()
if num is S.One:
return AccumBounds(*[i**(1/den) for i in self.args])
elif den is not S.One: # e.g. if other is not Float
return (self**num)**(1/den) # ok for non-negative base
if isinstance(other, AccumBounds):
if (self.min.is_extended_positive or
self.min.is_extended_nonnegative and
other.min.is_extended_nonnegative):
p = [self**i for i in other.args]
if not any(i.is_Pow for i in p):
a = [j for i in p for j in i.args or (i,)]
try:
return self.func(min(a), max(a))
except TypeError: # can't sort
pass
return Pow(self, other, evaluate=False)
return NotImplemented
@_sympifyit('other', NotImplemented)
def __rpow__(self, other):
if other.is_real and other.is_extended_nonnegative and (
self.max - self.min).is_extended_positive:
if other is S.One:
return S.One
if other.is_extended_positive:
a, b = [other**i for i in self.args]
if min(a, b) != a:
a, b = b, a
return self.func(a, b)
if other.is_zero:
if self.min.is_zero:
return self.func(0, 1)
if self.min.is_extended_positive:
return S.Zero
return Pow(other, self, evaluate=False)
def __abs__(self):
if self.max.is_extended_negative:
return self.__neg__()
elif self.min.is_extended_negative:
return AccumBounds(S.Zero, Max(abs(self.min), self.max))
else:
return self
def __contains__(self, other):
"""
Returns ``True`` if other is contained in self, where other
belongs to extended real numbers, ``False`` if not contained,
otherwise TypeError is raised.
Examples
========
>>> from sympy import AccumBounds, oo
>>> 1 in AccumBounds(-1, 3)
True
-oo and oo go together as limits (in AccumulationBounds).
>>> -oo in AccumBounds(1, oo)
True
>>> oo in AccumBounds(-oo, 0)
True
"""
other = _sympify(other)
if other in (S.Infinity, S.NegativeInfinity):
if self.min is S.NegativeInfinity or self.max is S.Infinity:
return True
return False
rv = And(self.min <= other, self.max >= other)
if rv not in (True, False):
raise TypeError("input failed to evaluate")
return rv
def intersection(self, other):
"""
Returns the intersection of 'self' and 'other'.
Here other can be an instance of :py:class:`~.FiniteSet` or AccumulationBounds.
Parameters
==========
other : AccumulationBounds
Another AccumulationBounds object with which the intersection
has to be computed.
Returns
=======
AccumulationBounds
Intersection of ``self`` and ``other``.
Examples
========
>>> from sympy import AccumBounds, FiniteSet
>>> AccumBounds(1, 3).intersection(AccumBounds(2, 4))
AccumBounds(2, 3)
>>> AccumBounds(1, 3).intersection(AccumBounds(4, 6))
EmptySet
>>> AccumBounds(1, 4).intersection(FiniteSet(1, 2, 5))
{1, 2}
"""
if not isinstance(other, (AccumBounds, FiniteSet)):
raise TypeError(
"Input must be AccumulationBounds or FiniteSet object")
if isinstance(other, FiniteSet):
fin_set = S.EmptySet
for i in other:
if i in self:
fin_set = fin_set + FiniteSet(i)
return fin_set
if self.max < other.min or self.min > other.max:
return S.EmptySet
if self.min <= other.min:
if self.max <= other.max:
return AccumBounds(other.min, self.max)
if self.max > other.max:
return other
if other.min <= self.min:
if other.max < self.max:
return AccumBounds(self.min, other.max)
if other.max > self.max:
return self
def union(self, other):
# TODO : Devise a better method for Union of AccumBounds
# this method is not actually correct and
# can be made better
if not isinstance(other, AccumBounds):
raise TypeError(
"Input must be AccumulationBounds or FiniteSet object")
if self.min <= other.min and self.max >= other.min:
return AccumBounds(self.min, Max(self.max, other.max))
if other.min <= self.min and other.max >= self.min:
return AccumBounds(other.min, Max(self.max, other.max))
@dispatch(AccumulationBounds, AccumulationBounds) # type: ignore # noqa:F811
def _eval_is_le(lhs, rhs): # noqa:F811
if is_le(lhs.max, rhs.min):
return True
if is_gt(lhs.min, rhs.max):
return False
@dispatch(AccumulationBounds, Basic) # type: ignore # noqa:F811
def _eval_is_le(lhs, rhs): # noqa: F811
"""
Returns ``True `` if range of values attained by ``lhs`` AccumulationBounds
object is greater than the range of values attained by ``rhs``,
where ``rhs`` may be any value of type AccumulationBounds object or
extended real number value, ``False`` if ``rhs`` satisfies
the same property, else an unevaluated :py:class:`~.Relational`.
Examples
========
>>> from sympy import AccumBounds, oo
>>> AccumBounds(1, 3) > AccumBounds(4, oo)
False
>>> AccumBounds(1, 4) > AccumBounds(3, 4)
AccumBounds(1, 4) > AccumBounds(3, 4)
>>> AccumBounds(1, oo) > -1
True
"""
if not rhs.is_extended_real:
raise TypeError(
"Invalid comparison of %s %s" %
(type(rhs), rhs))
elif rhs.is_comparable:
if is_le(lhs.max, rhs):
return True
if is_gt(lhs.min, rhs):
return False
@dispatch(AccumulationBounds, AccumulationBounds)
def _eval_is_ge(lhs, rhs): # noqa:F811
if is_ge(lhs.min, rhs.max):
return True
if is_lt(lhs.max, rhs.min):
return False
@dispatch(AccumulationBounds, Expr) # type:ignore
def _eval_is_ge(lhs, rhs): # noqa: F811
"""
Returns ``True`` if range of values attained by ``lhs`` AccumulationBounds
object is less that the range of values attained by ``rhs``, where
other may be any value of type AccumulationBounds object or extended
real number value, ``False`` if ``rhs`` satisfies the same
property, else an unevaluated :py:class:`~.Relational`.
Examples
========
>>> from sympy import AccumBounds, oo
>>> AccumBounds(1, 3) >= AccumBounds(4, oo)
False
>>> AccumBounds(1, 4) >= AccumBounds(3, 4)
AccumBounds(1, 4) >= AccumBounds(3, 4)
>>> AccumBounds(1, oo) >= 1
True
"""
if not rhs.is_extended_real:
raise TypeError(
"Invalid comparison of %s %s" %
(type(rhs), rhs))
elif rhs.is_comparable:
if is_ge(lhs.min, rhs):
return True
if is_lt(lhs.max, rhs):
return False
@dispatch(Expr, AccumulationBounds) # type:ignore
def _eval_is_ge(lhs, rhs): # noqa:F811
if not lhs.is_extended_real:
raise TypeError(
"Invalid comparison of %s %s" %
(type(lhs), lhs))
elif lhs.is_comparable:
if is_le(rhs.max, lhs):
return True
if is_gt(rhs.min, lhs):
return False
@dispatch(AccumulationBounds, AccumulationBounds) # type:ignore
def _eval_is_ge(lhs, rhs): # noqa:F811
if is_ge(lhs.min, rhs.max):
return True
if is_lt(lhs.max, rhs.min):
return False
# setting an alias for AccumulationBounds
AccumBounds = AccumulationBounds

View File

@ -0,0 +1,108 @@
"""
This module implements a method to find
Euler-Lagrange Equations for given Lagrangian.
"""
from itertools import combinations_with_replacement
from sympy.core.function import (Derivative, Function, diff)
from sympy.core.relational import Eq
from sympy.core.singleton import S
from sympy.core.symbol import Symbol
from sympy.core.sympify import sympify
from sympy.utilities.iterables import iterable
def euler_equations(L, funcs=(), vars=()):
r"""
Find the Euler-Lagrange equations [1]_ for a given Lagrangian.
Parameters
==========
L : Expr
The Lagrangian that should be a function of the functions listed
in the second argument and their derivatives.
For example, in the case of two functions $f(x,y)$, $g(x,y)$ and
two independent variables $x$, $y$ the Lagrangian has the form:
.. math:: L\left(f(x,y),g(x,y),\frac{\partial f(x,y)}{\partial x},
\frac{\partial f(x,y)}{\partial y},
\frac{\partial g(x,y)}{\partial x},
\frac{\partial g(x,y)}{\partial y},x,y\right)
In many cases it is not necessary to provide anything, except the
Lagrangian, it will be auto-detected (and an error raised if this
cannot be done).
funcs : Function or an iterable of Functions
The functions that the Lagrangian depends on. The Euler equations
are differential equations for each of these functions.
vars : Symbol or an iterable of Symbols
The Symbols that are the independent variables of the functions.
Returns
=======
eqns : list of Eq
The list of differential equations, one for each function.
Examples
========
>>> from sympy import euler_equations, Symbol, Function
>>> x = Function('x')
>>> t = Symbol('t')
>>> L = (x(t).diff(t))**2/2 - x(t)**2/2
>>> euler_equations(L, x(t), t)
[Eq(-x(t) - Derivative(x(t), (t, 2)), 0)]
>>> u = Function('u')
>>> x = Symbol('x')
>>> L = (u(t, x).diff(t))**2/2 - (u(t, x).diff(x))**2/2
>>> euler_equations(L, u(t, x), [t, x])
[Eq(-Derivative(u(t, x), (t, 2)) + Derivative(u(t, x), (x, 2)), 0)]
References
==========
.. [1] https://en.wikipedia.org/wiki/Euler%E2%80%93Lagrange_equation
"""
funcs = tuple(funcs) if iterable(funcs) else (funcs,)
if not funcs:
funcs = tuple(L.atoms(Function))
else:
for f in funcs:
if not isinstance(f, Function):
raise TypeError('Function expected, got: %s' % f)
vars = tuple(vars) if iterable(vars) else (vars,)
if not vars:
vars = funcs[0].args
else:
vars = tuple(sympify(var) for var in vars)
if not all(isinstance(v, Symbol) for v in vars):
raise TypeError('Variables are not symbols, got %s' % vars)
for f in funcs:
if not vars == f.args:
raise ValueError("Variables %s do not match args: %s" % (vars, f))
order = max([len(d.variables) for d in L.atoms(Derivative)
if d.expr in funcs] + [0])
eqns = []
for f in funcs:
eq = diff(L, f)
for i in range(1, order + 1):
for p in combinations_with_replacement(vars, i):
eq = eq + S.NegativeOne**i*diff(L, diff(f, *p), *p)
new_eq = Eq(eq, 0)
if isinstance(new_eq, Eq):
eqns.append(new_eq)
return eqns

View File

@ -0,0 +1,476 @@
"""
Finite difference weights
=========================
This module implements an algorithm for efficient generation of finite
difference weights for ordinary differentials of functions for
derivatives from 0 (interpolation) up to arbitrary order.
The core algorithm is provided in the finite difference weight generating
function (``finite_diff_weights``), and two convenience functions are provided
for:
- estimating a derivative (or interpolate) directly from a series of points
is also provided (``apply_finite_diff``).
- differentiating by using finite difference approximations
(``differentiate_finite``).
"""
from sympy.core.function import Derivative
from sympy.core.singleton import S
from sympy.core.function import Subs
from sympy.core.traversal import preorder_traversal
from sympy.utilities.exceptions import sympy_deprecation_warning
from sympy.utilities.iterables import iterable
def finite_diff_weights(order, x_list, x0=S.One):
"""
Calculates the finite difference weights for an arbitrarily spaced
one-dimensional grid (``x_list``) for derivatives at ``x0`` of order
0, 1, ..., up to ``order`` using a recursive formula. Order of accuracy
is at least ``len(x_list) - order``, if ``x_list`` is defined correctly.
Parameters
==========
order: int
Up to what derivative order weights should be calculated.
0 corresponds to interpolation.
x_list: sequence
Sequence of (unique) values for the independent variable.
It is useful (but not necessary) to order ``x_list`` from
nearest to furthest from ``x0``; see examples below.
x0: Number or Symbol
Root or value of the independent variable for which the finite
difference weights should be generated. Default is ``S.One``.
Returns
=======
list
A list of sublists, each corresponding to coefficients for
increasing derivative order, and each containing lists of
coefficients for increasing subsets of x_list.
Examples
========
>>> from sympy import finite_diff_weights, S
>>> res = finite_diff_weights(1, [-S(1)/2, S(1)/2, S(3)/2, S(5)/2], 0)
>>> res
[[[1, 0, 0, 0],
[1/2, 1/2, 0, 0],
[3/8, 3/4, -1/8, 0],
[5/16, 15/16, -5/16, 1/16]],
[[0, 0, 0, 0],
[-1, 1, 0, 0],
[-1, 1, 0, 0],
[-23/24, 7/8, 1/8, -1/24]]]
>>> res[0][-1] # FD weights for 0th derivative, using full x_list
[5/16, 15/16, -5/16, 1/16]
>>> res[1][-1] # FD weights for 1st derivative
[-23/24, 7/8, 1/8, -1/24]
>>> res[1][-2] # FD weights for 1st derivative, using x_list[:-1]
[-1, 1, 0, 0]
>>> res[1][-1][0] # FD weight for 1st deriv. for x_list[0]
-23/24
>>> res[1][-1][1] # FD weight for 1st deriv. for x_list[1], etc.
7/8
Each sublist contains the most accurate formula at the end.
Note, that in the above example ``res[1][1]`` is the same as ``res[1][2]``.
Since res[1][2] has an order of accuracy of
``len(x_list[:3]) - order = 3 - 1 = 2``, the same is true for ``res[1][1]``!
>>> res = finite_diff_weights(1, [S(0), S(1), -S(1), S(2), -S(2)], 0)[1]
>>> res
[[0, 0, 0, 0, 0],
[-1, 1, 0, 0, 0],
[0, 1/2, -1/2, 0, 0],
[-1/2, 1, -1/3, -1/6, 0],
[0, 2/3, -2/3, -1/12, 1/12]]
>>> res[0] # no approximation possible, using x_list[0] only
[0, 0, 0, 0, 0]
>>> res[1] # classic forward step approximation
[-1, 1, 0, 0, 0]
>>> res[2] # classic centered approximation
[0, 1/2, -1/2, 0, 0]
>>> res[3:] # higher order approximations
[[-1/2, 1, -1/3, -1/6, 0], [0, 2/3, -2/3, -1/12, 1/12]]
Let us compare this to a differently defined ``x_list``. Pay attention to
``foo[i][k]`` corresponding to the gridpoint defined by ``x_list[k]``.
>>> foo = finite_diff_weights(1, [-S(2), -S(1), S(0), S(1), S(2)], 0)[1]
>>> foo
[[0, 0, 0, 0, 0],
[-1, 1, 0, 0, 0],
[1/2, -2, 3/2, 0, 0],
[1/6, -1, 1/2, 1/3, 0],
[1/12, -2/3, 0, 2/3, -1/12]]
>>> foo[1] # not the same and of lower accuracy as res[1]!
[-1, 1, 0, 0, 0]
>>> foo[2] # classic double backward step approximation
[1/2, -2, 3/2, 0, 0]
>>> foo[4] # the same as res[4]
[1/12, -2/3, 0, 2/3, -1/12]
Note that, unless you plan on using approximations based on subsets of
``x_list``, the order of gridpoints does not matter.
The capability to generate weights at arbitrary points can be
used e.g. to minimize Runge's phenomenon by using Chebyshev nodes:
>>> from sympy import cos, symbols, pi, simplify
>>> N, (h, x) = 4, symbols('h x')
>>> x_list = [x+h*cos(i*pi/(N)) for i in range(N,-1,-1)] # chebyshev nodes
>>> print(x_list)
[-h + x, -sqrt(2)*h/2 + x, x, sqrt(2)*h/2 + x, h + x]
>>> mycoeffs = finite_diff_weights(1, x_list, 0)[1][4]
>>> [simplify(c) for c in mycoeffs] #doctest: +NORMALIZE_WHITESPACE
[(h**3/2 + h**2*x - 3*h*x**2 - 4*x**3)/h**4,
(-sqrt(2)*h**3 - 4*h**2*x + 3*sqrt(2)*h*x**2 + 8*x**3)/h**4,
(6*h**2*x - 8*x**3)/h**4,
(sqrt(2)*h**3 - 4*h**2*x - 3*sqrt(2)*h*x**2 + 8*x**3)/h**4,
(-h**3/2 + h**2*x + 3*h*x**2 - 4*x**3)/h**4]
Notes
=====
If weights for a finite difference approximation of 3rd order
derivative is wanted, weights for 0th, 1st and 2nd order are
calculated "for free", so are formulae using subsets of ``x_list``.
This is something one can take advantage of to save computational cost.
Be aware that one should define ``x_list`` from nearest to furthest from
``x0``. If not, subsets of ``x_list`` will yield poorer approximations,
which might not grand an order of accuracy of ``len(x_list) - order``.
See also
========
sympy.calculus.finite_diff.apply_finite_diff
References
==========
.. [1] Generation of Finite Difference Formulas on Arbitrarily Spaced
Grids, Bengt Fornberg; Mathematics of computation; 51; 184;
(1988); 699-706; doi:10.1090/S0025-5718-1988-0935077-0
"""
# The notation below closely corresponds to the one used in the paper.
order = S(order)
if not order.is_number:
raise ValueError("Cannot handle symbolic order.")
if order < 0:
raise ValueError("Negative derivative order illegal.")
if int(order) != order:
raise ValueError("Non-integer order illegal")
M = order
N = len(x_list) - 1
delta = [[[0 for nu in range(N+1)] for n in range(N+1)] for
m in range(M+1)]
delta[0][0][0] = S.One
c1 = S.One
for n in range(1, N+1):
c2 = S.One
for nu in range(n):
c3 = x_list[n] - x_list[nu]
c2 = c2 * c3
if n <= M:
delta[n][n-1][nu] = 0
for m in range(min(n, M)+1):
delta[m][n][nu] = (x_list[n]-x0)*delta[m][n-1][nu] -\
m*delta[m-1][n-1][nu]
delta[m][n][nu] /= c3
for m in range(min(n, M)+1):
delta[m][n][n] = c1/c2*(m*delta[m-1][n-1][n-1] -
(x_list[n-1]-x0)*delta[m][n-1][n-1])
c1 = c2
return delta
def apply_finite_diff(order, x_list, y_list, x0=S.Zero):
"""
Calculates the finite difference approximation of
the derivative of requested order at ``x0`` from points
provided in ``x_list`` and ``y_list``.
Parameters
==========
order: int
order of derivative to approximate. 0 corresponds to interpolation.
x_list: sequence
Sequence of (unique) values for the independent variable.
y_list: sequence
The function value at corresponding values for the independent
variable in x_list.
x0: Number or Symbol
At what value of the independent variable the derivative should be
evaluated. Defaults to 0.
Returns
=======
sympy.core.add.Add or sympy.core.numbers.Number
The finite difference expression approximating the requested
derivative order at ``x0``.
Examples
========
>>> from sympy import apply_finite_diff
>>> cube = lambda arg: (1.0*arg)**3
>>> xlist = range(-3,3+1)
>>> apply_finite_diff(2, xlist, map(cube, xlist), 2) - 12 # doctest: +SKIP
-3.55271367880050e-15
we see that the example above only contain rounding errors.
apply_finite_diff can also be used on more abstract objects:
>>> from sympy import IndexedBase, Idx
>>> x, y = map(IndexedBase, 'xy')
>>> i = Idx('i')
>>> x_list, y_list = zip(*[(x[i+j], y[i+j]) for j in range(-1,2)])
>>> apply_finite_diff(1, x_list, y_list, x[i])
((x[i + 1] - x[i])/(-x[i - 1] + x[i]) - 1)*y[i]/(x[i + 1] - x[i]) -
(x[i + 1] - x[i])*y[i - 1]/((x[i + 1] - x[i - 1])*(-x[i - 1] + x[i])) +
(-x[i - 1] + x[i])*y[i + 1]/((x[i + 1] - x[i - 1])*(x[i + 1] - x[i]))
Notes
=====
Order = 0 corresponds to interpolation.
Only supply so many points you think makes sense
to around x0 when extracting the derivative (the function
need to be well behaved within that region). Also beware
of Runge's phenomenon.
See also
========
sympy.calculus.finite_diff.finite_diff_weights
References
==========
Fortran 90 implementation with Python interface for numerics: finitediff_
.. _finitediff: https://github.com/bjodah/finitediff
"""
# In the original paper the following holds for the notation:
# M = order
# N = len(x_list) - 1
N = len(x_list) - 1
if len(x_list) != len(y_list):
raise ValueError("x_list and y_list not equal in length.")
delta = finite_diff_weights(order, x_list, x0)
derivative = 0
for nu in range(len(x_list)):
derivative += delta[order][N][nu]*y_list[nu]
return derivative
def _as_finite_diff(derivative, points=1, x0=None, wrt=None):
"""
Returns an approximation of a derivative of a function in
the form of a finite difference formula. The expression is a
weighted sum of the function at a number of discrete values of
(one of) the independent variable(s).
Parameters
==========
derivative: a Derivative instance
points: sequence or coefficient, optional
If sequence: discrete values (length >= order+1) of the
independent variable used for generating the finite
difference weights.
If it is a coefficient, it will be used as the step-size
for generating an equidistant sequence of length order+1
centered around ``x0``. default: 1 (step-size 1)
x0: number or Symbol, optional
the value of the independent variable (``wrt``) at which the
derivative is to be approximated. Default: same as ``wrt``.
wrt: Symbol, optional
"with respect to" the variable for which the (partial)
derivative is to be approximated for. If not provided it
is required that the Derivative is ordinary. Default: ``None``.
Examples
========
>>> from sympy import symbols, Function, exp, sqrt, Symbol
>>> from sympy.calculus.finite_diff import _as_finite_diff
>>> x, h = symbols('x h')
>>> f = Function('f')
>>> _as_finite_diff(f(x).diff(x))
-f(x - 1/2) + f(x + 1/2)
The default step size and number of points are 1 and ``order + 1``
respectively. We can change the step size by passing a symbol
as a parameter:
>>> _as_finite_diff(f(x).diff(x), h)
-f(-h/2 + x)/h + f(h/2 + x)/h
We can also specify the discretized values to be used in a sequence:
>>> _as_finite_diff(f(x).diff(x), [x, x+h, x+2*h])
-3*f(x)/(2*h) + 2*f(h + x)/h - f(2*h + x)/(2*h)
The algorithm is not restricted to use equidistant spacing, nor
do we need to make the approximation around ``x0``, but we can get
an expression estimating the derivative at an offset:
>>> e, sq2 = exp(1), sqrt(2)
>>> xl = [x-h, x+h, x+e*h]
>>> _as_finite_diff(f(x).diff(x, 1), xl, x+h*sq2)
2*h*((h + sqrt(2)*h)/(2*h) - (-sqrt(2)*h + h)/(2*h))*f(E*h + x)/((-h + E*h)*(h + E*h)) +
(-(-sqrt(2)*h + h)/(2*h) - (-sqrt(2)*h + E*h)/(2*h))*f(-h + x)/(h + E*h) +
(-(h + sqrt(2)*h)/(2*h) + (-sqrt(2)*h + E*h)/(2*h))*f(h + x)/(-h + E*h)
Partial derivatives are also supported:
>>> y = Symbol('y')
>>> d2fdxdy=f(x,y).diff(x,y)
>>> _as_finite_diff(d2fdxdy, wrt=x)
-Derivative(f(x - 1/2, y), y) + Derivative(f(x + 1/2, y), y)
See also
========
sympy.calculus.finite_diff.apply_finite_diff
sympy.calculus.finite_diff.finite_diff_weights
"""
if derivative.is_Derivative:
pass
elif derivative.is_Atom:
return derivative
else:
return derivative.fromiter(
[_as_finite_diff(ar, points, x0, wrt) for ar
in derivative.args], **derivative.assumptions0)
if wrt is None:
old = None
for v in derivative.variables:
if old is v:
continue
derivative = _as_finite_diff(derivative, points, x0, v)
old = v
return derivative
order = derivative.variables.count(wrt)
if x0 is None:
x0 = wrt
if not iterable(points):
if getattr(points, 'is_Function', False) and wrt in points.args:
points = points.subs(wrt, x0)
# points is simply the step-size, let's make it a
# equidistant sequence centered around x0
if order % 2 == 0:
# even order => odd number of points, grid point included
points = [x0 + points*i for i
in range(-order//2, order//2 + 1)]
else:
# odd order => even number of points, half-way wrt grid point
points = [x0 + points*S(i)/2 for i
in range(-order, order + 1, 2)]
others = [wrt, 0]
for v in set(derivative.variables):
if v == wrt:
continue
others += [v, derivative.variables.count(v)]
if len(points) < order+1:
raise ValueError("Too few points for order %d" % order)
return apply_finite_diff(order, points, [
Derivative(derivative.expr.subs({wrt: x}), *others) for
x in points], x0)
def differentiate_finite(expr, *symbols,
points=1, x0=None, wrt=None, evaluate=False):
r""" Differentiate expr and replace Derivatives with finite differences.
Parameters
==========
expr : expression
\*symbols : differentiate with respect to symbols
points: sequence, coefficient or undefined function, optional
see ``Derivative.as_finite_difference``
x0: number or Symbol, optional
see ``Derivative.as_finite_difference``
wrt: Symbol, optional
see ``Derivative.as_finite_difference``
Examples
========
>>> from sympy import sin, Function, differentiate_finite
>>> from sympy.abc import x, y, h
>>> f, g = Function('f'), Function('g')
>>> differentiate_finite(f(x)*g(x), x, points=[x-h, x+h])
-f(-h + x)*g(-h + x)/(2*h) + f(h + x)*g(h + x)/(2*h)
``differentiate_finite`` works on any expression, including the expressions
with embedded derivatives:
>>> differentiate_finite(f(x) + sin(x), x, 2)
-2*f(x) + f(x - 1) + f(x + 1) - 2*sin(x) + sin(x - 1) + sin(x + 1)
>>> differentiate_finite(f(x, y), x, y)
f(x - 1/2, y - 1/2) - f(x - 1/2, y + 1/2) - f(x + 1/2, y - 1/2) + f(x + 1/2, y + 1/2)
>>> differentiate_finite(f(x)*g(x).diff(x), x)
(-g(x) + g(x + 1))*f(x + 1/2) - (g(x) - g(x - 1))*f(x - 1/2)
To make finite difference with non-constant discretization step use
undefined functions:
>>> dx = Function('dx')
>>> differentiate_finite(f(x)*g(x).diff(x), points=dx(x))
-(-g(x - dx(x)/2 - dx(x - dx(x)/2)/2)/dx(x - dx(x)/2) +
g(x - dx(x)/2 + dx(x - dx(x)/2)/2)/dx(x - dx(x)/2))*f(x - dx(x)/2)/dx(x) +
(-g(x + dx(x)/2 - dx(x + dx(x)/2)/2)/dx(x + dx(x)/2) +
g(x + dx(x)/2 + dx(x + dx(x)/2)/2)/dx(x + dx(x)/2))*f(x + dx(x)/2)/dx(x)
"""
if any(term.is_Derivative for term in list(preorder_traversal(expr))):
evaluate = False
Dexpr = expr.diff(*symbols, evaluate=evaluate)
if evaluate:
sympy_deprecation_warning("""
The evaluate flag to differentiate_finite() is deprecated.
evaluate=True expands the intermediate derivatives before computing
differences, but this usually not what you want, as it does not
satisfy the product rule.
""",
deprecated_since_version="1.5",
active_deprecations_target="deprecated-differentiate_finite-evaluate",
)
return Dexpr.replace(
lambda arg: arg.is_Derivative,
lambda arg: arg.as_finite_difference(points=points, x0=x0, wrt=wrt))
else:
DFexpr = Dexpr.as_finite_difference(points=points, x0=x0, wrt=wrt)
return DFexpr.replace(
lambda arg: isinstance(arg, Subs),
lambda arg: arg.expr.as_finite_difference(
points=points, x0=arg.point[0], wrt=arg.variables[0]))

View File

@ -0,0 +1,406 @@
"""
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

View File

@ -0,0 +1,336 @@
from sympy.core.numbers import (E, Rational, oo, pi, zoo)
from sympy.core.singleton import S
from sympy.core.symbol import Symbol
from sympy.functions.elementary.exponential import (exp, log)
from sympy.functions.elementary.miscellaneous import (Max, Min, sqrt)
from sympy.functions.elementary.trigonometric import (cos, sin, tan)
from sympy.calculus.accumulationbounds import AccumBounds
from sympy.core import Add, Mul, Pow
from sympy.core.expr import unchanged
from sympy.testing.pytest import raises, XFAIL
from sympy.abc import x
a = Symbol('a', real=True)
B = AccumBounds
def test_AccumBounds():
assert B(1, 2).args == (1, 2)
assert B(1, 2).delta is S.One
assert B(1, 2).mid == Rational(3, 2)
assert B(1, 3).is_real == True
assert B(1, 1) is S.One
assert B(1, 2) + 1 == B(2, 3)
assert 1 + B(1, 2) == B(2, 3)
assert B(1, 2) + B(2, 3) == B(3, 5)
assert -B(1, 2) == B(-2, -1)
assert B(1, 2) - 1 == B(0, 1)
assert 1 - B(1, 2) == B(-1, 0)
assert B(2, 3) - B(1, 2) == B(0, 2)
assert x + B(1, 2) == Add(B(1, 2), x)
assert a + B(1, 2) == B(1 + a, 2 + a)
assert B(1, 2) - x == Add(B(1, 2), -x)
assert B(-oo, 1) + oo == B(-oo, oo)
assert B(1, oo) + oo is oo
assert B(1, oo) - oo == B(-oo, oo)
assert (-oo - B(-1, oo)) is -oo
assert B(-oo, 1) - oo is -oo
assert B(1, oo) - oo == B(-oo, oo)
assert B(-oo, 1) - (-oo) == B(-oo, oo)
assert (oo - B(1, oo)) == B(-oo, oo)
assert (-oo - B(1, oo)) is -oo
assert B(1, 2)/2 == B(S.Half, 1)
assert 2/B(2, 3) == B(Rational(2, 3), 1)
assert 1/B(-1, 1) == B(-oo, oo)
assert abs(B(1, 2)) == B(1, 2)
assert abs(B(-2, -1)) == B(1, 2)
assert abs(B(-2, 1)) == B(0, 2)
assert abs(B(-1, 2)) == B(0, 2)
c = Symbol('c')
raises(ValueError, lambda: B(0, c))
raises(ValueError, lambda: B(1, -1))
r = Symbol('r', real=True)
raises(ValueError, lambda: B(r, r - 1))
def test_AccumBounds_mul():
assert B(1, 2)*2 == B(2, 4)
assert 2*B(1, 2) == B(2, 4)
assert B(1, 2)*B(2, 3) == B(2, 6)
assert B(0, 2)*B(2, oo) == B(0, oo)
l, r = B(-oo, oo), B(-a, a)
assert l*r == B(-oo, oo)
assert r*l == B(-oo, oo)
l, r = B(1, oo), B(-3, -2)
assert l*r == B(-oo, -2)
assert r*l == B(-oo, -2)
assert B(1, 2)*0 == 0
assert B(1, oo)*0 == B(0, oo)
assert B(-oo, 1)*0 == B(-oo, 0)
assert B(-oo, oo)*0 == B(-oo, oo)
assert B(1, 2)*x == Mul(B(1, 2), x, evaluate=False)
assert B(0, 2)*oo == B(0, oo)
assert B(-2, 0)*oo == B(-oo, 0)
assert B(0, 2)*(-oo) == B(-oo, 0)
assert B(-2, 0)*(-oo) == B(0, oo)
assert B(-1, 1)*oo == B(-oo, oo)
assert B(-1, 1)*(-oo) == B(-oo, oo)
assert B(-oo, oo)*oo == B(-oo, oo)
def test_AccumBounds_div():
assert B(-1, 3)/B(3, 4) == B(Rational(-1, 3), 1)
assert B(-2, 4)/B(-3, 4) == B(-oo, oo)
assert B(-3, -2)/B(-4, 0) == B(S.Half, oo)
# these two tests can have a better answer
# after Union of B is improved
assert B(-3, -2)/B(-2, 1) == B(-oo, oo)
assert B(2, 3)/B(-2, 2) == B(-oo, oo)
assert B(-3, -2)/B(0, 4) == B(-oo, Rational(-1, 2))
assert B(2, 4)/B(-3, 0) == B(-oo, Rational(-2, 3))
assert B(2, 4)/B(0, 3) == B(Rational(2, 3), oo)
assert B(0, 1)/B(0, 1) == B(0, oo)
assert B(-1, 0)/B(0, 1) == B(-oo, 0)
assert B(-1, 2)/B(-2, 2) == B(-oo, oo)
assert 1/B(-1, 2) == B(-oo, oo)
assert 1/B(0, 2) == B(S.Half, oo)
assert (-1)/B(0, 2) == B(-oo, Rational(-1, 2))
assert 1/B(-oo, 0) == B(-oo, 0)
assert 1/B(-1, 0) == B(-oo, -1)
assert (-2)/B(-oo, 0) == B(0, oo)
assert 1/B(-oo, -1) == B(-1, 0)
assert B(1, 2)/a == Mul(B(1, 2), 1/a, evaluate=False)
assert B(1, 2)/0 == B(1, 2)*zoo
assert B(1, oo)/oo == B(0, oo)
assert B(1, oo)/(-oo) == B(-oo, 0)
assert B(-oo, -1)/oo == B(-oo, 0)
assert B(-oo, -1)/(-oo) == B(0, oo)
assert B(-oo, oo)/oo == B(-oo, oo)
assert B(-oo, oo)/(-oo) == B(-oo, oo)
assert B(-1, oo)/oo == B(0, oo)
assert B(-1, oo)/(-oo) == B(-oo, 0)
assert B(-oo, 1)/oo == B(-oo, 0)
assert B(-oo, 1)/(-oo) == B(0, oo)
def test_issue_18795():
r = Symbol('r', real=True)
a = B(-1,1)
c = B(7, oo)
b = B(-oo, oo)
assert c - tan(r) == B(7-tan(r), oo)
assert b + tan(r) == B(-oo, oo)
assert (a + r)/a == B(-oo, oo)*B(r - 1, r + 1)
assert (b + a)/a == B(-oo, oo)
def test_AccumBounds_func():
assert (x**2 + 2*x + 1).subs(x, B(-1, 1)) == B(-1, 4)
assert exp(B(0, 1)) == B(1, E)
assert exp(B(-oo, oo)) == B(0, oo)
assert log(B(3, 6)) == B(log(3), log(6))
@XFAIL
def test_AccumBounds_powf():
nn = Symbol('nn', nonnegative=True)
assert B(1 + nn, 2 + nn)**B(1, 2) == B(1 + nn, (2 + nn)**2)
i = Symbol('i', integer=True, negative=True)
assert B(1, 2)**i == B(2**i, 1)
def test_AccumBounds_pow():
assert B(0, 2)**2 == B(0, 4)
assert B(-1, 1)**2 == B(0, 1)
assert B(1, 2)**2 == B(1, 4)
assert B(-1, 2)**3 == B(-1, 8)
assert B(-1, 1)**0 == 1
assert B(1, 2)**Rational(5, 2) == B(1, 4*sqrt(2))
assert B(0, 2)**S.Half == B(0, sqrt(2))
neg = Symbol('neg', negative=True)
assert unchanged(Pow, B(neg, 1), S.Half)
nn = Symbol('nn', nonnegative=True)
assert B(nn, nn + 1)**S.Half == B(sqrt(nn), sqrt(nn + 1))
assert B(nn, nn + 1)**nn == B(nn**nn, (nn + 1)**nn)
assert unchanged(Pow, B(nn, nn + 1), x)
i = Symbol('i', integer=True)
assert B(1, 2)**i == B(Min(1, 2**i), Max(1, 2**i))
i = Symbol('i', integer=True, nonnegative=True)
assert B(1, 2)**i == B(1, 2**i)
assert B(0, 1)**i == B(0**i, 1)
assert B(1, 5)**(-2) == B(Rational(1, 25), 1)
assert B(-1, 3)**(-2) == B(0, oo)
assert B(0, 2)**(-3) == B(Rational(1, 8), oo)
assert B(-2, 0)**(-3) == B(-oo, -Rational(1, 8))
assert B(0, 2)**(-2) == B(Rational(1, 4), oo)
assert B(-1, 2)**(-3) == B(-oo, oo)
assert B(-3, -2)**(-3) == B(Rational(-1, 8), Rational(-1, 27))
assert B(-3, -2)**(-2) == B(Rational(1, 9), Rational(1, 4))
assert B(0, oo)**S.Half == B(0, oo)
assert B(-oo, 0)**(-2) == B(0, oo)
assert B(-2, 0)**(-2) == B(Rational(1, 4), oo)
assert B(Rational(1, 3), S.Half)**oo is S.Zero
assert B(0, S.Half)**oo is S.Zero
assert B(S.Half, 1)**oo == B(0, oo)
assert B(0, 1)**oo == B(0, oo)
assert B(2, 3)**oo is oo
assert B(1, 2)**oo == B(0, oo)
assert B(S.Half, 3)**oo == B(0, oo)
assert B(Rational(-1, 3), Rational(-1, 4))**oo is S.Zero
assert B(-1, Rational(-1, 2))**oo is S.NaN
assert B(-3, -2)**oo is zoo
assert B(-2, -1)**oo is S.NaN
assert B(-2, Rational(-1, 2))**oo is S.NaN
assert B(Rational(-1, 2), S.Half)**oo is S.Zero
assert B(Rational(-1, 2), 1)**oo == B(0, oo)
assert B(Rational(-2, 3), 2)**oo == B(0, oo)
assert B(-1, 1)**oo == B(-oo, oo)
assert B(-1, S.Half)**oo == B(-oo, oo)
assert B(-1, 2)**oo == B(-oo, oo)
assert B(-2, S.Half)**oo == B(-oo, oo)
assert B(1, 2)**x == Pow(B(1, 2), x, evaluate=False)
assert B(2, 3)**(-oo) is S.Zero
assert B(0, 2)**(-oo) == B(0, oo)
assert B(-1, 2)**(-oo) == B(-oo, oo)
assert (tan(x)**sin(2*x)).subs(x, B(0, pi/2)) == \
Pow(B(-oo, oo), B(0, 1))
def test_AccumBounds_exponent():
# base is 0
z = 0**B(a, a + S.Half)
assert z.subs(a, 0) == B(0, 1)
assert z.subs(a, 1) == 0
p = z.subs(a, -1)
assert p.is_Pow and p.args == (0, B(-1, -S.Half))
# base > 0
# when base is 1 the type of bounds does not matter
assert 1**B(a, a + 1) == 1
# otherwise we need to know if 0 is in the bounds
assert S.Half**B(-2, 2) == B(S(1)/4, 4)
assert 2**B(-2, 2) == B(S(1)/4, 4)
# +eps may introduce +oo
# if there is a negative integer exponent
assert B(0, 1)**B(S(1)/2, 1) == B(0, 1)
assert B(0, 1)**B(0, 1) == B(0, 1)
# positive bases have positive bounds
assert B(2, 3)**B(-3, -2) == B(S(1)/27, S(1)/4)
assert B(2, 3)**B(-3, 2) == B(S(1)/27, 9)
# bounds generating imaginary parts unevaluated
assert unchanged(Pow, B(-1, 1), B(1, 2))
assert B(0, S(1)/2)**B(1, oo) == B(0, S(1)/2)
assert B(0, 1)**B(1, oo) == B(0, oo)
assert B(0, 2)**B(1, oo) == B(0, oo)
assert B(0, oo)**B(1, oo) == B(0, oo)
assert B(S(1)/2, 1)**B(1, oo) == B(0, oo)
assert B(S(1)/2, 1)**B(-oo, -1) == B(0, oo)
assert B(S(1)/2, 1)**B(-oo, oo) == B(0, oo)
assert B(S(1)/2, 2)**B(1, oo) == B(0, oo)
assert B(S(1)/2, 2)**B(-oo, -1) == B(0, oo)
assert B(S(1)/2, 2)**B(-oo, oo) == B(0, oo)
assert B(S(1)/2, oo)**B(1, oo) == B(0, oo)
assert B(S(1)/2, oo)**B(-oo, -1) == B(0, oo)
assert B(S(1)/2, oo)**B(-oo, oo) == B(0, oo)
assert B(1, 2)**B(1, oo) == B(0, oo)
assert B(1, 2)**B(-oo, -1) == B(0, oo)
assert B(1, 2)**B(-oo, oo) == B(0, oo)
assert B(1, oo)**B(1, oo) == B(0, oo)
assert B(1, oo)**B(-oo, -1) == B(0, oo)
assert B(1, oo)**B(-oo, oo) == B(0, oo)
assert B(2, oo)**B(1, oo) == B(2, oo)
assert B(2, oo)**B(-oo, -1) == B(0, S(1)/2)
assert B(2, oo)**B(-oo, oo) == B(0, oo)
def test_comparison_AccumBounds():
assert (B(1, 3) < 4) == S.true
assert (B(1, 3) < -1) == S.false
assert (B(1, 3) < 2).rel_op == '<'
assert (B(1, 3) <= 2).rel_op == '<='
assert (B(1, 3) > 4) == S.false
assert (B(1, 3) > -1) == S.true
assert (B(1, 3) > 2).rel_op == '>'
assert (B(1, 3) >= 2).rel_op == '>='
assert (B(1, 3) < B(4, 6)) == S.true
assert (B(1, 3) < B(2, 4)).rel_op == '<'
assert (B(1, 3) < B(-2, 0)) == S.false
assert (B(1, 3) <= B(4, 6)) == S.true
assert (B(1, 3) <= B(-2, 0)) == S.false
assert (B(1, 3) > B(4, 6)) == S.false
assert (B(1, 3) > B(-2, 0)) == S.true
assert (B(1, 3) >= B(4, 6)) == S.false
assert (B(1, 3) >= B(-2, 0)) == S.true
# issue 13499
assert (cos(x) > 0).subs(x, oo) == (B(-1, 1) > 0)
c = Symbol('c')
raises(TypeError, lambda: (B(0, 1) < c))
raises(TypeError, lambda: (B(0, 1) <= c))
raises(TypeError, lambda: (B(0, 1) > c))
raises(TypeError, lambda: (B(0, 1) >= c))
def test_contains_AccumBounds():
assert (1 in B(1, 2)) == S.true
raises(TypeError, lambda: a in B(1, 2))
assert 0 in B(-1, 0)
raises(TypeError, lambda:
(cos(1)**2 + sin(1)**2 - 1) in B(-1, 0))
assert (-oo in B(1, oo)) == S.true
assert (oo in B(-oo, 0)) == S.true
# issue 13159
assert Mul(0, B(-1, 1)) == Mul(B(-1, 1), 0) == 0
import itertools
for perm in itertools.permutations([0, B(-1, 1), x]):
assert Mul(*perm) == 0
def test_intersection_AccumBounds():
assert B(0, 3).intersection(B(1, 2)) == B(1, 2)
assert B(0, 3).intersection(B(1, 4)) == B(1, 3)
assert B(0, 3).intersection(B(-1, 2)) == B(0, 2)
assert B(0, 3).intersection(B(-1, 4)) == B(0, 3)
assert B(0, 1).intersection(B(2, 3)) == S.EmptySet
raises(TypeError, lambda: B(0, 3).intersection(1))
def test_union_AccumBounds():
assert B(0, 3).union(B(1, 2)) == B(0, 3)
assert B(0, 3).union(B(1, 4)) == B(0, 4)
assert B(0, 3).union(B(-1, 2)) == B(-1, 3)
assert B(0, 3).union(B(-1, 4)) == B(-1, 4)
raises(TypeError, lambda: B(0, 3).union(1))

View File

@ -0,0 +1,74 @@
from sympy.core.function import (Derivative as D, Function)
from sympy.core.relational import Eq
from sympy.core.symbol import (Symbol, symbols)
from sympy.functions.elementary.trigonometric import (cos, sin)
from sympy.testing.pytest import raises
from sympy.calculus.euler import euler_equations as euler
def test_euler_interface():
x = Function('x')
y = Symbol('y')
t = Symbol('t')
raises(TypeError, lambda: euler())
raises(TypeError, lambda: euler(D(x(t), t)*y(t), [x(t), y]))
raises(ValueError, lambda: euler(D(x(t), t)*x(y), [x(t), x(y)]))
raises(TypeError, lambda: euler(D(x(t), t)**2, x(0)))
raises(TypeError, lambda: euler(D(x(t), t)*y(t), [t]))
assert euler(D(x(t), t)**2/2, {x(t)}) == [Eq(-D(x(t), t, t), 0)]
assert euler(D(x(t), t)**2/2, x(t), {t}) == [Eq(-D(x(t), t, t), 0)]
def test_euler_pendulum():
x = Function('x')
t = Symbol('t')
L = D(x(t), t)**2/2 + cos(x(t))
assert euler(L, x(t), t) == [Eq(-sin(x(t)) - D(x(t), t, t), 0)]
def test_euler_henonheiles():
x = Function('x')
y = Function('y')
t = Symbol('t')
L = sum(D(z(t), t)**2/2 - z(t)**2/2 for z in [x, y])
L += -x(t)**2*y(t) + y(t)**3/3
assert euler(L, [x(t), y(t)], t) == [Eq(-2*x(t)*y(t) - x(t) -
D(x(t), t, t), 0),
Eq(-x(t)**2 + y(t)**2 -
y(t) - D(y(t), t, t), 0)]
def test_euler_sineg():
psi = Function('psi')
t = Symbol('t')
x = Symbol('x')
L = D(psi(t, x), t)**2/2 - D(psi(t, x), x)**2/2 + cos(psi(t, x))
assert euler(L, psi(t, x), [t, x]) == [Eq(-sin(psi(t, x)) -
D(psi(t, x), t, t) +
D(psi(t, x), x, x), 0)]
def test_euler_high_order():
# an example from hep-th/0309038
m = Symbol('m')
k = Symbol('k')
x = Function('x')
y = Function('y')
t = Symbol('t')
L = (m*D(x(t), t)**2/2 + m*D(y(t), t)**2/2 -
k*D(x(t), t)*D(y(t), t, t) + k*D(y(t), t)*D(x(t), t, t))
assert euler(L, [x(t), y(t)]) == [Eq(2*k*D(y(t), t, t, t) -
m*D(x(t), t, t), 0),
Eq(-2*k*D(x(t), t, t, t) -
m*D(y(t), t, t), 0)]
w = Symbol('w')
L = D(x(t, w), t, w)**2/2
assert euler(L) == [Eq(D(x(t, w), t, t, w, w), 0)]
def test_issue_18653():
x, y, z = symbols("x y z")
f, g, h = symbols("f g h", cls=Function, args=(x, y))
f, g, h = f(), g(), h()
expr2 = f.diff(x)*h.diff(z)
assert euler(expr2, (f,), (x, y)) == []

View File

@ -0,0 +1,168 @@
from itertools import product
from sympy.core.function import (Function, diff)
from sympy.core.numbers import Rational
from sympy.core.singleton import S
from sympy.core.symbol import symbols
from sympy.functions.elementary.exponential import exp
from sympy.calculus.finite_diff import (
apply_finite_diff, differentiate_finite, finite_diff_weights,
_as_finite_diff
)
from sympy.testing.pytest import raises, warns_deprecated_sympy
def test_apply_finite_diff():
x, h = symbols('x h')
f = Function('f')
assert (apply_finite_diff(1, [x-h, x+h], [f(x-h), f(x+h)], x) -
(f(x+h)-f(x-h))/(2*h)).simplify() == 0
assert (apply_finite_diff(1, [5, 6, 7], [f(5), f(6), f(7)], 5) -
(Rational(-3, 2)*f(5) + 2*f(6) - S.Half*f(7))).simplify() == 0
raises(ValueError, lambda: apply_finite_diff(1, [x, h], [f(x)]))
def test_finite_diff_weights():
d = finite_diff_weights(1, [5, 6, 7], 5)
assert d[1][2] == [Rational(-3, 2), 2, Rational(-1, 2)]
# Table 1, p. 702 in doi:10.1090/S0025-5718-1988-0935077-0
# --------------------------------------------------------
xl = [0, 1, -1, 2, -2, 3, -3, 4, -4]
# d holds all coefficients
d = finite_diff_weights(4, xl, S.Zero)
# Zeroeth derivative
for i in range(5):
assert d[0][i] == [S.One] + [S.Zero]*8
# First derivative
assert d[1][0] == [S.Zero]*9
assert d[1][2] == [S.Zero, S.Half, Rational(-1, 2)] + [S.Zero]*6
assert d[1][4] == [S.Zero, Rational(2, 3), Rational(-2, 3), Rational(-1, 12), Rational(1, 12)] + [S.Zero]*4
assert d[1][6] == [S.Zero, Rational(3, 4), Rational(-3, 4), Rational(-3, 20), Rational(3, 20),
Rational(1, 60), Rational(-1, 60)] + [S.Zero]*2
assert d[1][8] == [S.Zero, Rational(4, 5), Rational(-4, 5), Rational(-1, 5), Rational(1, 5),
Rational(4, 105), Rational(-4, 105), Rational(-1, 280), Rational(1, 280)]
# Second derivative
for i in range(2):
assert d[2][i] == [S.Zero]*9
assert d[2][2] == [-S(2), S.One, S.One] + [S.Zero]*6
assert d[2][4] == [Rational(-5, 2), Rational(4, 3), Rational(4, 3), Rational(-1, 12), Rational(-1, 12)] + [S.Zero]*4
assert d[2][6] == [Rational(-49, 18), Rational(3, 2), Rational(3, 2), Rational(-3, 20), Rational(-3, 20),
Rational(1, 90), Rational(1, 90)] + [S.Zero]*2
assert d[2][8] == [Rational(-205, 72), Rational(8, 5), Rational(8, 5), Rational(-1, 5), Rational(-1, 5),
Rational(8, 315), Rational(8, 315), Rational(-1, 560), Rational(-1, 560)]
# Third derivative
for i in range(3):
assert d[3][i] == [S.Zero]*9
assert d[3][4] == [S.Zero, -S.One, S.One, S.Half, Rational(-1, 2)] + [S.Zero]*4
assert d[3][6] == [S.Zero, Rational(-13, 8), Rational(13, 8), S.One, -S.One,
Rational(-1, 8), Rational(1, 8)] + [S.Zero]*2
assert d[3][8] == [S.Zero, Rational(-61, 30), Rational(61, 30), Rational(169, 120), Rational(-169, 120),
Rational(-3, 10), Rational(3, 10), Rational(7, 240), Rational(-7, 240)]
# Fourth derivative
for i in range(4):
assert d[4][i] == [S.Zero]*9
assert d[4][4] == [S(6), -S(4), -S(4), S.One, S.One] + [S.Zero]*4
assert d[4][6] == [Rational(28, 3), Rational(-13, 2), Rational(-13, 2), S(2), S(2),
Rational(-1, 6), Rational(-1, 6)] + [S.Zero]*2
assert d[4][8] == [Rational(91, 8), Rational(-122, 15), Rational(-122, 15), Rational(169, 60), Rational(169, 60),
Rational(-2, 5), Rational(-2, 5), Rational(7, 240), Rational(7, 240)]
# Table 2, p. 703 in doi:10.1090/S0025-5718-1988-0935077-0
# --------------------------------------------------------
xl = [[j/S(2) for j in list(range(-i*2+1, 0, 2))+list(range(1, i*2+1, 2))]
for i in range(1, 5)]
# d holds all coefficients
d = [finite_diff_weights({0: 1, 1: 2, 2: 4, 3: 4}[i], xl[i], 0) for
i in range(4)]
# Zeroth derivative
assert d[0][0][1] == [S.Half, S.Half]
assert d[1][0][3] == [Rational(-1, 16), Rational(9, 16), Rational(9, 16), Rational(-1, 16)]
assert d[2][0][5] == [Rational(3, 256), Rational(-25, 256), Rational(75, 128), Rational(75, 128),
Rational(-25, 256), Rational(3, 256)]
assert d[3][0][7] == [Rational(-5, 2048), Rational(49, 2048), Rational(-245, 2048), Rational(1225, 2048),
Rational(1225, 2048), Rational(-245, 2048), Rational(49, 2048), Rational(-5, 2048)]
# First derivative
assert d[0][1][1] == [-S.One, S.One]
assert d[1][1][3] == [Rational(1, 24), Rational(-9, 8), Rational(9, 8), Rational(-1, 24)]
assert d[2][1][5] == [Rational(-3, 640), Rational(25, 384), Rational(-75, 64),
Rational(75, 64), Rational(-25, 384), Rational(3, 640)]
assert d[3][1][7] == [Rational(5, 7168), Rational(-49, 5120),
Rational(245, 3072), Rational(-1225, 1024),
Rational(1225, 1024), Rational(-245, 3072),
Rational(49, 5120), Rational(-5, 7168)]
# Reasonably the rest of the table is also correct... (testing of that
# deemed excessive at the moment)
raises(ValueError, lambda: finite_diff_weights(-1, [1, 2]))
raises(ValueError, lambda: finite_diff_weights(1.2, [1, 2]))
x = symbols('x')
raises(ValueError, lambda: finite_diff_weights(x, [1, 2]))
def test_as_finite_diff():
x = symbols('x')
f = Function('f')
dx = Function('dx')
_as_finite_diff(f(x).diff(x), [x-2, x-1, x, x+1, x+2])
# Use of undefined functions in ``points``
df_true = -f(x+dx(x)/2-dx(x+dx(x)/2)/2) / dx(x+dx(x)/2) \
+ f(x+dx(x)/2+dx(x+dx(x)/2)/2) / dx(x+dx(x)/2)
df_test = diff(f(x), x).as_finite_difference(points=dx(x), x0=x+dx(x)/2)
assert (df_test - df_true).simplify() == 0
def test_differentiate_finite():
x, y, h = symbols('x y h')
f = Function('f')
with warns_deprecated_sympy():
res0 = differentiate_finite(f(x, y) + exp(42), x, y, evaluate=True)
xm, xp, ym, yp = [v + sign*S.Half for v, sign in product([x, y], [-1, 1])]
ref0 = f(xm, ym) + f(xp, yp) - f(xm, yp) - f(xp, ym)
assert (res0 - ref0).simplify() == 0
g = Function('g')
with warns_deprecated_sympy():
res1 = differentiate_finite(f(x)*g(x) + 42, x, evaluate=True)
ref1 = (-f(x - S.Half) + f(x + S.Half))*g(x) + \
(-g(x - S.Half) + g(x + S.Half))*f(x)
assert (res1 - ref1).simplify() == 0
res2 = differentiate_finite(f(x) + x**3 + 42, x, points=[x-1, x+1])
ref2 = (f(x + 1) + (x + 1)**3 - f(x - 1) - (x - 1)**3)/2
assert (res2 - ref2).simplify() == 0
raises(TypeError, lambda: differentiate_finite(f(x)*g(x), x,
pints=[x-1, x+1]))
res3 = differentiate_finite(f(x)*g(x).diff(x), x)
ref3 = (-g(x) + g(x + 1))*f(x + S.Half) - (g(x) - g(x - 1))*f(x - S.Half)
assert res3 == ref3
res4 = differentiate_finite(f(x)*g(x).diff(x).diff(x), x)
ref4 = -((g(x - Rational(3, 2)) - 2*g(x - S.Half) + g(x + S.Half))*f(x - S.Half)) \
+ (g(x - S.Half) - 2*g(x + S.Half) + g(x + Rational(3, 2)))*f(x + S.Half)
assert res4 == ref4
res5_expr = f(x).diff(x)*g(x).diff(x)
res5 = differentiate_finite(res5_expr, points=[x-h, x, x+h])
ref5 = (-2*f(x)/h + f(-h + x)/(2*h) + 3*f(h + x)/(2*h))*(-2*g(x)/h + g(-h + x)/(2*h) \
+ 3*g(h + x)/(2*h))/(2*h) - (2*f(x)/h - 3*f(-h + x)/(2*h) - \
f(h + x)/(2*h))*(2*g(x)/h - 3*g(-h + x)/(2*h) - g(h + x)/(2*h))/(2*h)
assert res5 == ref5
res6 = res5.limit(h, 0).doit()
ref6 = diff(res5_expr, x)
assert res6 == ref6

View File

@ -0,0 +1,122 @@
from sympy.core.numbers import (I, Rational, pi, oo)
from sympy.core.singleton import S
from sympy.core.symbol import Symbol, Dummy
from sympy.core.function import Lambda
from sympy.functions.elementary.exponential import (exp, log)
from sympy.functions.elementary.trigonometric import sec, csc
from sympy.functions.elementary.hyperbolic import (coth, sech,
atanh, asech, acoth, acsch)
from sympy.functions.elementary.miscellaneous import sqrt
from sympy.calculus.singularities import (
singularities,
is_increasing,
is_strictly_increasing,
is_decreasing,
is_strictly_decreasing,
is_monotonic
)
from sympy.sets import Interval, FiniteSet, Union, ImageSet
from sympy.testing.pytest import raises
from sympy.abc import x, y
def test_singularities():
x = Symbol('x')
assert singularities(x**2, x) == S.EmptySet
assert singularities(x/(x**2 + 3*x + 2), x) == FiniteSet(-2, -1)
assert singularities(1/(x**2 + 1), x) == FiniteSet(I, -I)
assert singularities(x/(x**3 + 1), x) == \
FiniteSet(-1, (1 - sqrt(3) * I) / 2, (1 + sqrt(3) * I) / 2)
assert singularities(1/(y**2 + 2*I*y + 1), y) == \
FiniteSet(-I + sqrt(2)*I, -I - sqrt(2)*I)
_n = Dummy('n')
assert singularities(sech(x), x).dummy_eq(Union(
ImageSet(Lambda(_n, 2*_n*I*pi + I*pi/2), S.Integers),
ImageSet(Lambda(_n, 2*_n*I*pi + 3*I*pi/2), S.Integers)))
assert singularities(coth(x), x).dummy_eq(Union(
ImageSet(Lambda(_n, 2*_n*I*pi + I*pi), S.Integers),
ImageSet(Lambda(_n, 2*_n*I*pi), S.Integers)))
assert singularities(atanh(x), x) == FiniteSet(-1, 1)
assert singularities(acoth(x), x) == FiniteSet(-1, 1)
assert singularities(asech(x), x) == FiniteSet(0)
assert singularities(acsch(x), x) == FiniteSet(0)
x = Symbol('x', real=True)
assert singularities(1/(x**2 + 1), x) == S.EmptySet
assert singularities(exp(1/x), x, S.Reals) == FiniteSet(0)
assert singularities(exp(1/x), x, Interval(1, 2)) == S.EmptySet
assert singularities(log((x - 2)**2), x, Interval(1, 3)) == FiniteSet(2)
raises(NotImplementedError, lambda: singularities(x**-oo, x))
assert singularities(sec(x), x, Interval(0, 3*pi)) == FiniteSet(
pi/2, 3*pi/2, 5*pi/2)
assert singularities(csc(x), x, Interval(0, 3*pi)) == FiniteSet(
0, pi, 2*pi, 3*pi)
def test_is_increasing():
"""Test whether is_increasing returns correct value."""
a = Symbol('a', negative=True)
assert is_increasing(x**3 - 3*x**2 + 4*x, S.Reals)
assert is_increasing(-x**2, Interval(-oo, 0))
assert not is_increasing(-x**2, Interval(0, oo))
assert not is_increasing(4*x**3 - 6*x**2 - 72*x + 30, Interval(-2, 3))
assert is_increasing(x**2 + y, Interval(1, oo), x)
assert is_increasing(-x**2*a, Interval(1, oo), x)
assert is_increasing(1)
assert is_increasing(4*x**3 - 6*x**2 - 72*x + 30, Interval(-2, 3)) is False
def test_is_strictly_increasing():
"""Test whether is_strictly_increasing returns correct value."""
assert is_strictly_increasing(
4*x**3 - 6*x**2 - 72*x + 30, Interval.Ropen(-oo, -2))
assert is_strictly_increasing(
4*x**3 - 6*x**2 - 72*x + 30, Interval.Lopen(3, oo))
assert not is_strictly_increasing(
4*x**3 - 6*x**2 - 72*x + 30, Interval.open(-2, 3))
assert not is_strictly_increasing(-x**2, Interval(0, oo))
assert not is_strictly_decreasing(1)
assert is_strictly_increasing(4*x**3 - 6*x**2 - 72*x + 30, Interval.open(-2, 3)) is False
def test_is_decreasing():
"""Test whether is_decreasing returns correct value."""
b = Symbol('b', positive=True)
assert is_decreasing(1/(x**2 - 3*x), Interval.open(Rational(3,2), 3))
assert is_decreasing(1/(x**2 - 3*x), Interval.open(1.5, 3))
assert is_decreasing(1/(x**2 - 3*x), Interval.Lopen(3, oo))
assert not is_decreasing(1/(x**2 - 3*x), Interval.Ropen(-oo, Rational(3, 2)))
assert not is_decreasing(-x**2, Interval(-oo, 0))
assert not is_decreasing(-x**2*b, Interval(-oo, 0), x)
def test_is_strictly_decreasing():
"""Test whether is_strictly_decreasing returns correct value."""
assert is_strictly_decreasing(1/(x**2 - 3*x), Interval.Lopen(3, oo))
assert not is_strictly_decreasing(
1/(x**2 - 3*x), Interval.Ropen(-oo, Rational(3, 2)))
assert not is_strictly_decreasing(-x**2, Interval(-oo, 0))
assert not is_strictly_decreasing(1)
assert is_strictly_decreasing(1/(x**2 - 3*x), Interval.open(Rational(3,2), 3))
assert is_strictly_decreasing(1/(x**2 - 3*x), Interval.open(1.5, 3))
def test_is_monotonic():
"""Test whether is_monotonic returns correct value."""
assert is_monotonic(1/(x**2 - 3*x), Interval.open(Rational(3,2), 3))
assert is_monotonic(1/(x**2 - 3*x), Interval.open(1.5, 3))
assert is_monotonic(1/(x**2 - 3*x), Interval.Lopen(3, oo))
assert is_monotonic(x**3 - 3*x**2 + 4*x, S.Reals)
assert not is_monotonic(-x**2, S.Reals)
assert is_monotonic(x**2 + y + 1, Interval(1, 2), x)
raises(NotImplementedError, lambda: is_monotonic(x**2 + y + 1))
def test_issue_23401():
x = Symbol('x')
expr = (x + 1)/(-1.0e-3*x**2 + 0.1*x + 0.1)
assert is_increasing(expr, Interval(1,2), x)

View File

@ -0,0 +1,392 @@
from sympy.core.function import Lambda
from sympy.core.numbers import (E, I, Rational, oo, pi)
from sympy.core.relational import Eq
from sympy.core.singleton import S
from sympy.core.symbol import (Dummy, Symbol)
from sympy.functions.elementary.complexes import (Abs, re)
from sympy.functions.elementary.exponential import (exp, log)
from sympy.functions.elementary.integers import frac
from sympy.functions.elementary.miscellaneous import sqrt
from sympy.functions.elementary.piecewise import Piecewise
from sympy.functions.elementary.trigonometric import (
cos, cot, csc, sec, sin, tan, asin, acos, atan, acot, asec, acsc)
from sympy.functions.elementary.hyperbolic import (sinh, cosh, tanh, coth,
sech, csch, asinh, acosh, atanh, acoth, asech, acsch)
from sympy.functions.special.gamma_functions import gamma
from sympy.functions.special.error_functions import expint
from sympy.matrices.expressions.matexpr import MatrixSymbol
from sympy.simplify.simplify import simplify
from sympy.calculus.util import (function_range, continuous_domain, not_empty_in,
periodicity, lcim, is_convex,
stationary_points, minimum, maximum)
from sympy.sets.sets import (Interval, FiniteSet, Complement, Union)
from sympy.sets.fancysets import ImageSet
from sympy.sets.conditionset import ConditionSet
from sympy.testing.pytest import XFAIL, raises, _both_exp_pow, slow
from sympy.abc import x, y
a = Symbol('a', real=True)
def test_function_range():
assert function_range(sin(x), x, Interval(-pi/2, pi/2)
) == Interval(-1, 1)
assert function_range(sin(x), x, Interval(0, pi)
) == Interval(0, 1)
assert function_range(tan(x), x, Interval(0, pi)
) == Interval(-oo, oo)
assert function_range(tan(x), x, Interval(pi/2, pi)
) == Interval(-oo, 0)
assert function_range((x + 3)/(x - 2), x, Interval(-5, 5)
) == Union(Interval(-oo, Rational(2, 7)), Interval(Rational(8, 3), oo))
assert function_range(1/(x**2), x, Interval(-1, 1)
) == Interval(1, oo)
assert function_range(exp(x), x, Interval(-1, 1)
) == Interval(exp(-1), exp(1))
assert function_range(log(x) - x, x, S.Reals
) == Interval(-oo, -1)
assert function_range(sqrt(3*x - 1), x, Interval(0, 2)
) == Interval(0, sqrt(5))
assert function_range(x*(x - 1) - (x**2 - x), x, S.Reals
) == FiniteSet(0)
assert function_range(x*(x - 1) - (x**2 - x) + y, x, S.Reals
) == FiniteSet(y)
assert function_range(sin(x), x, Union(Interval(-5, -3), FiniteSet(4))
) == Union(Interval(-sin(3), 1), FiniteSet(sin(4)))
assert function_range(cos(x), x, Interval(-oo, -4)
) == Interval(-1, 1)
assert function_range(cos(x), x, S.EmptySet) == S.EmptySet
assert function_range(x/sqrt(x**2+1), x, S.Reals) == Interval.open(-1,1)
raises(NotImplementedError, lambda : function_range(
exp(x)*(sin(x) - cos(x))/2 - x, x, S.Reals))
raises(NotImplementedError, lambda : function_range(
sin(x) + x, x, S.Reals)) # issue 13273
raises(NotImplementedError, lambda : function_range(
log(x), x, S.Integers))
raises(NotImplementedError, lambda : function_range(
sin(x)/2, x, S.Naturals))
@slow
def test_function_range1():
assert function_range(tan(x)**2 + tan(3*x)**2 + 1, x, S.Reals) == Interval(1,oo)
def test_continuous_domain():
assert continuous_domain(sin(x), x, Interval(0, 2*pi)) == Interval(0, 2*pi)
assert continuous_domain(tan(x), x, Interval(0, 2*pi)) == \
Union(Interval(0, pi/2, False, True), Interval(pi/2, pi*Rational(3, 2), True, True),
Interval(pi*Rational(3, 2), 2*pi, True, False))
assert continuous_domain(cot(x), x, Interval(0, 2*pi)) == Union(
Interval.open(0, pi), Interval.open(pi, 2*pi))
assert continuous_domain((x - 1)/((x - 1)**2), x, S.Reals) == \
Union(Interval(-oo, 1, True, True), Interval(1, oo, True, True))
assert continuous_domain(log(x) + log(4*x - 1), x, S.Reals) == \
Interval(Rational(1, 4), oo, True, True)
assert continuous_domain(1/sqrt(x - 3), x, S.Reals) == Interval(3, oo, True, True)
assert continuous_domain(1/x - 2, x, S.Reals) == \
Union(Interval.open(-oo, 0), Interval.open(0, oo))
assert continuous_domain(1/(x**2 - 4) + 2, x, S.Reals) == \
Union(Interval.open(-oo, -2), Interval.open(-2, 2), Interval.open(2, oo))
assert continuous_domain((x+1)**pi, x, S.Reals) == Interval(-1, oo)
assert continuous_domain((x+1)**(pi/2), x, S.Reals) == Interval(-1, oo)
assert continuous_domain(x**x, x, S.Reals) == Interval(0, oo)
assert continuous_domain((x+1)**log(x**2), x, S.Reals) == Union(
Interval.Ropen(-1, 0), Interval.open(0, oo))
domain = continuous_domain(log(tan(x)**2 + 1), x, S.Reals)
assert not domain.contains(3*pi/2)
assert domain.contains(5)
d = Symbol('d', even=True, zero=False)
assert continuous_domain(x**(1/d), x, S.Reals) == Interval(0, oo)
n = Dummy('n')
assert continuous_domain(1/sin(x), x, S.Reals).dummy_eq(Complement(
S.Reals, Union(ImageSet(Lambda(n, 2*n*pi + pi), S.Integers),
ImageSet(Lambda(n, 2*n*pi), S.Integers))))
assert continuous_domain(sin(x) + cos(x), x, S.Reals) == S.Reals
assert continuous_domain(asin(x), x, S.Reals) == Interval(-1, 1) # issue #21786
assert continuous_domain(1/acos(log(x)), x, S.Reals) == Interval.Ropen(exp(-1), E)
assert continuous_domain(sinh(x)+cosh(x), x, S.Reals) == S.Reals
assert continuous_domain(tanh(x)+sech(x), x, S.Reals) == S.Reals
assert continuous_domain(atan(x)+asinh(x), x, S.Reals) == S.Reals
assert continuous_domain(acosh(x), x, S.Reals) == Interval(1, oo)
assert continuous_domain(atanh(x), x, S.Reals) == Interval.open(-1, 1)
assert continuous_domain(atanh(x)+acosh(x), x, S.Reals) == S.EmptySet
assert continuous_domain(asech(x), x, S.Reals) == Interval.Lopen(0, 1)
assert continuous_domain(acoth(x), x, S.Reals) == Union(
Interval.open(-oo, -1), Interval.open(1, oo))
assert continuous_domain(asec(x), x, S.Reals) == Union(
Interval(-oo, -1), Interval(1, oo))
assert continuous_domain(acsc(x), x, S.Reals) == Union(
Interval(-oo, -1), Interval(1, oo))
for f in (coth, acsch, csch):
assert continuous_domain(f(x), x, S.Reals) == Union(
Interval.open(-oo, 0), Interval.open(0, oo))
assert continuous_domain(acot(x), x, S.Reals).contains(0) == False
assert continuous_domain(1/(exp(x) - x), x, S.Reals) == Complement(
S.Reals, ConditionSet(x, Eq(-x + exp(x), 0), S.Reals))
assert continuous_domain(frac(x**2), x, Interval(-2,-1)) == Union(
Interval.open(-2, -sqrt(3)), Interval.open(-sqrt(2), -1),
Interval.open(-sqrt(3), -sqrt(2)))
assert continuous_domain(frac(x), x, S.Reals) == Complement(
S.Reals, S.Integers)
raises(NotImplementedError, lambda : continuous_domain(
1/(x**2+1), x, S.Complexes))
raises(NotImplementedError, lambda : continuous_domain(
gamma(x), x, Interval(-5,0)))
assert continuous_domain(x + gamma(pi), x, S.Reals) == S.Reals
@XFAIL
def test_continuous_domain_acot():
acot_cont = Piecewise((pi+acot(x), x<0), (acot(x), True))
assert continuous_domain(acot_cont, x, S.Reals) == S.Reals
@XFAIL
def test_continuous_domain_gamma():
assert continuous_domain(gamma(x), x, S.Reals).contains(-1) == False
@XFAIL
def test_continuous_domain_neg_power():
assert continuous_domain((x-2)**(1-x), x, S.Reals) == Interval.open(2, oo)
def test_not_empty_in():
assert not_empty_in(FiniteSet(x, 2*x).intersect(Interval(1, 2, True, False)), x) == \
Interval(S.Half, 2, True, False)
assert not_empty_in(FiniteSet(x, x**2).intersect(Interval(1, 2)), x) == \
Union(Interval(-sqrt(2), -1), Interval(1, 2))
assert not_empty_in(FiniteSet(x**2 + x, x).intersect(Interval(2, 4)), x) == \
Union(Interval(-sqrt(17)/2 - S.Half, -2),
Interval(1, Rational(-1, 2) + sqrt(17)/2), Interval(2, 4))
assert not_empty_in(FiniteSet(x/(x - 1)).intersect(S.Reals), x) == \
Complement(S.Reals, FiniteSet(1))
assert not_empty_in(FiniteSet(a/(a - 1)).intersect(S.Reals), a) == \
Complement(S.Reals, FiniteSet(1))
assert not_empty_in(FiniteSet((x**2 - 3*x + 2)/(x - 1)).intersect(S.Reals), x) == \
Complement(S.Reals, FiniteSet(1))
assert not_empty_in(FiniteSet(3, 4, x/(x - 1)).intersect(Interval(2, 3)), x) == \
Interval(-oo, oo)
assert not_empty_in(FiniteSet(4, x/(x - 1)).intersect(Interval(2, 3)), x) == \
Interval(S(3)/2, 2)
assert not_empty_in(FiniteSet(x/(x**2 - 1)).intersect(S.Reals), x) == \
Complement(S.Reals, FiniteSet(-1, 1))
assert not_empty_in(FiniteSet(x, x**2).intersect(Union(Interval(1, 3, True, True),
Interval(4, 5))), x) == \
Union(Interval(-sqrt(5), -2), Interval(-sqrt(3), -1, True, True),
Interval(1, 3, True, True), Interval(4, 5))
assert not_empty_in(FiniteSet(1).intersect(Interval(3, 4)), x) == S.EmptySet
assert not_empty_in(FiniteSet(x**2/(x + 2)).intersect(Interval(1, oo)), x) == \
Union(Interval(-2, -1, True, False), Interval(2, oo))
raises(ValueError, lambda: not_empty_in(x))
raises(ValueError, lambda: not_empty_in(Interval(0, 1), x))
raises(NotImplementedError,
lambda: not_empty_in(FiniteSet(x).intersect(S.Reals), x, a))
@_both_exp_pow
def test_periodicity():
assert periodicity(sin(2*x), x) == pi
assert periodicity((-2)*tan(4*x), x) == pi/4
assert periodicity(sin(x)**2, x) == 2*pi
assert periodicity(3**tan(3*x), x) == pi/3
assert periodicity(tan(x)*cos(x), x) == 2*pi
assert periodicity(sin(x)**(tan(x)), x) == 2*pi
assert periodicity(tan(x)*sec(x), x) == 2*pi
assert periodicity(sin(2*x)*cos(2*x) - y, x) == pi/2
assert periodicity(tan(x) + cot(x), x) == pi
assert periodicity(sin(x) - cos(2*x), x) == 2*pi
assert periodicity(sin(x) - 1, x) == 2*pi
assert periodicity(sin(4*x) + sin(x)*cos(x), x) == pi
assert periodicity(exp(sin(x)), x) == 2*pi
assert periodicity(log(cot(2*x)) - sin(cos(2*x)), x) == pi
assert periodicity(sin(2*x)*exp(tan(x) - csc(2*x)), x) == pi
assert periodicity(cos(sec(x) - csc(2*x)), x) == 2*pi
assert periodicity(tan(sin(2*x)), x) == pi
assert periodicity(2*tan(x)**2, x) == pi
assert periodicity(sin(x%4), x) == 4
assert periodicity(sin(x)%4, x) == 2*pi
assert periodicity(tan((3*x-2)%4), x) == Rational(4, 3)
assert periodicity((sqrt(2)*(x+1)+x) % 3, x) == 3 / (sqrt(2)+1)
assert periodicity((x**2+1) % x, x) is None
assert periodicity(sin(re(x)), x) == 2*pi
assert periodicity(sin(x)**2 + cos(x)**2, x) is S.Zero
assert periodicity(tan(x), y) is S.Zero
assert periodicity(sin(x) + I*cos(x), x) == 2*pi
assert periodicity(x - sin(2*y), y) == pi
assert periodicity(exp(x), x) is None
assert periodicity(exp(I*x), x) == 2*pi
assert periodicity(exp(I*a), a) == 2*pi
assert periodicity(exp(a), a) is None
assert periodicity(exp(log(sin(a) + I*cos(2*a)), evaluate=False), a) == 2*pi
assert periodicity(exp(log(sin(2*a) + I*cos(a)), evaluate=False), a) == 2*pi
assert periodicity(exp(sin(a)), a) == 2*pi
assert periodicity(exp(2*I*a), a) == pi
assert periodicity(exp(a + I*sin(a)), a) is None
assert periodicity(exp(cos(a/2) + sin(a)), a) == 4*pi
assert periodicity(log(x), x) is None
assert periodicity(exp(x)**sin(x), x) is None
assert periodicity(sin(x)**y, y) is None
assert periodicity(Abs(sin(Abs(sin(x)))), x) == pi
assert all(periodicity(Abs(f(x)), x) == pi for f in (
cos, sin, sec, csc, tan, cot))
assert periodicity(Abs(sin(tan(x))), x) == pi
assert periodicity(Abs(sin(sin(x) + tan(x))), x) == 2*pi
assert periodicity(sin(x) > S.Half, x) == 2*pi
assert periodicity(x > 2, x) is None
assert periodicity(x**3 - x**2 + 1, x) is None
assert periodicity(Abs(x), x) is None
assert periodicity(Abs(x**2 - 1), x) is None
assert periodicity((x**2 + 4)%2, x) is None
assert periodicity((E**x)%3, x) is None
assert periodicity(sin(expint(1, x))/expint(1, x), x) is None
# returning `None` for any Piecewise
p = Piecewise((0, x < -1), (x**2, x <= 1), (log(x), True))
assert periodicity(p, x) is None
m = MatrixSymbol('m', 3, 3)
raises(NotImplementedError, lambda: periodicity(sin(m), m))
raises(NotImplementedError, lambda: periodicity(sin(m[0, 0]), m))
raises(NotImplementedError, lambda: periodicity(sin(m), m[0, 0]))
raises(NotImplementedError, lambda: periodicity(sin(m[0, 0]), m[0, 0]))
def test_periodicity_check():
assert periodicity(tan(x), x, check=True) == pi
assert periodicity(sin(x) + cos(x), x, check=True) == 2*pi
assert periodicity(sec(x), x) == 2*pi
assert periodicity(sin(x*y), x) == 2*pi/abs(y)
assert periodicity(Abs(sec(sec(x))), x) == pi
def test_lcim():
assert lcim([S.Half, S(2), S(3)]) == 6
assert lcim([pi/2, pi/4, pi]) == pi
assert lcim([2*pi, pi/2]) == 2*pi
assert lcim([S.One, 2*pi]) is None
assert lcim([S(2) + 2*E, E/3 + Rational(1, 3), S.One + E]) == S(2) + 2*E
def test_is_convex():
assert is_convex(1/x, x, domain=Interval.open(0, oo)) == True
assert is_convex(1/x, x, domain=Interval(-oo, 0)) == False
assert is_convex(x**2, x, domain=Interval(0, oo)) == True
assert is_convex(1/x**3, x, domain=Interval.Lopen(0, oo)) == True
assert is_convex(-1/x**3, x, domain=Interval.Ropen(-oo, 0)) == True
assert is_convex(log(x) ,x) == False
assert is_convex(x**2+y**2, x, y) == True
assert is_convex(cos(x) + cos(y), x) == False
assert is_convex(8*x**2 - 2*y**2, x, y) == False
def test_stationary_points():
assert stationary_points(sin(x), x, Interval(-pi/2, pi/2)
) == {-pi/2, pi/2}
assert stationary_points(sin(x), x, Interval.Ropen(0, pi/4)
) is S.EmptySet
assert stationary_points(tan(x), x,
) is S.EmptySet
assert stationary_points(sin(x)*cos(x), x, Interval(0, pi)
) == {pi/4, pi*Rational(3, 4)}
assert stationary_points(sec(x), x, Interval(0, pi)
) == {0, pi}
assert stationary_points((x+3)*(x-2), x
) == FiniteSet(Rational(-1, 2))
assert stationary_points((x + 3)/(x - 2), x, Interval(-5, 5)
) is S.EmptySet
assert stationary_points((x**2+3)/(x-2), x
) == {2 - sqrt(7), 2 + sqrt(7)}
assert stationary_points((x**2+3)/(x-2), x, Interval(0, 5)
) == {2 + sqrt(7)}
assert stationary_points(x**4 + x**3 - 5*x**2, x, S.Reals
) == FiniteSet(-2, 0, Rational(5, 4))
assert stationary_points(exp(x), x
) is S.EmptySet
assert stationary_points(log(x) - x, x, S.Reals
) == {1}
assert stationary_points(cos(x), x, Union(Interval(0, 5), Interval(-6, -3))
) == {0, -pi, pi}
assert stationary_points(y, x, S.Reals
) == S.Reals
assert stationary_points(y, x, S.EmptySet) == S.EmptySet
def test_maximum():
assert maximum(sin(x), x) is S.One
assert maximum(sin(x), x, Interval(0, 1)) == sin(1)
assert maximum(tan(x), x) is oo
assert maximum(tan(x), x, Interval(-pi/4, pi/4)) is S.One
assert maximum(sin(x)*cos(x), x, S.Reals) == S.Half
assert simplify(maximum(sin(x)*cos(x), x, Interval(pi*Rational(3, 8), pi*Rational(5, 8)))
) == sqrt(2)/4
assert maximum((x+3)*(x-2), x) is oo
assert maximum((x+3)*(x-2), x, Interval(-5, 0)) == S(14)
assert maximum((x+3)/(x-2), x, Interval(-5, 0)) == Rational(2, 7)
assert simplify(maximum(-x**4-x**3+x**2+10, x)
) == 41*sqrt(41)/512 + Rational(5419, 512)
assert maximum(exp(x), x, Interval(-oo, 2)) == exp(2)
assert maximum(log(x) - x, x, S.Reals) is S.NegativeOne
assert maximum(cos(x), x, Union(Interval(0, 5), Interval(-6, -3))
) is S.One
assert maximum(cos(x)-sin(x), x, S.Reals) == sqrt(2)
assert maximum(y, x, S.Reals) == y
assert maximum(abs(a**3 + a), a, Interval(0, 2)) == 10
assert maximum(abs(60*a**3 + 24*a), a, Interval(0, 2)) == 528
assert maximum(abs(12*a*(5*a**2 + 2)), a, Interval(0, 2)) == 528
assert maximum(x/sqrt(x**2+1), x, S.Reals) == 1
raises(ValueError, lambda : maximum(sin(x), x, S.EmptySet))
raises(ValueError, lambda : maximum(log(cos(x)), x, S.EmptySet))
raises(ValueError, lambda : maximum(1/(x**2 + y**2 + 1), x, S.EmptySet))
raises(ValueError, lambda : maximum(sin(x), sin(x)))
raises(ValueError, lambda : maximum(sin(x), x*y, S.EmptySet))
raises(ValueError, lambda : maximum(sin(x), S.One))
def test_minimum():
assert minimum(sin(x), x) is S.NegativeOne
assert minimum(sin(x), x, Interval(1, 4)) == sin(4)
assert minimum(tan(x), x) is -oo
assert minimum(tan(x), x, Interval(-pi/4, pi/4)) is S.NegativeOne
assert minimum(sin(x)*cos(x), x, S.Reals) == Rational(-1, 2)
assert simplify(minimum(sin(x)*cos(x), x, Interval(pi*Rational(3, 8), pi*Rational(5, 8)))
) == -sqrt(2)/4
assert minimum((x+3)*(x-2), x) == Rational(-25, 4)
assert minimum((x+3)/(x-2), x, Interval(-5, 0)) == Rational(-3, 2)
assert minimum(x**4-x**3+x**2+10, x) == S(10)
assert minimum(exp(x), x, Interval(-2, oo)) == exp(-2)
assert minimum(log(x) - x, x, S.Reals) is -oo
assert minimum(cos(x), x, Union(Interval(0, 5), Interval(-6, -3))
) is S.NegativeOne
assert minimum(cos(x)-sin(x), x, S.Reals) == -sqrt(2)
assert minimum(y, x, S.Reals) == y
assert minimum(x/sqrt(x**2+1), x, S.Reals) == -1
raises(ValueError, lambda : minimum(sin(x), x, S.EmptySet))
raises(ValueError, lambda : minimum(log(cos(x)), x, S.EmptySet))
raises(ValueError, lambda : minimum(1/(x**2 + y**2 + 1), x, S.EmptySet))
raises(ValueError, lambda : minimum(sin(x), sin(x)))
raises(ValueError, lambda : minimum(sin(x), x*y, S.EmptySet))
raises(ValueError, lambda : minimum(sin(x), S.One))
def test_issue_19869():
assert (maximum(sqrt(3)*(x - 1)/(3*sqrt(x**2 + 1)), x)
) == sqrt(3)/3
def test_issue_16469():
f = abs(a)
assert function_range(f, a, S.Reals) == Interval(0, oo, False, True)
@_both_exp_pow
def test_issue_18747():
assert periodicity(exp(pi*I*(x/4 + S.Half/2)), x) == 8
def test_issue_25942():
assert (acos(x) > pi/3).as_set() == Interval.Ropen(-1, S(1)/2)

View File

@ -0,0 +1,905 @@
from .accumulationbounds import AccumBounds, AccumulationBounds # noqa: F401
from .singularities import singularities
from sympy.core import Pow, S
from sympy.core.function import diff, expand_mul, Function
from sympy.core.kind import NumberKind
from sympy.core.mod import Mod
from sympy.core.numbers import equal_valued
from sympy.core.relational import Relational
from sympy.core.symbol import Symbol, Dummy
from sympy.core.sympify import _sympify
from sympy.functions.elementary.complexes import Abs, im, re
from sympy.functions.elementary.exponential import exp, log
from sympy.functions.elementary.integers import frac
from sympy.functions.elementary.piecewise import Piecewise
from sympy.functions.elementary.trigonometric import (
TrigonometricFunction, sin, cos, tan, cot, csc, sec,
asin, acos, acot, atan, asec, acsc)
from sympy.functions.elementary.hyperbolic import (sinh, cosh, tanh, coth,
sech, csch, asinh, acosh, atanh, acoth, asech, acsch)
from sympy.polys.polytools import degree, lcm_list
from sympy.sets.sets import (Interval, Intersection, FiniteSet, Union,
Complement)
from sympy.sets.fancysets import ImageSet
from sympy.sets.conditionset import ConditionSet
from sympy.utilities import filldedent
from sympy.utilities.iterables import iterable
from sympy.matrices.dense import hessian
def continuous_domain(f, symbol, domain):
"""
Returns the domain on which the function expression f is continuous.
This function is limited by the ability to determine the various
singularities and discontinuities of the given function.
The result is either given as a union of intervals or constructed using
other set operations.
Parameters
==========
f : :py:class:`~.Expr`
The concerned function.
symbol : :py:class:`~.Symbol`
The variable for which the intervals are to be determined.
domain : :py:class:`~.Interval`
The domain over which the continuity of the symbol has to be checked.
Examples
========
>>> from sympy import Interval, Symbol, S, tan, log, pi, sqrt
>>> from sympy.calculus.util import continuous_domain
>>> x = Symbol('x')
>>> continuous_domain(1/x, x, S.Reals)
Union(Interval.open(-oo, 0), Interval.open(0, oo))
>>> continuous_domain(tan(x), x, Interval(0, pi))
Union(Interval.Ropen(0, pi/2), Interval.Lopen(pi/2, pi))
>>> continuous_domain(sqrt(x - 2), x, Interval(-5, 5))
Interval(2, 5)
>>> continuous_domain(log(2*x - 1), x, S.Reals)
Interval.open(1/2, oo)
Returns
=======
:py:class:`~.Interval`
Union of all intervals where the function is continuous.
Raises
======
NotImplementedError
If the method to determine continuity of such a function
has not yet been developed.
"""
from sympy.solvers.inequalities import solve_univariate_inequality
if not domain.is_subset(S.Reals):
raise NotImplementedError(filldedent('''
Domain must be a subset of S.Reals.
'''))
implemented = [Pow, exp, log, Abs, frac,
sin, cos, tan, cot, sec, csc,
asin, acos, atan, acot, asec, acsc,
sinh, cosh, tanh, coth, sech, csch,
asinh, acosh, atanh, acoth, asech, acsch]
used = [fct.func for fct in f.atoms(Function) if fct.has(symbol)]
if any(func not in implemented for func in used):
raise NotImplementedError(filldedent('''
Unable to determine the domain of the given function.
'''))
x = Symbol('x')
constraints = {
log: (x > 0,),
asin: (x >= -1, x <= 1),
acos: (x >= -1, x <= 1),
acosh: (x >= 1,),
atanh: (x > -1, x < 1),
asech: (x > 0, x <= 1)
}
constraints_union = {
asec: (x <= -1, x >= 1),
acsc: (x <= -1, x >= 1),
acoth: (x < -1, x > 1)
}
cont_domain = domain
for atom in f.atoms(Pow):
den = atom.exp.as_numer_denom()[1]
if atom.exp.is_rational and den.is_odd:
pass # 0**negative handled by singularities()
else:
constraint = solve_univariate_inequality(atom.base >= 0,
symbol).as_set()
cont_domain = Intersection(constraint, cont_domain)
for atom in f.atoms(Function):
if atom.func in constraints:
for c in constraints[atom.func]:
constraint_relational = c.subs(x, atom.args[0])
constraint_set = solve_univariate_inequality(
constraint_relational, symbol).as_set()
cont_domain = Intersection(constraint_set, cont_domain)
elif atom.func in constraints_union:
constraint_set = S.EmptySet
for c in constraints_union[atom.func]:
constraint_relational = c.subs(x, atom.args[0])
constraint_set += solve_univariate_inequality(
constraint_relational, symbol).as_set()
cont_domain = Intersection(constraint_set, cont_domain)
# XXX: the discontinuities below could be factored out in
# a new "discontinuities()".
elif atom.func == acot:
from sympy.solvers.solveset import solveset_real
# Sympy's acot() has a step discontinuity at 0. Since it's
# neither an essential singularity nor a pole, singularities()
# will not report it. But it's still relevant for determining
# the continuity of the function f.
cont_domain -= solveset_real(atom.args[0], symbol)
# Note that the above may introduce spurious discontinuities, e.g.
# for abs(acot(x)) at 0.
elif atom.func == frac:
from sympy.solvers.solveset import solveset_real
r = function_range(atom.args[0], symbol, domain)
r = Intersection(r, S.Integers)
if r.is_finite_set:
discont = S.EmptySet
for n in r:
discont += solveset_real(atom.args[0]-n, symbol)
else:
discont = ConditionSet(
symbol, S.Integers.contains(atom.args[0]), cont_domain)
cont_domain -= discont
return cont_domain - singularities(f, symbol, domain)
def function_range(f, symbol, domain):
"""
Finds the range of a function in a given domain.
This method is limited by the ability to determine the singularities and
determine limits.
Parameters
==========
f : :py:class:`~.Expr`
The concerned function.
symbol : :py:class:`~.Symbol`
The variable for which the range of function is to be determined.
domain : :py:class:`~.Interval`
The domain under which the range of the function has to be found.
Examples
========
>>> from sympy import Interval, Symbol, S, exp, log, pi, sqrt, sin, tan
>>> from sympy.calculus.util import function_range
>>> x = Symbol('x')
>>> function_range(sin(x), x, Interval(0, 2*pi))
Interval(-1, 1)
>>> function_range(tan(x), x, Interval(-pi/2, pi/2))
Interval(-oo, oo)
>>> function_range(1/x, x, S.Reals)
Union(Interval.open(-oo, 0), Interval.open(0, oo))
>>> function_range(exp(x), x, S.Reals)
Interval.open(0, oo)
>>> function_range(log(x), x, S.Reals)
Interval(-oo, oo)
>>> function_range(sqrt(x), x, Interval(-5, 9))
Interval(0, 3)
Returns
=======
:py:class:`~.Interval`
Union of all ranges for all intervals under domain where function is
continuous.
Raises
======
NotImplementedError
If any of the intervals, in the given domain, for which function
is continuous are not finite or real,
OR if the critical points of the function on the domain cannot be found.
"""
if domain is S.EmptySet:
return S.EmptySet
period = periodicity(f, symbol)
if period == S.Zero:
# the expression is constant wrt symbol
return FiniteSet(f.expand())
from sympy.series.limits import limit
from sympy.solvers.solveset import solveset
if period is not None:
if isinstance(domain, Interval):
if (domain.inf - domain.sup).is_infinite:
domain = Interval(0, period)
elif isinstance(domain, Union):
for sub_dom in domain.args:
if isinstance(sub_dom, Interval) and \
((sub_dom.inf - sub_dom.sup).is_infinite):
domain = Interval(0, period)
intervals = continuous_domain(f, symbol, domain)
range_int = S.EmptySet
if isinstance(intervals,(Interval, FiniteSet)):
interval_iter = (intervals,)
elif isinstance(intervals, Union):
interval_iter = intervals.args
else:
raise NotImplementedError(filldedent('''
Unable to find range for the given domain.
'''))
for interval in interval_iter:
if isinstance(interval, FiniteSet):
for singleton in interval:
if singleton in domain:
range_int += FiniteSet(f.subs(symbol, singleton))
elif isinstance(interval, Interval):
vals = S.EmptySet
critical_points = S.EmptySet
critical_values = S.EmptySet
bounds = ((interval.left_open, interval.inf, '+'),
(interval.right_open, interval.sup, '-'))
for is_open, limit_point, direction in bounds:
if is_open:
critical_values += FiniteSet(limit(f, symbol, limit_point, direction))
vals += critical_values
else:
vals += FiniteSet(f.subs(symbol, limit_point))
solution = solveset(f.diff(symbol), symbol, interval)
if not iterable(solution):
raise NotImplementedError(
'Unable to find critical points for {}'.format(f))
if isinstance(solution, ImageSet):
raise NotImplementedError(
'Infinite number of critical points for {}'.format(f))
critical_points += solution
for critical_point in critical_points:
vals += FiniteSet(f.subs(symbol, critical_point))
left_open, right_open = False, False
if critical_values is not S.EmptySet:
if critical_values.inf == vals.inf:
left_open = True
if critical_values.sup == vals.sup:
right_open = True
range_int += Interval(vals.inf, vals.sup, left_open, right_open)
else:
raise NotImplementedError(filldedent('''
Unable to find range for the given domain.
'''))
return range_int
def not_empty_in(finset_intersection, *syms):
"""
Finds the domain of the functions in ``finset_intersection`` in which the
``finite_set`` is not-empty.
Parameters
==========
finset_intersection : Intersection of FiniteSet
The unevaluated intersection of FiniteSet containing
real-valued functions with Union of Sets
syms : Tuple of symbols
Symbol for which domain is to be found
Raises
======
NotImplementedError
The algorithms to find the non-emptiness of the given FiniteSet are
not yet implemented.
ValueError
The input is not valid.
RuntimeError
It is a bug, please report it to the github issue tracker
(https://github.com/sympy/sympy/issues).
Examples
========
>>> from sympy import FiniteSet, Interval, not_empty_in, oo
>>> from sympy.abc import x
>>> not_empty_in(FiniteSet(x/2).intersect(Interval(0, 1)), x)
Interval(0, 2)
>>> not_empty_in(FiniteSet(x, x**2).intersect(Interval(1, 2)), x)
Union(Interval(1, 2), Interval(-sqrt(2), -1))
>>> not_empty_in(FiniteSet(x**2/(x + 2)).intersect(Interval(1, oo)), x)
Union(Interval.Lopen(-2, -1), Interval(2, oo))
"""
# TODO: handle piecewise defined functions
# TODO: handle transcendental functions
# TODO: handle multivariate functions
if len(syms) == 0:
raise ValueError("One or more symbols must be given in syms.")
if finset_intersection is S.EmptySet:
return S.EmptySet
if isinstance(finset_intersection, Union):
elm_in_sets = finset_intersection.args[0]
return Union(not_empty_in(finset_intersection.args[1], *syms),
elm_in_sets)
if isinstance(finset_intersection, FiniteSet):
finite_set = finset_intersection
_sets = S.Reals
else:
finite_set = finset_intersection.args[1]
_sets = finset_intersection.args[0]
if not isinstance(finite_set, FiniteSet):
raise ValueError('A FiniteSet must be given, not %s: %s' %
(type(finite_set), finite_set))
if len(syms) == 1:
symb = syms[0]
else:
raise NotImplementedError('more than one variables %s not handled' %
(syms,))
def elm_domain(expr, intrvl):
""" Finds the domain of an expression in any given interval """
from sympy.solvers.solveset import solveset
_start = intrvl.start
_end = intrvl.end
_singularities = solveset(expr.as_numer_denom()[1], symb,
domain=S.Reals)
if intrvl.right_open:
if _end is S.Infinity:
_domain1 = S.Reals
else:
_domain1 = solveset(expr < _end, symb, domain=S.Reals)
else:
_domain1 = solveset(expr <= _end, symb, domain=S.Reals)
if intrvl.left_open:
if _start is S.NegativeInfinity:
_domain2 = S.Reals
else:
_domain2 = solveset(expr > _start, symb, domain=S.Reals)
else:
_domain2 = solveset(expr >= _start, symb, domain=S.Reals)
# domain in the interval
expr_with_sing = Intersection(_domain1, _domain2)
expr_domain = Complement(expr_with_sing, _singularities)
return expr_domain
if isinstance(_sets, Interval):
return Union(*[elm_domain(element, _sets) for element in finite_set])
if isinstance(_sets, Union):
_domain = S.EmptySet
for intrvl in _sets.args:
_domain_element = Union(*[elm_domain(element, intrvl)
for element in finite_set])
_domain = Union(_domain, _domain_element)
return _domain
def periodicity(f, symbol, check=False):
"""
Tests the given function for periodicity in the given symbol.
Parameters
==========
f : :py:class:`~.Expr`
The concerned function.
symbol : :py:class:`~.Symbol`
The variable for which the period is to be determined.
check : bool, optional
The flag to verify whether the value being returned is a period or not.
Returns
=======
period
The period of the function is returned.
``None`` is returned when the function is aperiodic or has a complex period.
The value of $0$ is returned as the period of a constant function.
Raises
======
NotImplementedError
The value of the period computed cannot be verified.
Notes
=====
Currently, we do not support functions with a complex period.
The period of functions having complex periodic values such
as ``exp``, ``sinh`` is evaluated to ``None``.
The value returned might not be the "fundamental" period of the given
function i.e. it may not be the smallest periodic value of the function.
The verification of the period through the ``check`` flag is not reliable
due to internal simplification of the given expression. Hence, it is set
to ``False`` by default.
Examples
========
>>> from sympy import periodicity, Symbol, sin, cos, tan, exp
>>> x = Symbol('x')
>>> f = sin(x) + sin(2*x) + sin(3*x)
>>> periodicity(f, x)
2*pi
>>> periodicity(sin(x)*cos(x), x)
pi
>>> periodicity(exp(tan(2*x) - 1), x)
pi/2
>>> periodicity(sin(4*x)**cos(2*x), x)
pi
>>> periodicity(exp(x), x)
"""
if symbol.kind is not NumberKind:
raise NotImplementedError("Cannot use symbol of kind %s" % symbol.kind)
temp = Dummy('x', real=True)
f = f.subs(symbol, temp)
symbol = temp
def _check(orig_f, period):
'''Return the checked period or raise an error.'''
new_f = orig_f.subs(symbol, symbol + period)
if new_f.equals(orig_f):
return period
else:
raise NotImplementedError(filldedent('''
The period of the given function cannot be verified.
When `%s` was replaced with `%s + %s` in `%s`, the result
was `%s` which was not recognized as being the same as
the original function.
So either the period was wrong or the two forms were
not recognized as being equal.
Set check=False to obtain the value.''' %
(symbol, symbol, period, orig_f, new_f)))
orig_f = f
period = None
if isinstance(f, Relational):
f = f.lhs - f.rhs
f = f.simplify()
if symbol not in f.free_symbols:
return S.Zero
if isinstance(f, TrigonometricFunction):
try:
period = f.period(symbol)
except NotImplementedError:
pass
if isinstance(f, Abs):
arg = f.args[0]
if isinstance(arg, (sec, csc, cos)):
# all but tan and cot might have a
# a period that is half as large
# so recast as sin
arg = sin(arg.args[0])
period = periodicity(arg, symbol)
if period is not None and isinstance(arg, sin):
# the argument of Abs was a trigonometric other than
# cot or tan; test to see if the half-period
# is valid. Abs(arg) has behaviour equivalent to
# orig_f, so use that for test:
orig_f = Abs(arg)
try:
return _check(orig_f, period/2)
except NotImplementedError as err:
if check:
raise NotImplementedError(err)
# else let new orig_f and period be
# checked below
if isinstance(f, exp) or (f.is_Pow and f.base == S.Exp1):
f = Pow(S.Exp1, expand_mul(f.exp))
if im(f) != 0:
period_real = periodicity(re(f), symbol)
period_imag = periodicity(im(f), symbol)
if period_real is not None and period_imag is not None:
period = lcim([period_real, period_imag])
if f.is_Pow and f.base != S.Exp1:
base, expo = f.args
base_has_sym = base.has(symbol)
expo_has_sym = expo.has(symbol)
if base_has_sym and not expo_has_sym:
period = periodicity(base, symbol)
elif expo_has_sym and not base_has_sym:
period = periodicity(expo, symbol)
else:
period = _periodicity(f.args, symbol)
elif f.is_Mul:
coeff, g = f.as_independent(symbol, as_Add=False)
if isinstance(g, TrigonometricFunction) or not equal_valued(coeff, 1):
period = periodicity(g, symbol)
else:
period = _periodicity(g.args, symbol)
elif f.is_Add:
k, g = f.as_independent(symbol)
if k is not S.Zero:
return periodicity(g, symbol)
period = _periodicity(g.args, symbol)
elif isinstance(f, Mod):
a, n = f.args
if a == symbol:
period = n
elif isinstance(a, TrigonometricFunction):
period = periodicity(a, symbol)
#check if 'f' is linear in 'symbol'
elif (a.is_polynomial(symbol) and degree(a, symbol) == 1 and
symbol not in n.free_symbols):
period = Abs(n / a.diff(symbol))
elif isinstance(f, Piecewise):
pass # not handling Piecewise yet as the return type is not favorable
elif period is None:
from sympy.solvers.decompogen import compogen, decompogen
g_s = decompogen(f, symbol)
num_of_gs = len(g_s)
if num_of_gs > 1:
for index, g in enumerate(reversed(g_s)):
start_index = num_of_gs - 1 - index
g = compogen(g_s[start_index:], symbol)
if g not in (orig_f, f): # Fix for issue 12620
period = periodicity(g, symbol)
if period is not None:
break
if period is not None:
if check:
return _check(orig_f, period)
return period
return None
def _periodicity(args, symbol):
"""
Helper for `periodicity` to find the period of a list of simpler
functions.
It uses the `lcim` method to find the least common period of
all the functions.
Parameters
==========
args : Tuple of :py:class:`~.Symbol`
All the symbols present in a function.
symbol : :py:class:`~.Symbol`
The symbol over which the function is to be evaluated.
Returns
=======
period
The least common period of the function for all the symbols
of the function.
``None`` if for at least one of the symbols the function is aperiodic.
"""
periods = []
for f in args:
period = periodicity(f, symbol)
if period is None:
return None
if period is not S.Zero:
periods.append(period)
if len(periods) > 1:
return lcim(periods)
if periods:
return periods[0]
def lcim(numbers):
"""Returns the least common integral multiple of a list of numbers.
The numbers can be rational or irrational or a mixture of both.
`None` is returned for incommensurable numbers.
Parameters
==========
numbers : list
Numbers (rational and/or irrational) for which lcim is to be found.
Returns
=======
number
lcim if it exists, otherwise ``None`` for incommensurable numbers.
Examples
========
>>> from sympy.calculus.util import lcim
>>> from sympy import S, pi
>>> lcim([S(1)/2, S(3)/4, S(5)/6])
15/2
>>> lcim([2*pi, 3*pi, pi, pi/2])
6*pi
>>> lcim([S(1), 2*pi])
"""
result = None
if all(num.is_irrational for num in numbers):
factorized_nums = [num.factor() for num in numbers]
factors_num = [num.as_coeff_Mul() for num in factorized_nums]
term = factors_num[0][1]
if all(factor == term for coeff, factor in factors_num):
common_term = term
coeffs = [coeff for coeff, factor in factors_num]
result = lcm_list(coeffs) * common_term
elif all(num.is_rational for num in numbers):
result = lcm_list(numbers)
else:
pass
return result
def is_convex(f, *syms, domain=S.Reals):
r"""Determines the convexity of the function passed in the argument.
Parameters
==========
f : :py:class:`~.Expr`
The concerned function.
syms : Tuple of :py:class:`~.Symbol`
The variables with respect to which the convexity is to be determined.
domain : :py:class:`~.Interval`, optional
The domain over which the convexity of the function has to be checked.
If unspecified, S.Reals will be the default domain.
Returns
=======
bool
The method returns ``True`` if the function is convex otherwise it
returns ``False``.
Raises
======
NotImplementedError
The check for the convexity of multivariate functions is not implemented yet.
Notes
=====
To determine concavity of a function pass `-f` as the concerned function.
To determine logarithmic convexity of a function pass `\log(f)` as
concerned function.
To determine logarithmic concavity of a function pass `-\log(f)` as
concerned function.
Currently, convexity check of multivariate functions is not handled.
Examples
========
>>> from sympy import is_convex, symbols, exp, oo, Interval
>>> x = symbols('x')
>>> is_convex(exp(x), x)
True
>>> is_convex(x**3, x, domain = Interval(-1, oo))
False
>>> is_convex(1/x**2, x, domain=Interval.open(0, oo))
True
References
==========
.. [1] https://en.wikipedia.org/wiki/Convex_function
.. [2] http://www.ifp.illinois.edu/~angelia/L3_convfunc.pdf
.. [3] https://en.wikipedia.org/wiki/Logarithmically_convex_function
.. [4] https://en.wikipedia.org/wiki/Logarithmically_concave_function
.. [5] https://en.wikipedia.org/wiki/Concave_function
"""
if len(syms) > 1 :
return hessian(f, syms).is_positive_semidefinite
from sympy.solvers.inequalities import solve_univariate_inequality
f = _sympify(f)
var = syms[0]
if any(s in domain for s in singularities(f, var)):
return False
condition = f.diff(var, 2) < 0
if solve_univariate_inequality(condition, var, False, domain):
return False
return True
def stationary_points(f, symbol, domain=S.Reals):
"""
Returns the stationary points of a function (where derivative of the
function is 0) in the given domain.
Parameters
==========
f : :py:class:`~.Expr`
The concerned function.
symbol : :py:class:`~.Symbol`
The variable for which the stationary points are to be determined.
domain : :py:class:`~.Interval`
The domain over which the stationary points have to be checked.
If unspecified, ``S.Reals`` will be the default domain.
Returns
=======
Set
A set of stationary points for the function. If there are no
stationary point, an :py:class:`~.EmptySet` is returned.
Examples
========
>>> from sympy import Interval, Symbol, S, sin, pi, pprint, stationary_points
>>> x = Symbol('x')
>>> stationary_points(1/x, x, S.Reals)
EmptySet
>>> pprint(stationary_points(sin(x), x), use_unicode=False)
pi 3*pi
{2*n*pi + -- | n in Integers} U {2*n*pi + ---- | n in Integers}
2 2
>>> stationary_points(sin(x),x, Interval(0, 4*pi))
{pi/2, 3*pi/2, 5*pi/2, 7*pi/2}
"""
from sympy.solvers.solveset import solveset
if domain is S.EmptySet:
return S.EmptySet
domain = continuous_domain(f, symbol, domain)
set = solveset(diff(f, symbol), symbol, domain)
return set
def maximum(f, symbol, domain=S.Reals):
"""
Returns the maximum value of a function in the given domain.
Parameters
==========
f : :py:class:`~.Expr`
The concerned function.
symbol : :py:class:`~.Symbol`
The variable for maximum value needs to be determined.
domain : :py:class:`~.Interval`
The domain over which the maximum have to be checked.
If unspecified, then the global maximum is returned.
Returns
=======
number
Maximum value of the function in given domain.
Examples
========
>>> from sympy import Interval, Symbol, S, sin, cos, pi, maximum
>>> x = Symbol('x')
>>> f = -x**2 + 2*x + 5
>>> maximum(f, x, S.Reals)
6
>>> maximum(sin(x), x, Interval(-pi, pi/4))
sqrt(2)/2
>>> maximum(sin(x)*cos(x), x)
1/2
"""
if isinstance(symbol, Symbol):
if domain is S.EmptySet:
raise ValueError("Maximum value not defined for empty domain.")
return function_range(f, symbol, domain).sup
else:
raise ValueError("%s is not a valid symbol." % symbol)
def minimum(f, symbol, domain=S.Reals):
"""
Returns the minimum value of a function in the given domain.
Parameters
==========
f : :py:class:`~.Expr`
The concerned function.
symbol : :py:class:`~.Symbol`
The variable for minimum value needs to be determined.
domain : :py:class:`~.Interval`
The domain over which the minimum have to be checked.
If unspecified, then the global minimum is returned.
Returns
=======
number
Minimum value of the function in the given domain.
Examples
========
>>> from sympy import Interval, Symbol, S, sin, cos, minimum
>>> x = Symbol('x')
>>> f = x**2 + 2*x + 5
>>> minimum(f, x, S.Reals)
4
>>> minimum(sin(x), x, Interval(2, 3))
sin(3)
>>> minimum(sin(x)*cos(x), x)
-1/2
"""
if isinstance(symbol, Symbol):
if domain is S.EmptySet:
raise ValueError("Minimum value not defined for empty domain.")
return function_range(f, symbol, domain).inf
else:
raise ValueError("%s is not a valid symbol." % symbol)