You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

336 lines
11 KiB

5 months ago
from sympy.core import cacheit, Dummy, Ne, Integer, Rational, S, Wild
from sympy.functions import binomial, sin, cos, Piecewise, Abs
from .integrals import integrate
# TODO sin(a*x)*cos(b*x) -> sin((a+b)x) + sin((a-b)x) ?
# creating, each time, Wild's and sin/cos/Mul is expensive. Also, our match &
# subs are very slow when not cached, and if we create Wild each time, we
# effectively block caching.
#
# so we cache the pattern
# need to use a function instead of lamda since hash of lambda changes on
# each call to _pat_sincos
def _integer_instance(n):
return isinstance(n, Integer)
@cacheit
def _pat_sincos(x):
a = Wild('a', exclude=[x])
n, m = [Wild(s, exclude=[x], properties=[_integer_instance])
for s in 'nm']
pat = sin(a*x)**n * cos(a*x)**m
return pat, a, n, m
_u = Dummy('u')
def trigintegrate(f, x, conds='piecewise'):
"""
Integrate f = Mul(trig) over x.
Examples
========
>>> from sympy import sin, cos, tan, sec
>>> from sympy.integrals.trigonometry import trigintegrate
>>> from sympy.abc import x
>>> trigintegrate(sin(x)*cos(x), x)
sin(x)**2/2
>>> trigintegrate(sin(x)**2, x)
x/2 - sin(x)*cos(x)/2
>>> trigintegrate(tan(x)*sec(x), x)
1/cos(x)
>>> trigintegrate(sin(x)*tan(x), x)
-log(sin(x) - 1)/2 + log(sin(x) + 1)/2 - sin(x)
References
==========
.. [1] https://en.wikibooks.org/wiki/Calculus/Integration_techniques
See Also
========
sympy.integrals.integrals.Integral.doit
sympy.integrals.integrals.Integral
"""
pat, a, n, m = _pat_sincos(x)
f = f.rewrite('sincos')
M = f.match(pat)
if M is None:
return
n, m = M[n], M[m]
if n.is_zero and m.is_zero:
return x
zz = x if n.is_zero else S.Zero
a = M[a]
if n.is_odd or m.is_odd:
u = _u
n_, m_ = n.is_odd, m.is_odd
# take smallest n or m -- to choose simplest substitution
if n_ and m_:
# Make sure to choose the positive one
# otherwise an incorrect integral can occur.
if n < 0 and m > 0:
m_ = True
n_ = False
elif m < 0 and n > 0:
n_ = True
m_ = False
# Both are negative so choose the smallest n or m
# in absolute value for simplest substitution.
elif (n < 0 and m < 0):
n_ = n > m
m_ = not (n > m)
# Both n and m are odd and positive
else:
n_ = (n < m) # NB: careful here, one of the
m_ = not (n < m) # conditions *must* be true
# n m u=C (n-1)/2 m
# S(x) * C(x) dx --> -(1-u^2) * u du
if n_:
ff = -(1 - u**2)**((n - 1)/2) * u**m
uu = cos(a*x)
# n m u=S n (m-1)/2
# S(x) * C(x) dx --> u * (1-u^2) du
elif m_:
ff = u**n * (1 - u**2)**((m - 1)/2)
uu = sin(a*x)
fi = integrate(ff, u) # XXX cyclic deps
fx = fi.subs(u, uu)
if conds == 'piecewise':
return Piecewise((fx / a, Ne(a, 0)), (zz, True))
return fx / a
# n & m are both even
#
# 2k 2m 2l 2l
# we transform S (x) * C (x) into terms with only S (x) or C (x)
#
# example:
# 100 4 100 2 2 100 4 2
# S (x) * C (x) = S (x) * (1-S (x)) = S (x) * (1 + S (x) - 2*S (x))
#
# 104 102 100
# = S (x) - 2*S (x) + S (x)
# 2k
# then S is integrated with recursive formula
# take largest n or m -- to choose simplest substitution
n_ = (Abs(n) > Abs(m))
m_ = (Abs(m) > Abs(n))
res = S.Zero
if n_:
# 2k 2 k i 2i
# C = (1 - S ) = sum(i, (-) * B(k, i) * S )
if m > 0:
for i in range(0, m//2 + 1):
res += (S.NegativeOne**i * binomial(m//2, i) *
_sin_pow_integrate(n + 2*i, x))
elif m == 0:
res = _sin_pow_integrate(n, x)
else:
# m < 0 , |n| > |m|
# /
# |
# | m n
# | cos (x) sin (x) dx =
# |
# |
#/
# /
# |
# -1 m+1 n-1 n - 1 | m+2 n-2
# ________ cos (x) sin (x) + _______ | cos (x) sin (x) dx
# |
# m + 1 m + 1 |
# /
res = (Rational(-1, m + 1) * cos(x)**(m + 1) * sin(x)**(n - 1) +
Rational(n - 1, m + 1) *
trigintegrate(cos(x)**(m + 2)*sin(x)**(n - 2), x))
elif m_:
# 2k 2 k i 2i
# S = (1 - C ) = sum(i, (-) * B(k, i) * C )
if n > 0:
# / /
# | |
# | m n | -m n
# | cos (x)*sin (x) dx or | cos (x) * sin (x) dx
# | |
# / /
#
# |m| > |n| ; m, n >0 ; m, n belong to Z - {0}
# n 2
# sin (x) term is expanded here in terms of cos (x),
# and then integrated.
#
for i in range(0, n//2 + 1):
res += (S.NegativeOne**i * binomial(n//2, i) *
_cos_pow_integrate(m + 2*i, x))
elif n == 0:
# /
# |
# | 1
# | _ _ _
# | m
# | cos (x)
# /
#
res = _cos_pow_integrate(m, x)
else:
# n < 0 , |m| > |n|
# /
# |
# | m n
# | cos (x) sin (x) dx =
# |
# |
#/
# /
# |
# 1 m-1 n+1 m - 1 | m-2 n+2
# _______ cos (x) sin (x) + _______ | cos (x) sin (x) dx
# |
# n + 1 n + 1 |
# /
res = (Rational(1, n + 1) * cos(x)**(m - 1)*sin(x)**(n + 1) +
Rational(m - 1, n + 1) *
trigintegrate(cos(x)**(m - 2)*sin(x)**(n + 2), x))
else:
if m == n:
##Substitute sin(2x)/2 for sin(x)cos(x) and then Integrate.
res = integrate((sin(2*x)*S.Half)**m, x)
elif (m == -n):
if n < 0:
# Same as the scheme described above.
# the function argument to integrate in the end will
# be 1, this cannot be integrated by trigintegrate.
# Hence use sympy.integrals.integrate.
res = (Rational(1, n + 1) * cos(x)**(m - 1) * sin(x)**(n + 1) +
Rational(m - 1, n + 1) *
integrate(cos(x)**(m - 2) * sin(x)**(n + 2), x))
else:
res = (Rational(-1, m + 1) * cos(x)**(m + 1) * sin(x)**(n - 1) +
Rational(n - 1, m + 1) *
integrate(cos(x)**(m + 2)*sin(x)**(n - 2), x))
if conds == 'piecewise':
return Piecewise((res.subs(x, a*x) / a, Ne(a, 0)), (zz, True))
return res.subs(x, a*x) / a
def _sin_pow_integrate(n, x):
if n > 0:
if n == 1:
#Recursion break
return -cos(x)
# n > 0
# / /
# | |
# | n -1 n-1 n - 1 | n-2
# | sin (x) dx = ______ cos (x) sin (x) + _______ | sin (x) dx
# | |
# | n n |
#/ /
#
#
return (Rational(-1, n) * cos(x) * sin(x)**(n - 1) +
Rational(n - 1, n) * _sin_pow_integrate(n - 2, x))
if n < 0:
if n == -1:
##Make sure this does not come back here again.
##Recursion breaks here or at n==0.
return trigintegrate(1/sin(x), x)
# n < 0
# / /
# | |
# | n 1 n+1 n + 2 | n+2
# | sin (x) dx = _______ cos (x) sin (x) + _______ | sin (x) dx
# | |
# | n + 1 n + 1 |
#/ /
#
return (Rational(1, n + 1) * cos(x) * sin(x)**(n + 1) +
Rational(n + 2, n + 1) * _sin_pow_integrate(n + 2, x))
else:
#n == 0
#Recursion break.
return x
def _cos_pow_integrate(n, x):
if n > 0:
if n == 1:
#Recursion break.
return sin(x)
# n > 0
# / /
# | |
# | n 1 n-1 n - 1 | n-2
# | sin (x) dx = ______ sin (x) cos (x) + _______ | cos (x) dx
# | |
# | n n |
#/ /
#
return (Rational(1, n) * sin(x) * cos(x)**(n - 1) +
Rational(n - 1, n) * _cos_pow_integrate(n - 2, x))
if n < 0:
if n == -1:
##Recursion break
return trigintegrate(1/cos(x), x)
# n < 0
# / /
# | |
# | n -1 n+1 n + 2 | n+2
# | cos (x) dx = _______ sin (x) cos (x) + _______ | cos (x) dx
# | |
# | n + 1 n + 1 |
#/ /
#
return (Rational(-1, n + 1) * sin(x) * cos(x)**(n + 1) +
Rational(n + 2, n + 1) * _cos_pow_integrate(n + 2, x))
else:
# n == 0
#Recursion Break.
return x