chore: 添加虚拟环境到仓库
- 添加 backend_service/venv 虚拟环境 - 包含所有Python依赖包 - 注意:虚拟环境约393MB,包含12655个文件
This commit is contained in:
@@ -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'
|
||||
]
|
||||
@@ -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) -> Expr: # type: ignore
|
||||
|
||||
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
|
||||
@@ -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
|
||||
@@ -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]))
|
||||
@@ -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
|
||||
@@ -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))
|
||||
@@ -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)) == []
|
||||
@@ -0,0 +1,164 @@
|
||||
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
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -0,0 +1,895 @@
|
||||
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("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_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))
|
||||
|
||||
critical_points = solveset(f.diff(symbol), symbol, interval)
|
||||
|
||||
if not iterable(critical_points):
|
||||
raise NotImplementedError(
|
||||
'Unable to find critical points for {}'.format(f))
|
||||
if isinstance(critical_points, ImageSet):
|
||||
raise NotImplementedError(
|
||||
'Infinite number of critical points for {}'.format(f))
|
||||
|
||||
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("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)
|
||||
Reference in New Issue
Block a user