/

# Source code for sympy.functions.elementary.hyperbolic

from sympy.core import S, C, sympify, cacheit
from sympy.core.function import Function, ArgumentIndexError, _coeff_isneg

from sympy.functions.elementary.miscellaneous import sqrt

###############################################################################
########################### HYPERBOLIC FUNCTIONS ##############################
###############################################################################

[docs]class HyperbolicFunction(Function):
"""Base class for hyperbolic functions. """

unbranched = True

[docs]class sinh(HyperbolicFunction):
"""
The hyperbolic sine function, :math:\\frac{exp(x) - exp(-x)}{2}.

* sinh(x) -> Returns the hyperbolic sine of x

========

cosh, tanh, asinh
"""
nargs = 1

[docs]    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)

[docs]    def inverse(self, argindex=1):
"""
Returns the inverse of this function.
"""
return asinh

@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.NegativeInfinity
elif arg is S.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 * C.sin(i_coeff)
else:
if _coeff_isneg(arg):
return -cls(-arg)

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
[docs]    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) / C.factorial(n)

def _eval_conjugate(self):
return self.func(self.args[0].conjugate())

[docs]    def as_real_imag(self, deep=True, **hints):
"""
Returns this function as a complex coordinate.
"""
if self.args[0].is_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)*C.cos(im), cosh(re)*C.sin(im))

def _eval_rewrite_as_exp(self, arg):
return (C.exp(arg) - C.exp(-arg)) / 2

def _eval_rewrite_as_cosh(self, arg):
return -S.ImaginaryUnit*cosh(arg + S.Pi*S.ImaginaryUnit/2)

def _eval_rewrite_as_tanh(self, arg):
tanh_half = tanh(S.Half*arg)
return 2*tanh_half/(1 - tanh_half**2)

def _eval_rewrite_as_coth(self, arg):
coth_half = coth(S.Half*arg)
return 2*coth_half/(coth_half**2 - 1)

if x in arg.free_symbols and C.Order(1, x).contains(arg):
return S.One
else:
return self.func(arg)

def _eval_is_real(self):
return self.args[0].is_real

def _eval_is_bounded(self):
arg = self.args[0]
if arg.is_imaginary:
return True

def _sage_(self):
import sage.all as sage
return sage.sinh(self.args[0]._sage_())

[docs]class cosh(HyperbolicFunction):
"""
The hyperbolic cosine function, :math:\\frac{exp(x) + exp(-x)}{2}.

* cosh(x) -> Returns the hyperbolic cosine of x

========

sinh, tanh, acosh
"""
nargs = 1

def fdiff(self, argindex=1):
if argindex == 1:
return sinh(self.args[0])
else:
raise ArgumentIndexError(self, argindex)

[docs]    def inverse(self, argindex=1):
"""
Returns the inverse of this function.
"""
return acosh

@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 S.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 C.cos(i_coeff)
else:
if _coeff_isneg(arg):
return cls(-arg)

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)/C.factorial(n)

def _eval_conjugate(self):
return self.func(self.args[0].conjugate())

def as_real_imag(self, deep=True, **hints):
if self.args[0].is_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)*C.cos(im), sinh(re)*C.sin(im))

def _eval_rewrite_as_exp(self, arg):
return (C.exp(arg) + C.exp(-arg)) / 2

def _eval_rewrite_as_sinh(self, arg):
return -S.ImaginaryUnit*sinh(arg + S.Pi*S.ImaginaryUnit/2)

def _eval_rewrite_as_tanh(self, arg):
tanh_half = tanh(S.Half*arg)**2
return (1+tanh_half)/(1-tanh_half)

def _eval_rewrite_as_coth(self, arg):
coth_half = coth(S.Half*arg)**2
return (coth_half+1)/(coth_half-1)

if x in arg.free_symbols and C.Order(1, x).contains(arg):
return S.One
else:
return self.func(arg)

def _eval_is_real(self):
return self.args[0].is_real

def _eval_is_bounded(self):
arg = self.args[0]
if arg.is_imaginary:
return True

def _sage_(self):
import sage.all as sage
return sage.cosh(self.args[0]._sage_())

[docs]class tanh(HyperbolicFunction):
"""
The hyperbolic tangent function, :math:\\frac{sinh(x)}{cosh(x)}.

* tanh(x) -> Returns the hyperbolic tangent of x

========

sinh, cosh, atanh
"""
nargs = 1

