123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445 |
- """ Elliptic Integrals. """
- from sympy.core import S, pi, I, Rational
- from sympy.core.function import Function, ArgumentIndexError
- from sympy.core.symbol import Dummy
- from sympy.functions.elementary.complexes import sign
- from sympy.functions.elementary.hyperbolic import atanh
- from sympy.functions.elementary.miscellaneous import sqrt
- from sympy.functions.elementary.trigonometric import sin, tan
- from sympy.functions.special.gamma_functions import gamma
- from sympy.functions.special.hyper import hyper, meijerg
- class elliptic_k(Function):
- r"""
- The complete elliptic integral of the first kind, defined by
- .. math:: K(m) = F\left(\tfrac{\pi}{2}\middle| m\right)
- where $F\left(z\middle| m\right)$ is the Legendre incomplete
- elliptic integral of the first kind.
- Explanation
- ===========
- The function $K(m)$ is a single-valued function on the complex
- plane with branch cut along the interval $(1, \infty)$.
- Note that our notation defines the incomplete elliptic integral
- in terms of the parameter $m$ instead of the elliptic modulus
- (eccentricity) $k$.
- In this case, the parameter $m$ is defined as $m=k^2$.
- Examples
- ========
- >>> from sympy import elliptic_k, I
- >>> from sympy.abc import m
- >>> elliptic_k(0)
- pi/2
- >>> elliptic_k(1.0 + I)
- 1.50923695405127 + 0.625146415202697*I
- >>> elliptic_k(m).series(n=3)
- pi/2 + pi*m/8 + 9*pi*m**2/128 + O(m**3)
- See Also
- ========
- elliptic_f
- References
- ==========
- .. [1] https://en.wikipedia.org/wiki/Elliptic_integrals
- .. [2] https://functions.wolfram.com/EllipticIntegrals/EllipticK
- """
- @classmethod
- def eval(cls, m):
- if m.is_zero:
- return pi*S.Half
- elif m is S.Half:
- return 8*pi**Rational(3, 2)/gamma(Rational(-1, 4))**2
- elif m is S.One:
- return S.ComplexInfinity
- elif m is S.NegativeOne:
- return gamma(Rational(1, 4))**2/(4*sqrt(2*pi))
- elif m in (S.Infinity, S.NegativeInfinity, I*S.Infinity,
- I*S.NegativeInfinity, S.ComplexInfinity):
- return S.Zero
- def fdiff(self, argindex=1):
- m = self.args[0]
- return (elliptic_e(m) - (1 - m)*elliptic_k(m))/(2*m*(1 - m))
- def _eval_conjugate(self):
- m = self.args[0]
- if (m.is_real and (m - 1).is_positive) is False:
- return self.func(m.conjugate())
- def _eval_nseries(self, x, n, logx, cdir=0):
- from sympy.simplify import hyperexpand
- return hyperexpand(self.rewrite(hyper)._eval_nseries(x, n=n, logx=logx))
- def _eval_rewrite_as_hyper(self, m, **kwargs):
- return pi*S.Half*hyper((S.Half, S.Half), (S.One,), m)
- def _eval_rewrite_as_meijerg(self, m, **kwargs):
- return meijerg(((S.Half, S.Half), []), ((S.Zero,), (S.Zero,)), -m)/2
- def _eval_is_zero(self):
- m = self.args[0]
- if m.is_infinite:
- return True
- def _eval_rewrite_as_Integral(self, *args):
- from sympy.integrals.integrals import Integral
- t = Dummy('t')
- m = self.args[0]
- return Integral(1/sqrt(1 - m*sin(t)**2), (t, 0, pi/2))
- class elliptic_f(Function):
- r"""
- The Legendre incomplete elliptic integral of the first
- kind, defined by
- .. math:: F\left(z\middle| m\right) =
- \int_0^z \frac{dt}{\sqrt{1 - m \sin^2 t}}
- Explanation
- ===========
- This function reduces to a complete elliptic integral of
- the first kind, $K(m)$, when $z = \pi/2$.
- Note that our notation defines the incomplete elliptic integral
- in terms of the parameter $m$ instead of the elliptic modulus
- (eccentricity) $k$.
- In this case, the parameter $m$ is defined as $m=k^2$.
- Examples
- ========
- >>> from sympy import elliptic_f, I
- >>> from sympy.abc import z, m
- >>> elliptic_f(z, m).series(z)
- z + z**5*(3*m**2/40 - m/30) + m*z**3/6 + O(z**6)
- >>> elliptic_f(3.0 + I/2, 1.0 + I)
- 2.909449841483 + 1.74720545502474*I
- See Also
- ========
- elliptic_k
- References
- ==========
- .. [1] https://en.wikipedia.org/wiki/Elliptic_integrals
- .. [2] https://functions.wolfram.com/EllipticIntegrals/EllipticF
- """
- @classmethod
- def eval(cls, z, m):
- if z.is_zero:
- return S.Zero
- if m.is_zero:
- return z
- k = 2*z/pi
- if k.is_integer:
- return k*elliptic_k(m)
- elif m in (S.Infinity, S.NegativeInfinity):
- return S.Zero
- elif z.could_extract_minus_sign():
- return -elliptic_f(-z, m)
- def fdiff(self, argindex=1):
- z, m = self.args
- fm = sqrt(1 - m*sin(z)**2)
- if argindex == 1:
- return 1/fm
- elif argindex == 2:
- return (elliptic_e(z, m)/(2*m*(1 - m)) - elliptic_f(z, m)/(2*m) -
- sin(2*z)/(4*(1 - m)*fm))
- raise ArgumentIndexError(self, argindex)
- def _eval_conjugate(self):
- z, m = self.args
- if (m.is_real and (m - 1).is_positive) is False:
- return self.func(z.conjugate(), m.conjugate())
- def _eval_rewrite_as_Integral(self, *args):
- from sympy.integrals.integrals import Integral
- t = Dummy('t')
- z, m = self.args[0], self.args[1]
- return Integral(1/(sqrt(1 - m*sin(t)**2)), (t, 0, z))
- def _eval_is_zero(self):
- z, m = self.args
- if z.is_zero:
- return True
- if m.is_extended_real and m.is_infinite:
- return True
- class elliptic_e(Function):
- r"""
- Called with two arguments $z$ and $m$, evaluates the
- incomplete elliptic integral of the second kind, defined by
- .. math:: E\left(z\middle| m\right) = \int_0^z \sqrt{1 - m \sin^2 t} dt
- Called with a single argument $m$, evaluates the Legendre complete
- elliptic integral of the second kind
- .. math:: E(m) = E\left(\tfrac{\pi}{2}\middle| m\right)
- Explanation
- ===========
- The function $E(m)$ is a single-valued function on the complex
- plane with branch cut along the interval $(1, \infty)$.
- Note that our notation defines the incomplete elliptic integral
- in terms of the parameter $m$ instead of the elliptic modulus
- (eccentricity) $k$.
- In this case, the parameter $m$ is defined as $m=k^2$.
- Examples
- ========
- >>> from sympy import elliptic_e, I
- >>> from sympy.abc import z, m
- >>> elliptic_e(z, m).series(z)
- z + z**5*(-m**2/40 + m/30) - m*z**3/6 + O(z**6)
- >>> elliptic_e(m).series(n=4)
- pi/2 - pi*m/8 - 3*pi*m**2/128 - 5*pi*m**3/512 + O(m**4)
- >>> elliptic_e(1 + I, 2 - I/2).n()
- 1.55203744279187 + 0.290764986058437*I
- >>> elliptic_e(0)
- pi/2
- >>> elliptic_e(2.0 - I)
- 0.991052601328069 + 0.81879421395609*I
- References
- ==========
- .. [1] https://en.wikipedia.org/wiki/Elliptic_integrals
- .. [2] https://functions.wolfram.com/EllipticIntegrals/EllipticE2
- .. [3] https://functions.wolfram.com/EllipticIntegrals/EllipticE
- """
- @classmethod
- def eval(cls, m, z=None):
- if z is not None:
- z, m = m, z
- k = 2*z/pi
- if m.is_zero:
- return z
- if z.is_zero:
- return S.Zero
- elif k.is_integer:
- return k*elliptic_e(m)
- elif m in (S.Infinity, S.NegativeInfinity):
- return S.ComplexInfinity
- elif z.could_extract_minus_sign():
- return -elliptic_e(-z, m)
- else:
- if m.is_zero:
- return pi/2
- elif m is S.One:
- return S.One
- elif m is S.Infinity:
- return I*S.Infinity
- elif m is S.NegativeInfinity:
- return S.Infinity
- elif m is S.ComplexInfinity:
- return S.ComplexInfinity
- def fdiff(self, argindex=1):
- if len(self.args) == 2:
- z, m = self.args
- if argindex == 1:
- return sqrt(1 - m*sin(z)**2)
- elif argindex == 2:
- return (elliptic_e(z, m) - elliptic_f(z, m))/(2*m)
- else:
- m = self.args[0]
- if argindex == 1:
- return (elliptic_e(m) - elliptic_k(m))/(2*m)
- raise ArgumentIndexError(self, argindex)
- def _eval_conjugate(self):
- if len(self.args) == 2:
- z, m = self.args
- if (m.is_real and (m - 1).is_positive) is False:
- return self.func(z.conjugate(), m.conjugate())
- else:
- m = self.args[0]
- if (m.is_real and (m - 1).is_positive) is False:
- return self.func(m.conjugate())
- def _eval_nseries(self, x, n, logx, cdir=0):
- from sympy.simplify import hyperexpand
- if len(self.args) == 1:
- return hyperexpand(self.rewrite(hyper)._eval_nseries(x, n=n, logx=logx))
- return super()._eval_nseries(x, n=n, logx=logx)
- def _eval_rewrite_as_hyper(self, *args, **kwargs):
- if len(args) == 1:
- m = args[0]
- return (pi/2)*hyper((Rational(-1, 2), S.Half), (S.One,), m)
- def _eval_rewrite_as_meijerg(self, *args, **kwargs):
- if len(args) == 1:
- m = args[0]
- return -meijerg(((S.Half, Rational(3, 2)), []), \
- ((S.Zero,), (S.Zero,)), -m)/4
- def _eval_rewrite_as_Integral(self, *args):
- from sympy.integrals.integrals import Integral
- z, m = (pi/2, self.args[0]) if len(self.args) == 1 else self.args
- t = Dummy('t')
- return Integral(sqrt(1 - m*sin(t)**2), (t, 0, z))
- class elliptic_pi(Function):
- r"""
- Called with three arguments $n$, $z$ and $m$, evaluates the
- Legendre incomplete elliptic integral of the third kind, defined by
- .. math:: \Pi\left(n; z\middle| m\right) = \int_0^z \frac{dt}
- {\left(1 - n \sin^2 t\right) \sqrt{1 - m \sin^2 t}}
- Called with two arguments $n$ and $m$, evaluates the complete
- elliptic integral of the third kind:
- .. math:: \Pi\left(n\middle| m\right) =
- \Pi\left(n; \tfrac{\pi}{2}\middle| m\right)
- Explanation
- ===========
- Note that our notation defines the incomplete elliptic integral
- in terms of the parameter $m$ instead of the elliptic modulus
- (eccentricity) $k$.
- In this case, the parameter $m$ is defined as $m=k^2$.
- Examples
- ========
- >>> from sympy import elliptic_pi, I
- >>> from sympy.abc import z, n, m
- >>> elliptic_pi(n, z, m).series(z, n=4)
- z + z**3*(m/6 + n/3) + O(z**4)
- >>> elliptic_pi(0.5 + I, 1.0 - I, 1.2)
- 2.50232379629182 - 0.760939574180767*I
- >>> elliptic_pi(0, 0)
- pi/2
- >>> elliptic_pi(1.0 - I/3, 2.0 + I)
- 3.29136443417283 + 0.32555634906645*I
- References
- ==========
- .. [1] https://en.wikipedia.org/wiki/Elliptic_integrals
- .. [2] https://functions.wolfram.com/EllipticIntegrals/EllipticPi3
- .. [3] https://functions.wolfram.com/EllipticIntegrals/EllipticPi
- """
- @classmethod
- def eval(cls, n, m, z=None):
- if z is not None:
- n, z, m = n, m, z
- if n.is_zero:
- return elliptic_f(z, m)
- elif n is S.One:
- return (elliptic_f(z, m) +
- (sqrt(1 - m*sin(z)**2)*tan(z) -
- elliptic_e(z, m))/(1 - m))
- k = 2*z/pi
- if k.is_integer:
- return k*elliptic_pi(n, m)
- elif m.is_zero:
- return atanh(sqrt(n - 1)*tan(z))/sqrt(n - 1)
- elif n == m:
- return (elliptic_f(z, n) - elliptic_pi(1, z, n) +
- tan(z)/sqrt(1 - n*sin(z)**2))
- elif n in (S.Infinity, S.NegativeInfinity):
- return S.Zero
- elif m in (S.Infinity, S.NegativeInfinity):
- return S.Zero
- elif z.could_extract_minus_sign():
- return -elliptic_pi(n, -z, m)
- if n.is_zero:
- return elliptic_f(z, m)
- if m.is_extended_real and m.is_infinite or \
- n.is_extended_real and n.is_infinite:
- return S.Zero
- else:
- if n.is_zero:
- return elliptic_k(m)
- elif n is S.One:
- return S.ComplexInfinity
- elif m.is_zero:
- return pi/(2*sqrt(1 - n))
- elif m == S.One:
- return S.NegativeInfinity/sign(n - 1)
- elif n == m:
- return elliptic_e(n)/(1 - n)
- elif n in (S.Infinity, S.NegativeInfinity):
- return S.Zero
- elif m in (S.Infinity, S.NegativeInfinity):
- return S.Zero
- if n.is_zero:
- return elliptic_k(m)
- if m.is_extended_real and m.is_infinite or \
- n.is_extended_real and n.is_infinite:
- return S.Zero
- def _eval_conjugate(self):
- if len(self.args) == 3:
- n, z, m = self.args
- if (n.is_real and (n - 1).is_positive) is False and \
- (m.is_real and (m - 1).is_positive) is False:
- return self.func(n.conjugate(), z.conjugate(), m.conjugate())
- else:
- n, m = self.args
- return self.func(n.conjugate(), m.conjugate())
- def fdiff(self, argindex=1):
- if len(self.args) == 3:
- n, z, m = self.args
- fm, fn = sqrt(1 - m*sin(z)**2), 1 - n*sin(z)**2
- if argindex == 1:
- return (elliptic_e(z, m) + (m - n)*elliptic_f(z, m)/n +
- (n**2 - m)*elliptic_pi(n, z, m)/n -
- n*fm*sin(2*z)/(2*fn))/(2*(m - n)*(n - 1))
- elif argindex == 2:
- return 1/(fm*fn)
- elif argindex == 3:
- return (elliptic_e(z, m)/(m - 1) +
- elliptic_pi(n, z, m) -
- m*sin(2*z)/(2*(m - 1)*fm))/(2*(n - m))
- else:
- n, m = self.args
- if argindex == 1:
- return (elliptic_e(m) + (m - n)*elliptic_k(m)/n +
- (n**2 - m)*elliptic_pi(n, m)/n)/(2*(m - n)*(n - 1))
- elif argindex == 2:
- return (elliptic_e(m)/(m - 1) + elliptic_pi(n, m))/(2*(n - m))
- raise ArgumentIndexError(self, argindex)
- def _eval_rewrite_as_Integral(self, *args):
- from sympy.integrals.integrals import Integral
- if len(self.args) == 2:
- n, m, z = self.args[0], self.args[1], pi/2
- else:
- n, z, m = self.args
- t = Dummy('t')
- return Integral(1/((1 - n*sin(t)**2)*sqrt(1 - m*sin(t)**2)), (t, 0, z))
|