2022-06-24 17:14:37 +02:00

1795 lines
50 KiB
Python

from sympy.core.logic import FuzzyBool
from sympy.core import S, sympify, cacheit, pi, I, Rational
from sympy.core.add import Add
from sympy.core.function import Function, ArgumentIndexError, _coeff_isneg
from sympy.functions.combinatorial.factorials import factorial, RisingFactorial
from sympy.functions.elementary.exponential import exp, log, match_real_imag
from sympy.functions.elementary.miscellaneous import sqrt
from sympy.functions.elementary.integers import floor
from sympy.core.logic import fuzzy_or, fuzzy_and
def _rewrite_hyperbolics_as_exp(expr):
expr = sympify(expr)
return expr.xreplace({h: h.rewrite(exp)
for h in expr.atoms(HyperbolicFunction)})
###############################################################################
########################### HYPERBOLIC FUNCTIONS ##############################
###############################################################################
class HyperbolicFunction(Function):
"""
Base class for hyperbolic functions.
See Also
========
sinh, cosh, tanh, coth
"""
unbranched = True
def _peeloff_ipi(arg):
"""
Split ARG into two parts, a "rest" and a multiple of I*pi/2.
This assumes ARG to be an Add.
The multiple of I*pi returned in the second position is always a Rational.
Examples
========
>>> from sympy.functions.elementary.hyperbolic import _peeloff_ipi as peel
>>> from sympy import pi, I
>>> from sympy.abc import x, y
>>> peel(x + I*pi/2)
(x, I*pi/2)
>>> peel(x + I*2*pi/3 + I*pi*y)
(x + I*pi*y + I*pi/6, I*pi/2)
"""
for a in Add.make_args(arg):
if a == S.Pi*S.ImaginaryUnit:
K = S.One
break
elif a.is_Mul:
K, p = a.as_two_terms()
if p == S.Pi*S.ImaginaryUnit and K.is_Rational:
break
else:
return arg, S.Zero
m1 = (K % S.Half)*S.Pi*S.ImaginaryUnit
m2 = K*S.Pi*S.ImaginaryUnit - m1
return arg - m2, m2
class sinh(HyperbolicFunction):
r"""
sinh(x) is the hyperbolic sine of x.
The hyperbolic sine function is $\frac{e^x - e^{-x}}{2}$.
Examples
========
>>> from sympy import sinh
>>> from sympy.abc import x
>>> sinh(x)
sinh(x)
See Also
========
cosh, tanh, asinh
"""
def fdiff(self, argindex=1):
"""
Returns the first derivative of this function.
"""
if argindex == 1:
return cosh(self.args[0])
else:
raise ArgumentIndexError(self, argindex)
def inverse(self, argindex=1):
"""
Returns the inverse of this function.
"""
return asinh
@classmethod
def eval(cls, arg):
from sympy import sin
arg = sympify(arg)
if arg.is_Number:
if arg is S.NaN:
return S.NaN
elif arg is S.Infinity:
return S.Infinity
elif arg is S.NegativeInfinity:
return S.NegativeInfinity
elif arg.is_zero:
return S.Zero
elif arg.is_negative:
return -cls(-arg)
else:
if arg is S.ComplexInfinity:
return S.NaN
i_coeff = arg.as_coefficient(S.ImaginaryUnit)
if i_coeff is not None:
return S.ImaginaryUnit * sin(i_coeff)
else:
if _coeff_isneg(arg):
return -cls(-arg)
if arg.is_Add:
x, m = _peeloff_ipi(arg)
if m:
return sinh(m)*cosh(x) + cosh(m)*sinh(x)
if arg.is_zero:
return S.Zero
if arg.func == asinh:
return arg.args[0]
if arg.func == acosh:
x = arg.args[0]
return sqrt(x - 1) * sqrt(x + 1)
if arg.func == atanh:
x = arg.args[0]
return x/sqrt(1 - x**2)
if arg.func == acoth:
x = arg.args[0]
return 1/(sqrt(x - 1) * sqrt(x + 1))
@staticmethod
@cacheit
def taylor_term(n, x, *previous_terms):
"""
Returns the next term in the Taylor series expansion.
"""
if n < 0 or n % 2 == 0:
return S.Zero
else:
x = sympify(x)
if len(previous_terms) > 2:
p = previous_terms[-2]
return p * x**2 / (n*(n - 1))
else:
return x**(n) / factorial(n)
def _eval_conjugate(self):
return self.func(self.args[0].conjugate())
def as_real_imag(self, deep=True, **hints):
"""
Returns this function as a complex coordinate.
"""
from sympy import cos, sin
if self.args[0].is_extended_real:
if deep:
hints['complex'] = False
return (self.expand(deep, **hints), S.Zero)
else:
return (self, S.Zero)
if deep:
re, im = self.args[0].expand(deep, **hints).as_real_imag()
else:
re, im = self.args[0].as_real_imag()
return (sinh(re)*cos(im), cosh(re)*sin(im))
def _eval_expand_complex(self, deep=True, **hints):
re_part, im_part = self.as_real_imag(deep=deep, **hints)
return re_part + im_part*S.ImaginaryUnit
def _eval_expand_trig(self, deep=True, **hints):
if deep:
arg = self.args[0].expand(deep, **hints)
else:
arg = self.args[0]
x = None
if arg.is_Add: # TODO, implement more if deep stuff here
x, y = arg.as_two_terms()
else:
coeff, terms = arg.as_coeff_Mul(rational=True)
if coeff is not S.One and coeff.is_Integer and terms is not S.One:
x = terms
y = (coeff - 1)*x
if x is not None:
return (sinh(x)*cosh(y) + sinh(y)*cosh(x)).expand(trig=True)
return sinh(arg)
def _eval_rewrite_as_tractable(self, arg, limitvar=None, **kwargs):
return (exp(arg) - exp(-arg)) / 2
def _eval_rewrite_as_exp(self, arg, **kwargs):
return (exp(arg) - exp(-arg)) / 2
def _eval_rewrite_as_cosh(self, arg, **kwargs):
return -S.ImaginaryUnit*cosh(arg + S.Pi*S.ImaginaryUnit/2)
def _eval_rewrite_as_tanh(self, arg, **kwargs):
tanh_half = tanh(S.Half*arg)
return 2*tanh_half/(1 - tanh_half**2)
def _eval_rewrite_as_coth(self, arg, **kwargs):
coth_half = coth(S.Half*arg)
return 2*coth_half/(coth_half**2 - 1)
def _eval_as_leading_term(self, x, logx=None, cdir=0):
arg = self.args[0].as_leading_term(x, logx=logx, cdir=cdir)
arg0 = arg.subs(x, 0)
if arg0 is S.NaN:
arg0 = arg.limit(x, 0, dir='-' if cdir.is_negative else '+')
if arg0.is_zero:
return arg
elif arg0.is_finite:
return self.func(arg0)
else:
return self
def _eval_is_real(self):
arg = self.args[0]
if arg.is_real:
return True
# if `im` is of the form n*pi
# else, check if it is a number
re, im = arg.as_real_imag()
return (im%pi).is_zero
def _eval_is_extended_real(self):
if self.args[0].is_extended_real:
return True
def _eval_is_positive(self):
if self.args[0].is_extended_real:
return self.args[0].is_positive
def _eval_is_negative(self):
if self.args[0].is_extended_real:
return self.args[0].is_negative
def _eval_is_finite(self):
arg = self.args[0]
return arg.is_finite
def _eval_is_zero(self):
arg = self.args[0]
if arg.is_zero:
return True
class cosh(HyperbolicFunction):
r"""
cosh(x) is the hyperbolic cosine of x.
The hyperbolic cosine function is $\frac{e^x + e^{-x}}{2}$.
Examples
========
>>> from sympy import cosh
>>> from sympy.abc import x
>>> cosh(x)
cosh(x)
See Also
========
sinh, tanh, acosh
"""
def fdiff(self, argindex=1):
if argindex == 1:
return sinh(self.args[0])
else:
raise ArgumentIndexError(self, argindex)
@classmethod
def eval(cls, arg):
from sympy import cos
arg = sympify(arg)
if arg.is_Number:
if arg is S.NaN:
return S.NaN
elif arg is S.Infinity:
return S.Infinity
elif arg is S.NegativeInfinity:
return S.Infinity
elif arg.is_zero:
return S.One
elif arg.is_negative:
return cls(-arg)
else:
if arg is S.ComplexInfinity:
return S.NaN
i_coeff = arg.as_coefficient(S.ImaginaryUnit)
if i_coeff is not None:
return cos(i_coeff)
else:
if _coeff_isneg(arg):
return cls(-arg)
if arg.is_Add:
x, m = _peeloff_ipi(arg)
if m:
return cosh(m)*cosh(x) + sinh(m)*sinh(x)
if arg.is_zero:
return S.One
if arg.func == asinh:
return sqrt(1 + arg.args[0]**2)
if arg.func == acosh:
return arg.args[0]
if arg.func == atanh:
return 1/sqrt(1 - arg.args[0]**2)
if arg.func == acoth:
x = arg.args[0]
return x/(sqrt(x - 1) * sqrt(x + 1))
@staticmethod
@cacheit
def taylor_term(n, x, *previous_terms):
if n < 0 or n % 2 == 1:
return S.Zero
else:
x = sympify(x)
if len(previous_terms) > 2:
p = previous_terms[-2]
return p * x**2 / (n*(n - 1))
else:
return x**(n)/factorial(n)
def _eval_conjugate(self):
return self.func(self.args[0].conjugate())
def as_real_imag(self, deep=True, **hints):
from sympy import cos, sin
if self.args[0].is_extended_real:
if deep:
hints['complex'] = False
return (self.expand(deep, **hints), S.Zero)
else:
return (self, S.Zero)
if deep:
re, im = self.args[0].expand(deep, **hints).as_real_imag()
else:
re, im = self.args[0].as_real_imag()
return (cosh(re)*cos(im), sinh(re)*sin(im))
def _eval_expand_complex(self, deep=True, **hints):
re_part, im_part = self.as_real_imag(deep=deep, **hints)
return re_part + im_part*S.ImaginaryUnit
def _eval_expand_trig(self, deep=True, **hints):
if deep:
arg = self.args[0].expand(deep, **hints)
else:
arg = self.args[0]
x = None
if arg.is_Add: # TODO, implement more if deep stuff here
x, y = arg.as_two_terms()
else:
coeff, terms = arg.as_coeff_Mul(rational=True)
if coeff is not S.One and coeff.is_Integer and terms is not S.One:
x = terms
y = (coeff - 1)*x
if x is not None:
return (cosh(x)*cosh(y) + sinh(x)*sinh(y)).expand(trig=True)
return cosh(arg)
def _eval_rewrite_as_tractable(self, arg, limitvar=None, **kwargs):
return (exp(arg) + exp(-arg)) / 2
def _eval_rewrite_as_exp(self, arg, **kwargs):
return (exp(arg) + exp(-arg)) / 2
def _eval_rewrite_as_sinh(self, arg, **kwargs):
return -S.ImaginaryUnit*sinh(arg + S.Pi*S.ImaginaryUnit/2)
def _eval_rewrite_as_tanh(self, arg, **kwargs):
tanh_half = tanh(S.Half*arg)**2
return (1 + tanh_half)/(1 - tanh_half)
def _eval_rewrite_as_coth(self, arg, **kwargs):
coth_half = coth(S.Half*arg)**2
return (coth_half + 1)/(coth_half - 1)
def _eval_as_leading_term(self, x, logx=None, cdir=0):
arg = self.args[0].as_leading_term(x, logx=logx, cdir=cdir)
arg0 = arg.subs(x, 0)
if arg0 is S.NaN:
arg0 = arg.limit(x, 0, dir='-' if cdir.is_negative else '+')
if arg0.is_zero:
return S.One
elif arg0.is_finite:
return self.func(arg0)
else:
return self
def _eval_is_real(self):
arg = self.args[0]
# `cosh(x)` is real for real OR purely imaginary `x`
if arg.is_real or arg.is_imaginary:
return True
# cosh(a+ib) = cos(b)*cosh(a) + i*sin(b)*sinh(a)
# the imaginary part can be an expression like n*pi
# if not, check if the imaginary part is a number
re, im = arg.as_real_imag()
return (im%pi).is_zero
def _eval_is_positive(self):
# cosh(x+I*y) = cos(y)*cosh(x) + I*sin(y)*sinh(x)
# cosh(z) is positive iff it is real and the real part is positive.
# So we need sin(y)*sinh(x) = 0 which gives x=0 or y=n*pi
# Case 1 (y=n*pi): cosh(z) = (-1)**n * cosh(x) -> positive for n even
# Case 2 (x=0): cosh(z) = cos(y) -> positive when cos(y) is positive
z = self.args[0]
x, y = z.as_real_imag()
ymod = y % (2*pi)
yzero = ymod.is_zero
# shortcut if ymod is zero
if yzero:
return True
xzero = x.is_zero
# shortcut x is not zero
if xzero is False:
return yzero
return fuzzy_or([
# Case 1:
yzero,
# Case 2:
fuzzy_and([
xzero,
fuzzy_or([ymod < pi/2, ymod > 3*pi/2])
])
])
def _eval_is_nonnegative(self):
z = self.args[0]
x, y = z.as_real_imag()
ymod = y % (2*pi)
yzero = ymod.is_zero
# shortcut if ymod is zero
if yzero:
return True
xzero = x.is_zero
# shortcut x is not zero
if xzero is False:
return yzero
return fuzzy_or([
# Case 1:
yzero,
# Case 2:
fuzzy_and([
xzero,
fuzzy_or([ymod <= pi/2, ymod >= 3*pi/2])
])
])
def _eval_is_finite(self):
arg = self.args[0]
return arg.is_finite
class tanh(HyperbolicFunction):
r"""
tanh(x) is the hyperbolic tangent of x.
The hyperbolic tangent function is $\frac{\sinh(x)}{\cosh(x)}$.
Examples
========
>>> from sympy import tanh
>>> from sympy.abc import x
>>> tanh(x)
tanh(x)
See Also
========
sinh, cosh, atanh
"""
def fdiff(self, argindex=1):
if argindex == 1:
return S.One - tanh(self.args[0])**2
else:
raise ArgumentIndexError(self, argindex)
def inverse(self, argindex=1):
"""
Returns the inverse of this function.
"""
return atanh
@classmethod
def eval(cls, arg):
from sympy import tan
arg = sympify(arg)
if arg.is_Number:
if arg is S.NaN:
return S.NaN
elif arg is S.Infinity:
return S.One
elif arg is S.NegativeInfinity:
return S.NegativeOne
elif arg.is_zero:
return S.Zero
elif arg.is_negative:
return -cls(-arg)
else:
if arg is S.ComplexInfinity:
return S.NaN
i_coeff = arg.as_coefficient(S.ImaginaryUnit)
if i_coeff is not None:
if _coeff_isneg(i_coeff):
return -S.ImaginaryUnit * tan(-i_coeff)
return S.ImaginaryUnit * tan(i_coeff)
else:
if _coeff_isneg(arg):
return -cls(-arg)
if arg.is_Add:
x, m = _peeloff_ipi(arg)
if m:
tanhm = tanh(m)
if tanhm is S.ComplexInfinity:
return coth(x)
else: # tanhm == 0
return tanh(x)
if arg.is_zero:
return S.Zero
if arg.func == asinh:
x = arg.args[0]
return x/sqrt(1 + x**2)
if arg.func == acosh:
x = arg.args[0]
return sqrt(x - 1) * sqrt(x + 1) / x
if arg.func == atanh:
return arg.args[0]
if arg.func == acoth:
return 1/arg.args[0]
@staticmethod
@cacheit
def taylor_term(n, x, *previous_terms):
from sympy import bernoulli
if n < 0 or n % 2 == 0:
return S.Zero
else:
x = sympify(x)
a = 2**(n + 1)
B = bernoulli(n + 1)
F = factorial(n + 1)
return a*(a - 1) * B/F * x**n
def _eval_conjugate(self):
return self.func(self.args[0].conjugate())
def as_real_imag(self, deep=True, **hints):
from sympy import cos, sin
if self.args[0].is_extended_real:
if deep:
hints['complex'] = False
return (self.expand(deep, **hints), S.Zero)
else:
return (self, S.Zero)
if deep:
re, im = self.args[0].expand(deep, **hints).as_real_imag()
else:
re, im = self.args[0].as_real_imag()
denom = sinh(re)**2 + cos(im)**2
return (sinh(re)*cosh(re)/denom, sin(im)*cos(im)/denom)
def _eval_expand_trig(self, **hints):
arg = self.args[0]
if arg.is_Add:
from sympy import symmetric_poly
n = len(arg.args)
TX = [tanh(x, evaluate=False)._eval_expand_trig()
for x in arg.args]
p = [0, 0] # [den, num]
for i in range(n + 1):
p[i % 2] += symmetric_poly(i, TX)
return p[1]/p[0]
elif arg.is_Mul:
from sympy.functions.combinatorial.numbers import nC
coeff, terms = arg.as_coeff_Mul()
if coeff.is_Integer and coeff > 1:
n = []
d = []
T = tanh(terms)
for k in range(1, coeff + 1, 2):
n.append(nC(range(coeff), k)*T**k)
for k in range(0, coeff + 1, 2):
d.append(nC(range(coeff), k)*T**k)
return Add(*n)/Add(*d)
return tanh(arg)
def _eval_rewrite_as_tractable(self, arg, limitvar=None, **kwargs):
neg_exp, pos_exp = exp(-arg), exp(arg)
return (pos_exp - neg_exp)/(pos_exp + neg_exp)
def _eval_rewrite_as_exp(self, arg, **kwargs):
neg_exp, pos_exp = exp(-arg), exp(arg)
return (pos_exp - neg_exp)/(pos_exp + neg_exp)
def _eval_rewrite_as_sinh(self, arg, **kwargs):
return S.ImaginaryUnit*sinh(arg)/sinh(S.Pi*S.ImaginaryUnit/2 - arg)
def _eval_rewrite_as_cosh(self, arg, **kwargs):
return S.ImaginaryUnit*cosh(S.Pi*S.ImaginaryUnit/2 - arg)/cosh(arg)
def _eval_rewrite_as_coth(self, arg, **kwargs):
return 1/coth(arg)
def _eval_as_leading_term(self, x, logx=None, cdir=0):
from sympy import Order
arg = self.args[0].as_leading_term(x)
if x in arg.free_symbols and Order(1, x).contains(arg):
return arg
else:
return self.func(arg)
def _eval_is_real(self):
arg = self.args[0]
if arg.is_real:
return True
re, im = arg.as_real_imag()
# if denom = 0, tanh(arg) = zoo
if re == 0 and im % pi == pi/2:
return None
# check if im is of the form n*pi/2 to make sin(2*im) = 0
# if not, im could be a number, return False in that case
return (im % (pi/2)).is_zero
def _eval_is_extended_real(self):
if self.args[0].is_extended_real:
return True
def _eval_is_positive(self):
if self.args[0].is_extended_real:
return self.args[0].is_positive
def _eval_is_negative(self):
if self.args[0].is_extended_real:
return self.args[0].is_negative
def _eval_is_finite(self):
from sympy import sinh, cos
arg = self.args[0]
re, im = arg.as_real_imag()
denom = cos(im)**2 + sinh(re)**2
if denom == 0:
return False
elif denom.is_number:
return True
if arg.is_extended_real:
return True
def _eval_is_zero(self):
arg = self.args[0]
if arg.is_zero:
return True
class coth(HyperbolicFunction):
r"""
coth(x) is the hyperbolic cotangent of x.
The hyperbolic cotangent function is $\frac{\cosh(x)}{\sinh(x)}$.
Examples
========
>>> from sympy import coth
>>> from sympy.abc import x
>>> coth(x)
coth(x)
See Also
========
sinh, cosh, acoth
"""
def fdiff(self, argindex=1):
if argindex == 1:
return -1/sinh(self.args[0])**2
else:
raise ArgumentIndexError(self, argindex)
def inverse(self, argindex=1):
"""
Returns the inverse of this function.
"""
return acoth
@classmethod
def eval(cls, arg):
from sympy import cot
arg = sympify(arg)
if arg.is_Number:
if arg is S.NaN:
return S.NaN
elif arg is S.Infinity:
return S.One
elif arg is S.NegativeInfinity:
return S.NegativeOne
elif arg.is_zero:
return S.ComplexInfinity
elif arg.is_negative:
return -cls(-arg)
else:
if arg is S.ComplexInfinity:
return S.NaN
i_coeff = arg.as_coefficient(S.ImaginaryUnit)
if i_coeff is not None:
if _coeff_isneg(i_coeff):
return S.ImaginaryUnit * cot(-i_coeff)
return -S.ImaginaryUnit * cot(i_coeff)
else:
if _coeff_isneg(arg):
return -cls(-arg)
if arg.is_Add:
x, m = _peeloff_ipi(arg)
if m:
cothm = coth(m)
if cothm is S.ComplexInfinity:
return coth(x)
else: # cothm == 0
return tanh(x)
if arg.is_zero:
return S.ComplexInfinity
if arg.func == asinh:
x = arg.args[0]
return sqrt(1 + x**2)/x
if arg.func == acosh:
x = arg.args[0]
return x/(sqrt(x - 1) * sqrt(x + 1))
if arg.func == atanh:
return 1/arg.args[0]
if arg.func == acoth:
return arg.args[0]
@staticmethod
@cacheit
def taylor_term(n, x, *previous_terms):
from sympy import bernoulli
if n == 0:
return 1 / sympify(x)
elif n < 0 or n % 2 == 0:
return S.Zero
else:
x = sympify(x)
B = bernoulli(n + 1)
F = factorial(n + 1)
return 2**(n + 1) * B/F * x**n
def _eval_conjugate(self):
return self.func(self.args[0].conjugate())
def as_real_imag(self, deep=True, **hints):
from sympy import cos, sin
if self.args[0].is_extended_real:
if deep:
hints['complex'] = False
return (self.expand(deep, **hints), S.Zero)
else:
return (self, S.Zero)
if deep:
re, im = self.args[0].expand(deep, **hints).as_real_imag()
else:
re, im = self.args[0].as_real_imag()
denom = sinh(re)**2 + sin(im)**2
return (sinh(re)*cosh(re)/denom, -sin(im)*cos(im)/denom)
def _eval_rewrite_as_tractable(self, arg, limitvar=None, **kwargs):
neg_exp, pos_exp = exp(-arg), exp(arg)
return (pos_exp + neg_exp)/(pos_exp - neg_exp)
def _eval_rewrite_as_exp(self, arg, **kwargs):
neg_exp, pos_exp = exp(-arg), exp(arg)
return (pos_exp + neg_exp)/(pos_exp - neg_exp)
def _eval_rewrite_as_sinh(self, arg, **kwargs):
return -S.ImaginaryUnit*sinh(S.Pi*S.ImaginaryUnit/2 - arg)/sinh(arg)
def _eval_rewrite_as_cosh(self, arg, **kwargs):
return -S.ImaginaryUnit*cosh(arg)/cosh(S.Pi*S.ImaginaryUnit/2 - arg)
def _eval_rewrite_as_tanh(self, arg, **kwargs):
return 1/tanh(arg)
def _eval_is_positive(self):
if self.args[0].is_extended_real:
return self.args[0].is_positive
def _eval_is_negative(self):
if self.args[0].is_extended_real:
return self.args[0].is_negative
def _eval_as_leading_term(self, x, logx=None, cdir=0):
from sympy import Order
arg = self.args[0].as_leading_term(x)
if x in arg.free_symbols and Order(1, x).contains(arg):
return 1/arg
else:
return self.func(arg)
def _eval_expand_trig(self, **hints):
arg = self.args[0]
if arg.is_Add:
from sympy import symmetric_poly
CX = [coth(x, evaluate=False)._eval_expand_trig() for x in arg.args]
p = [[], []]
n = len(arg.args)
for i in range(n, -1, -1):
p[(n - i) % 2].append(symmetric_poly(i, CX))
return Add(*p[0])/Add(*p[1])
elif arg.is_Mul:
from sympy import binomial
coeff, x = arg.as_coeff_Mul(rational=True)
if coeff.is_Integer and coeff > 1:
c = coth(x, evaluate=False)
p = [[], []]
for i in range(coeff, -1, -1):
p[(coeff - i) % 2].append(binomial(coeff, i)*c**i)
return Add(*p[0])/Add(*p[1])
return coth(arg)
class ReciprocalHyperbolicFunction(HyperbolicFunction):
"""Base class for reciprocal functions of hyperbolic functions. """
#To be defined in class
_reciprocal_of = None
_is_even = None # type: FuzzyBool
_is_odd = None # type: FuzzyBool
@classmethod
def eval(cls, arg):
if arg.could_extract_minus_sign():
if cls._is_even:
return cls(-arg)
if cls._is_odd:
return -cls(-arg)
t = cls._reciprocal_of.eval(arg)
if hasattr(arg, 'inverse') and arg.inverse() == cls:
return arg.args[0]
return 1/t if t is not None else t
def _call_reciprocal(self, method_name, *args, **kwargs):
# Calls method_name on _reciprocal_of
o = self._reciprocal_of(self.args[0])
return getattr(o, method_name)(*args, **kwargs)
def _calculate_reciprocal(self, method_name, *args, **kwargs):
# If calling method_name on _reciprocal_of returns a value != None
# then return the reciprocal of that value
t = self._call_reciprocal(method_name, *args, **kwargs)
return 1/t if t is not None else t
def _rewrite_reciprocal(self, method_name, arg):
# Special handling for rewrite functions. If reciprocal rewrite returns
# unmodified expression, then return None
t = self._call_reciprocal(method_name, arg)
if t is not None and t != self._reciprocal_of(arg):
return 1/t
def _eval_rewrite_as_exp(self, arg, **kwargs):
return self._rewrite_reciprocal("_eval_rewrite_as_exp", arg)
def _eval_rewrite_as_tractable(self, arg, limitvar=None, **kwargs):
return self._rewrite_reciprocal("_eval_rewrite_as_tractable", arg)
def _eval_rewrite_as_tanh(self, arg, **kwargs):
return self._rewrite_reciprocal("_eval_rewrite_as_tanh", arg)
def _eval_rewrite_as_coth(self, arg, **kwargs):
return self._rewrite_reciprocal("_eval_rewrite_as_coth", arg)
def as_real_imag(self, deep = True, **hints):
return (1 / self._reciprocal_of(self.args[0])).as_real_imag(deep, **hints)
def _eval_conjugate(self):
return self.func(self.args[0].conjugate())
def _eval_expand_complex(self, deep=True, **hints):
re_part, im_part = self.as_real_imag(deep=True, **hints)
return re_part + S.ImaginaryUnit*im_part
def _eval_expand_trig(self, **hints):
return self._calculate_reciprocal("_eval_expand_trig", **hints)
def _eval_as_leading_term(self, x, logx=None, cdir=0):
return (1/self._reciprocal_of(self.args[0]))._eval_as_leading_term(x)
def _eval_is_extended_real(self):
return self._reciprocal_of(self.args[0]).is_extended_real
def _eval_is_finite(self):
return (1/self._reciprocal_of(self.args[0])).is_finite
class csch(ReciprocalHyperbolicFunction):
r"""
csch(x) is the hyperbolic cosecant of x.
The hyperbolic cosecant function is $\frac{2}{e^x - e^{-x}}$
Examples
========
>>> from sympy import csch
>>> from sympy.abc import x
>>> csch(x)
csch(x)
See Also
========
sinh, cosh, tanh, sech, asinh, acosh
"""
_reciprocal_of = sinh
_is_odd = True
def fdiff(self, argindex=1):
"""
Returns the first derivative of this function
"""
if argindex == 1:
return -coth(self.args[0]) * csch(self.args[0])
else:
raise ArgumentIndexError(self, argindex)
@staticmethod
@cacheit
def taylor_term(n, x, *previous_terms):
"""
Returns the next term in the Taylor series expansion
"""
from sympy import bernoulli
if n == 0:
return 1/sympify(x)
elif n < 0 or n % 2 == 0:
return S.Zero
else:
x = sympify(x)
B = bernoulli(n + 1)
F = factorial(n + 1)
return 2 * (1 - 2**n) * B/F * x**n
def _eval_rewrite_as_cosh(self, arg, **kwargs):
return S.ImaginaryUnit / cosh(arg + S.ImaginaryUnit * S.Pi / 2)
def _eval_is_positive(self):
if self.args[0].is_extended_real:
return self.args[0].is_positive
def _eval_is_negative(self):
if self.args[0].is_extended_real:
return self.args[0].is_negative
class sech(ReciprocalHyperbolicFunction):
r"""
sech(x) is the hyperbolic secant of x.
The hyperbolic secant function is $\frac{2}{e^x + e^{-x}}$
Examples
========
>>> from sympy import sech
>>> from sympy.abc import x
>>> sech(x)
sech(x)
See Also
========
sinh, cosh, tanh, coth, csch, asinh, acosh
"""
_reciprocal_of = cosh
_is_even = True
def fdiff(self, argindex=1):
if argindex == 1:
return - tanh(self.args[0])*sech(self.args[0])
else:
raise ArgumentIndexError(self, argindex)
@staticmethod
@cacheit
def taylor_term(n, x, *previous_terms):
from sympy.functions.combinatorial.numbers import euler
if n < 0 or n % 2 == 1:
return S.Zero
else:
x = sympify(x)
return euler(n) / factorial(n) * x**(n)
def _eval_rewrite_as_sinh(self, arg, **kwargs):
return S.ImaginaryUnit / sinh(arg + S.ImaginaryUnit * S.Pi /2)
def _eval_is_positive(self):
if self.args[0].is_extended_real:
return True
###############################################################################
############################# HYPERBOLIC INVERSES #############################
###############################################################################
class InverseHyperbolicFunction(Function):
"""Base class for inverse hyperbolic functions."""
pass
class asinh(InverseHyperbolicFunction):
"""
asinh(x) is the inverse hyperbolic sine of x.
The inverse hyperbolic sine function.
Examples
========
>>> from sympy import asinh
>>> from sympy.abc import x
>>> asinh(x).diff(x)
1/sqrt(x**2 + 1)
>>> asinh(1)
log(1 + sqrt(2))
See Also
========
acosh, atanh, sinh
"""
def fdiff(self, argindex=1):
if argindex == 1:
return 1/sqrt(self.args[0]**2 + 1)
else:
raise ArgumentIndexError(self, argindex)
@classmethod
def eval(cls, arg):
from sympy import asin
arg = sympify(arg)
if arg.is_Number:
if arg is S.NaN:
return S.NaN
elif arg is S.Infinity:
return S.Infinity
elif arg is S.NegativeInfinity:
return S.NegativeInfinity
elif arg.is_zero:
return S.Zero
elif arg is S.One:
return log(sqrt(2) + 1)
elif arg is S.NegativeOne:
return log(sqrt(2) - 1)
elif arg.is_negative:
return -cls(-arg)
else:
if arg is S.ComplexInfinity:
return S.ComplexInfinity
if arg.is_zero:
return S.Zero
i_coeff = arg.as_coefficient(S.ImaginaryUnit)
if i_coeff is not None:
return S.ImaginaryUnit * asin(i_coeff)
else:
if _coeff_isneg(arg):
return -cls(-arg)
if isinstance(arg, sinh) and arg.args[0].is_number:
z = arg.args[0]
if z.is_real:
return z
r, i = match_real_imag(z)
if r is not None and i is not None:
f = floor((i + pi/2)/pi)
m = z - I*pi*f
even = f.is_even
if even is True:
return m
elif even is False:
return -m
@staticmethod
@cacheit
def taylor_term(n, x, *previous_terms):
if n < 0 or n % 2 == 0:
return S.Zero
else:
x = sympify(x)
if len(previous_terms) >= 2 and n > 2:
p = previous_terms[-2]
return -p * (n - 2)**2/(n*(n - 1)) * x**2
else:
k = (n - 1) // 2
R = RisingFactorial(S.Half, k)
F = factorial(k)
return (-1)**k * R / F * x**n / n
def _eval_as_leading_term(self, x, logx=None, cdir=0):
from sympy import Order
arg = self.args[0].as_leading_term(x)
if x in arg.free_symbols and Order(1, x).contains(arg):
return arg
else:
return self.func(arg)
def _eval_rewrite_as_log(self, x, **kwargs):
return log(x + sqrt(x**2 + 1))
def inverse(self, argindex=1):
"""
Returns the inverse of this function.
"""
return sinh
def _eval_is_zero(self):
arg = self.args[0]
if arg.is_zero:
return True
class acosh(InverseHyperbolicFunction):
"""
acosh(x) is the inverse hyperbolic cosine of x.
The inverse hyperbolic cosine function.
Examples
========
>>> from sympy import acosh
>>> from sympy.abc import x
>>> acosh(x).diff(x)
1/sqrt(x**2 - 1)
>>> acosh(1)
0
See Also
========
asinh, atanh, cosh
"""
def fdiff(self, argindex=1):
if argindex == 1:
return 1/sqrt(self.args[0]**2 - 1)
else:
raise ArgumentIndexError(self, argindex)
@classmethod
def eval(cls, arg):
arg = sympify(arg)
if arg.is_Number:
if arg is S.NaN:
return S.NaN
elif arg is S.Infinity:
return S.Infinity
elif arg is S.NegativeInfinity:
return S.Infinity
elif arg.is_zero:
return S.Pi*S.ImaginaryUnit / 2
elif arg is S.One:
return S.Zero
elif arg is S.NegativeOne:
return S.Pi*S.ImaginaryUnit
if arg.is_number:
cst_table = {
S.ImaginaryUnit: log(S.ImaginaryUnit*(1 + sqrt(2))),
-S.ImaginaryUnit: log(-S.ImaginaryUnit*(1 + sqrt(2))),
S.Half: S.Pi/3,
Rational(-1, 2): S.Pi*Rational(2, 3),
sqrt(2)/2: S.Pi/4,
-sqrt(2)/2: S.Pi*Rational(3, 4),
1/sqrt(2): S.Pi/4,
-1/sqrt(2): S.Pi*Rational(3, 4),
sqrt(3)/2: S.Pi/6,
-sqrt(3)/2: S.Pi*Rational(5, 6),
(sqrt(3) - 1)/sqrt(2**3): S.Pi*Rational(5, 12),
-(sqrt(3) - 1)/sqrt(2**3): S.Pi*Rational(7, 12),
sqrt(2 + sqrt(2))/2: S.Pi/8,
-sqrt(2 + sqrt(2))/2: S.Pi*Rational(7, 8),
sqrt(2 - sqrt(2))/2: S.Pi*Rational(3, 8),
-sqrt(2 - sqrt(2))/2: S.Pi*Rational(5, 8),
(1 + sqrt(3))/(2*sqrt(2)): S.Pi/12,
-(1 + sqrt(3))/(2*sqrt(2)): S.Pi*Rational(11, 12),
(sqrt(5) + 1)/4: S.Pi/5,
-(sqrt(5) + 1)/4: S.Pi*Rational(4, 5)
}
if arg in cst_table:
if arg.is_extended_real:
return cst_table[arg]*S.ImaginaryUnit
return cst_table[arg]
if arg is S.ComplexInfinity:
return S.ComplexInfinity
if arg == S.ImaginaryUnit*S.Infinity:
return S.Infinity + S.ImaginaryUnit*S.Pi/2
if arg == -S.ImaginaryUnit*S.Infinity:
return S.Infinity - S.ImaginaryUnit*S.Pi/2
if arg.is_zero:
return S.Pi*S.ImaginaryUnit*S.Half
if isinstance(arg, cosh) and arg.args[0].is_number:
z = arg.args[0]
if z.is_real:
from sympy.functions.elementary.complexes import Abs
return Abs(z)
r, i = match_real_imag(z)
if r is not None and i is not None:
f = floor(i/pi)
m = z - I*pi*f
even = f.is_even
if even is True:
if r.is_nonnegative:
return m
elif r.is_negative:
return -m
elif even is False:
m -= I*pi
if r.is_nonpositive:
return -m
elif r.is_positive:
return m
@staticmethod
@cacheit
def taylor_term(n, x, *previous_terms):
if n == 0:
return S.Pi*S.ImaginaryUnit / 2
elif n < 0 or n % 2 == 0:
return S.Zero
else:
x = sympify(x)
if len(previous_terms) >= 2 and n > 2:
p = previous_terms[-2]
return p * (n - 2)**2/(n*(n - 1)) * x**2
else:
k = (n - 1) // 2
R = RisingFactorial(S.Half, k)
F = factorial(k)
return -R / F * S.ImaginaryUnit * x**n / n
def _eval_as_leading_term(self, x, logx=None, cdir=0):
from sympy import Order
arg = self.args[0].as_leading_term(x)
if x in arg.free_symbols and Order(1, x).contains(arg):
return S.ImaginaryUnit*S.Pi/2
else:
return self.func(arg)
def _eval_rewrite_as_log(self, x, **kwargs):
return log(x + sqrt(x + 1) * sqrt(x - 1))
def inverse(self, argindex=1):
"""
Returns the inverse of this function.
"""
return cosh
class atanh(InverseHyperbolicFunction):
"""
atanh(x) is the inverse hyperbolic tangent of x.
The inverse hyperbolic tangent function.
Examples
========
>>> from sympy import atanh
>>> from sympy.abc import x
>>> atanh(x).diff(x)
1/(1 - x**2)
See Also
========
asinh, acosh, tanh
"""
def fdiff(self, argindex=1):
if argindex == 1:
return 1/(1 - self.args[0]**2)
else:
raise ArgumentIndexError(self, argindex)
@classmethod
def eval(cls, arg):
from sympy import atan
arg = sympify(arg)
if arg.is_Number:
if arg is S.NaN:
return S.NaN
elif arg.is_zero:
return S.Zero
elif arg is S.One:
return S.Infinity
elif arg is S.NegativeOne:
return S.NegativeInfinity
elif arg is S.Infinity:
return -S.ImaginaryUnit * atan(arg)
elif arg is S.NegativeInfinity:
return S.ImaginaryUnit * atan(-arg)
elif arg.is_negative:
return -cls(-arg)
else:
if arg is S.ComplexInfinity:
from sympy.calculus.util import AccumBounds
return S.ImaginaryUnit*AccumBounds(-S.Pi/2, S.Pi/2)
i_coeff = arg.as_coefficient(S.ImaginaryUnit)
if i_coeff is not None:
return S.ImaginaryUnit * atan(i_coeff)
else:
if _coeff_isneg(arg):
return -cls(-arg)
if arg.is_zero:
return S.Zero
if isinstance(arg, tanh) and arg.args[0].is_number:
z = arg.args[0]
if z.is_real:
return z
r, i = match_real_imag(z)
if r is not None and i is not None:
f = floor(2*i/pi)
even = f.is_even
m = z - I*f*pi/2
if even is True:
return m
elif even is False:
return m - I*pi/2
@staticmethod
@cacheit
def taylor_term(n, x, *previous_terms):
if n < 0 or n % 2 == 0:
return S.Zero
else:
x = sympify(x)
return x**n / n
def _eval_as_leading_term(self, x, logx=None, cdir=0):
from sympy import Order
arg = self.args[0].as_leading_term(x)
if x in arg.free_symbols and Order(1, x).contains(arg):
return arg
else:
return self.func(arg)
def _eval_rewrite_as_log(self, x, **kwargs):
return (log(1 + x) - log(1 - x)) / 2
def _eval_is_zero(self):
arg = self.args[0]
if arg.is_zero:
return True
def inverse(self, argindex=1):
"""
Returns the inverse of this function.
"""
return tanh
class acoth(InverseHyperbolicFunction):
"""
acoth(x) is the inverse hyperbolic cotangent of x.
The inverse hyperbolic cotangent function.
Examples
========
>>> from sympy import acoth
>>> from sympy.abc import x
>>> acoth(x).diff(x)
1/(1 - x**2)
See Also
========
asinh, acosh, coth
"""
def fdiff(self, argindex=1):
if argindex == 1:
return 1/(1 - self.args[0]**2)
else:
raise ArgumentIndexError(self, argindex)
@classmethod
def eval(cls, arg):
from sympy import acot
arg = sympify(arg)
if arg.is_Number:
if arg is S.NaN:
return S.NaN
elif arg is S.Infinity:
return S.Zero
elif arg is S.NegativeInfinity:
return S.Zero
elif arg.is_zero:
return S.Pi*S.ImaginaryUnit / 2
elif arg is S.One:
return S.Infinity
elif arg is S.NegativeOne:
return S.NegativeInfinity
elif arg.is_negative:
return -cls(-arg)
else:
if arg is S.ComplexInfinity:
return S.Zero
i_coeff = arg.as_coefficient(S.ImaginaryUnit)
if i_coeff is not None:
return -S.ImaginaryUnit * acot(i_coeff)
else:
if _coeff_isneg(arg):
return -cls(-arg)
if arg.is_zero:
return S.Pi*S.ImaginaryUnit*S.Half
@staticmethod
@cacheit
def taylor_term(n, x, *previous_terms):
if n == 0:
return S.Pi*S.ImaginaryUnit / 2
elif n < 0 or n % 2 == 0:
return S.Zero
else:
x = sympify(x)
return x**n / n
def _eval_as_leading_term(self, x, logx=None, cdir=0):
from sympy import Order
arg = self.args[0].as_leading_term(x)
if x in arg.free_symbols and Order(1, x).contains(arg):
return S.ImaginaryUnit*S.Pi/2
else:
return self.func(arg)
def _eval_rewrite_as_log(self, x, **kwargs):
return (log(1 + 1/x) - log(1 - 1/x)) / 2
def inverse(self, argindex=1):
"""
Returns the inverse of this function.
"""
return coth
class asech(InverseHyperbolicFunction):
"""
asech(x) is the inverse hyperbolic secant of x.
The inverse hyperbolic secant function.
Examples
========
>>> from sympy import asech, sqrt, S
>>> from sympy.abc import x
>>> asech(x).diff(x)
-1/(x*sqrt(1 - x**2))
>>> asech(1).diff(x)
0
>>> asech(1)
0
>>> asech(S(2))
I*pi/3
>>> asech(-sqrt(2))
3*I*pi/4
>>> asech((sqrt(6) - sqrt(2)))
I*pi/12
See Also
========
asinh, atanh, cosh, acoth
References
==========
.. [1] https://en.wikipedia.org/wiki/Hyperbolic_function
.. [2] http://dlmf.nist.gov/4.37
.. [3] http://functions.wolfram.com/ElementaryFunctions/ArcSech/
"""
def fdiff(self, argindex=1):
if argindex == 1:
z = self.args[0]
return -1/(z*sqrt(1 - z**2))
else:
raise ArgumentIndexError(self, argindex)
@classmethod
def eval(cls, arg):
arg = sympify(arg)
if arg.is_Number:
if arg is S.NaN:
return S.NaN
elif arg is S.Infinity:
return S.Pi*S.ImaginaryUnit / 2
elif arg is S.NegativeInfinity:
return S.Pi*S.ImaginaryUnit / 2
elif arg.is_zero:
return S.Infinity
elif arg is S.One:
return S.Zero
elif arg is S.NegativeOne:
return S.Pi*S.ImaginaryUnit
if arg.is_number:
cst_table = {
S.ImaginaryUnit: - (S.Pi*S.ImaginaryUnit / 2) + log(1 + sqrt(2)),
-S.ImaginaryUnit: (S.Pi*S.ImaginaryUnit / 2) + log(1 + sqrt(2)),
(sqrt(6) - sqrt(2)): S.Pi / 12,
(sqrt(2) - sqrt(6)): 11*S.Pi / 12,
sqrt(2 - 2/sqrt(5)): S.Pi / 10,
-sqrt(2 - 2/sqrt(5)): 9*S.Pi / 10,
2 / sqrt(2 + sqrt(2)): S.Pi / 8,
-2 / sqrt(2 + sqrt(2)): 7*S.Pi / 8,
2 / sqrt(3): S.Pi / 6,
-2 / sqrt(3): 5*S.Pi / 6,
(sqrt(5) - 1): S.Pi / 5,
(1 - sqrt(5)): 4*S.Pi / 5,
sqrt(2): S.Pi / 4,
-sqrt(2): 3*S.Pi / 4,
sqrt(2 + 2/sqrt(5)): 3*S.Pi / 10,
-sqrt(2 + 2/sqrt(5)): 7*S.Pi / 10,
S(2): S.Pi / 3,
-S(2): 2*S.Pi / 3,
sqrt(2*(2 + sqrt(2))): 3*S.Pi / 8,
-sqrt(2*(2 + sqrt(2))): 5*S.Pi / 8,
(1 + sqrt(5)): 2*S.Pi / 5,
(-1 - sqrt(5)): 3*S.Pi / 5,
(sqrt(6) + sqrt(2)): 5*S.Pi / 12,
(-sqrt(6) - sqrt(2)): 7*S.Pi / 12,
}
if arg in cst_table:
if arg.is_extended_real:
return cst_table[arg]*S.ImaginaryUnit
return cst_table[arg]
if arg is S.ComplexInfinity:
from sympy.calculus.util import AccumBounds
return S.ImaginaryUnit*AccumBounds(-S.Pi/2, S.Pi/2)
if arg.is_zero:
return S.Infinity
@staticmethod
@cacheit
def expansion_term(n, x, *previous_terms):
if n == 0:
return log(2 / x)
elif n < 0 or n % 2 == 1:
return S.Zero
else:
x = sympify(x)
if len(previous_terms) > 2 and n > 2:
p = previous_terms[-2]
return p * (n - 1)**2 // (n // 2)**2 * x**2 / 4
else:
k = n // 2
R = RisingFactorial(S.Half , k) * n
F = factorial(k) * n // 2 * n // 2
return -1 * R / F * x**n / 4
def inverse(self, argindex=1):
"""
Returns the inverse of this function.
"""
return sech
def _eval_rewrite_as_log(self, arg, **kwargs):
return log(1/arg + sqrt(1/arg - 1) * sqrt(1/arg + 1))
class acsch(InverseHyperbolicFunction):
"""
acsch(x) is the inverse hyperbolic cosecant of x.
The inverse hyperbolic cosecant function.
Examples
========
>>> from sympy import acsch, sqrt, S
>>> from sympy.abc import x
>>> acsch(x).diff(x)
-1/(x**2*sqrt(1 + x**(-2)))
>>> acsch(1).diff(x)
0
>>> acsch(1)
log(1 + sqrt(2))
>>> acsch(S.ImaginaryUnit)
-I*pi/2
>>> acsch(-2*S.ImaginaryUnit)
I*pi/6
>>> acsch(S.ImaginaryUnit*(sqrt(6) - sqrt(2)))
-5*I*pi/12
See Also
========
asinh
References
==========
.. [1] https://en.wikipedia.org/wiki/Hyperbolic_function
.. [2] http://dlmf.nist.gov/4.37
.. [3] http://functions.wolfram.com/ElementaryFunctions/ArcCsch/
"""
def fdiff(self, argindex=1):
if argindex == 1:
z = self.args[0]
return -1/(z**2*sqrt(1 + 1/z**2))
else:
raise ArgumentIndexError(self, argindex)
@classmethod
def eval(cls, arg):
arg = sympify(arg)
if arg.is_Number:
if arg is S.NaN:
return S.NaN
elif arg is S.Infinity:
return S.Zero
elif arg is S.NegativeInfinity:
return S.Zero
elif arg.is_zero:
return S.ComplexInfinity
elif arg is S.One:
return log(1 + sqrt(2))
elif arg is S.NegativeOne:
return - log(1 + sqrt(2))
if arg.is_number:
cst_table = {
S.ImaginaryUnit: -S.Pi / 2,
S.ImaginaryUnit*(sqrt(2) + sqrt(6)): -S.Pi / 12,
S.ImaginaryUnit*(1 + sqrt(5)): -S.Pi / 10,
S.ImaginaryUnit*2 / sqrt(2 - sqrt(2)): -S.Pi / 8,
S.ImaginaryUnit*2: -S.Pi / 6,
S.ImaginaryUnit*sqrt(2 + 2/sqrt(5)): -S.Pi / 5,
S.ImaginaryUnit*sqrt(2): -S.Pi / 4,
S.ImaginaryUnit*(sqrt(5)-1): -3*S.Pi / 10,
S.ImaginaryUnit*2 / sqrt(3): -S.Pi / 3,
S.ImaginaryUnit*2 / sqrt(2 + sqrt(2)): -3*S.Pi / 8,
S.ImaginaryUnit*sqrt(2 - 2/sqrt(5)): -2*S.Pi / 5,
S.ImaginaryUnit*(sqrt(6) - sqrt(2)): -5*S.Pi / 12,
S(2): -S.ImaginaryUnit*log((1+sqrt(5))/2),
}
if arg in cst_table:
return cst_table[arg]*S.ImaginaryUnit
if arg is S.ComplexInfinity:
return S.Zero
if arg.is_zero:
return S.ComplexInfinity
if _coeff_isneg(arg):
return -cls(-arg)
def inverse(self, argindex=1):
"""
Returns the inverse of this function.
"""
return csch
def _eval_rewrite_as_log(self, arg, **kwargs):
return log(1/arg + sqrt(1/arg**2 + 1))