def fdiff(self, argindex=1):
if argindex == 1:
return S.One - tanh(self.args[0])**2
else:
raise ArgumentIndexError(self, argindex)

[docs]    def inverse(self, argindex=1):
"""
Returns the inverse of this function.
"""
return atanh

@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.One
elif arg is S.NegativeInfinity:
return S.NegativeOne
elif arg is S.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 * C.tan(-i_coeff)
return S.ImaginaryUnit * C.tan(i_coeff)
else:
if _coeff_isneg(arg):
return -cls(-arg)

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):
if n < 0 or n % 2 == 0:
return S.Zero
else:
x = sympify(x)

a = 2**(n+1)

B = C.bernoulli(n+1)
F = C.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):
if self.args[0].is_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 + C.cos(im)**2
return (sinh(re)*cosh(re)/denom, C.sin(im)*C.cos(im)/denom)

def _eval_rewrite_as_exp(self, arg):
neg_exp, pos_exp = C.exp(-arg), C.exp(arg)
return (pos_exp-neg_exp)/(pos_exp+neg_exp)

def _eval_rewrite_as_sinh(self, arg):
return S.ImaginaryUnit*sinh(arg)/sinh(S.Pi*S.ImaginaryUnit/2 - arg)

def _eval_rewrite_as_cosh(self, arg):
return S.ImaginaryUnit*cosh(S.Pi*S.ImaginaryUnit/2 - arg)/cosh(arg)

def _eval_rewrite_as_coth(self, arg):
return 1/coth(arg)

if x in arg.free_symbols and C.Order(1, x).contains(arg):
return S.One
else:
return self.func(arg)

def _eval_is_real(self):
return self.args[0].is_real

def _eval_is_bounded(self):
arg = self.args[0]
if arg.is_real:
return True

def _sage_(self):
import sage.all as sage
return sage.tanh(self.args[0]._sage_())

[docs]class coth(HyperbolicFunction):
"""
The hyperbolic tangent function, :math:\\frac{cosh(x)}{sinh(x)}.

* coth(x) -> Returns the hyperbolic cotangent of x
"""
nargs = 1

def fdiff(self, argindex=1):
if argindex == 1:
return -1/sinh(self.args[0])**2
else:
raise ArgumentIndexError(self, argindex)

[docs]    def inverse(self, argindex=1):
"""
Returns the inverse of this function.
"""
return acoth

@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.One
elif arg is S.NegativeInfinity:
return S.NegativeOne
elif arg is S.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 * C.cot(-i_coeff)
return -S.ImaginaryUnit * C.cot(i_coeff)
else:
if _coeff_isneg(arg):
return -cls(-arg)

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):
if n == 0:
return 1 / sympify(x)
elif n < 0 or n % 2 == 0:
return S.Zero
else:
x = sympify(x)

B = C.bernoulli(n+1)
F = C.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):
if self.args[0].is_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 + C.sin(im)**2
return (sinh(re)*cosh(re)/denom, -C.sin(im)*C.cos(im)/denom)

def _eval_rewrite_as_exp(self, arg):
neg_exp, pos_exp = C.exp(-arg), C.exp(arg)
return (pos_exp+neg_exp)/(pos_exp-neg_exp)

def _eval_rewrite_as_sinh(self, arg):
return -S.ImaginaryUnit*sinh(S.Pi*S.ImaginaryUnit/2 - arg)/sinh(arg)

def _eval_rewrite_as_cosh(self, arg):
return -S.ImaginaryUnit*cosh(arg)/cosh(S.Pi*S.ImaginaryUnit/2 - arg)

def _eval_rewrite_as_tanh(self, arg):
return 1/tanh(arg)

if x in arg.free_symbols and C.Order(1, x).contains(arg):
return S.One
else:
return self.func(arg)

def _sage_(self):
import sage.all as sage
return sage.coth(self.args[0]._sage_())

###############################################################################
############################# HYPERBOLIC INVERSES #############################
###############################################################################

[docs]class asinh(Function):
"""
The inverse hyperbolic sine function.

* asinh(x) -> Returns the inverse hyperbolic sine of x

========

acosh, atanh, sinh
"""
nargs = 1

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.NegativeInfinity
elif arg is S.Zero:
return S.Zero
elif arg is S.One:
return C.log(sqrt(2) + 1)
elif arg is S.NegativeOne:
return C.log(sqrt(2) - 1)
elif arg.is_negative:
return -cls(-arg)
else:
if arg is S.ComplexInfinity:
return S.ComplexInfinity

i_coeff = arg.as_coefficient(S.ImaginaryUnit)

if i_coeff is not None:
return S.ImaginaryUnit * C.asin(i_coeff)
else:
if _coeff_isneg(arg):
return -cls(-arg)

@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 = C.RisingFactorial(S.Half, k)
F = C.factorial(k)
return (-1)**k * R / F * x**n / n

if x in arg.free_symbols and C.Order(1, x).contains(arg):
return arg
else:
return self.func(arg)

def _sage_(self):
import sage.all as sage
return sage.asinh(self.args[0]._sage_())

[docs]class acosh(Function):
"""
The inverse hyperbolic cosine function.

* acosh(x) -> Returns the inverse hyperbolic cosine of x

========

asinh, atanh, cosh
"""
nargs = 1

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 S.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 : C.log(S.ImaginaryUnit*(1+sqrt(2))),
-S.ImaginaryUnit : C.log(-S.ImaginaryUnit*(1+sqrt(2))),
S.Half       : S.Pi/3,
-S.Half      : 2*S.Pi/3,
sqrt(2)/2    : S.Pi/4,
-sqrt(2)/2   : 3*S.Pi/4,
1/sqrt(2)    : S.Pi/4,
-1/sqrt(2)   : 3*S.Pi/4,
sqrt(3)/2    : S.Pi/6,
-sqrt(3)/2   : 5*S.Pi/6,
(sqrt(3)-1)/sqrt(2**3) : 5*S.Pi/12,
-(sqrt(3)-1)/sqrt(2**3) : 7*S.Pi/12,
sqrt(2+sqrt(2))/2 : S.Pi/8,
-sqrt(2+sqrt(2))/2 : 7*S.Pi/8,
sqrt(2-sqrt(2))/2 : 3*S.Pi/8,
-sqrt(2-sqrt(2))/2 : 5*S.Pi/8,
(1+sqrt(3))/(2*sqrt(2)) : S.Pi/12,
-(1+sqrt(3))/(2*sqrt(2)) : 11*S.Pi/12,
(sqrt(5)+1)/4 : S.Pi/5,
-(sqrt(5)+1)/4 : 4*S.Pi/5
}

if arg in cst_table:
if arg.is_real:
return cst_table[arg]*S.ImaginaryUnit
return cst_table[arg]

if arg is S.ComplexInfinity:
return S.Infinity

i_coeff = arg.as_coefficient(S.ImaginaryUnit)

if i_coeff is not None:
if _coeff_isneg(i_coeff):
return S.ImaginaryUnit * C.acos(i_coeff)
return S.ImaginaryUnit * C.acos(-i_coeff)
else:
if _coeff_isneg(arg):
return -cls(-arg)

@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 = C.RisingFactorial(S.Half, k)
F = C.factorial(k)
return -R / F * S.ImaginaryUnit * x**n / n

if x in arg.free_symbols and C.Order(1, x).contains(arg):
return arg
else:
return self.func(arg)

def _sage_(self):
import sage.all as sage
return sage.acosh(self.args[0]._sage_())

[docs]class atanh(Function):
"""
The inverse hyperbolic tangent function.

* atanh(x) -> Returns the inverse hyperbolic tangent of x

========

asinh, acosh, tanh
"""
nargs = 1

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):
arg = sympify(arg)

if arg.is_Number:
if arg is S.NaN:
return S.NaN
elif arg is S.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 * C.atan(arg)
elif arg is S.NegativeInfinity:
return S.ImaginaryUnit * C.atan(-arg)
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 * C.atan(i_coeff)
else:
if _coeff_isneg(arg):
return -cls(-arg)

@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

if x in arg.free_symbols and C.Order(1, x).contains(arg):
return arg
else:
return self.func(arg)

def _sage_(self):
import sage.all as sage
return sage.atanh(self.args[0]._sage_())

[docs]class acoth(Function):
"""
The inverse hyperbolic cotangent function.

* acoth(x) -> Returns the inverse hyperbolic cotangent of x
"""
nargs = 1

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):
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 S.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 0

i_coeff = arg.as_coefficient(S.ImaginaryUnit)

if i_coeff is not None:
return -S.ImaginaryUnit * C.acot(i_coeff)
else:
if _coeff_isneg(arg):
return -cls(-arg)

@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