123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431 |
- r"""
- Elliptic functions historically comprise the elliptic integrals
- and their inverses, and originate from the problem of computing the
- arc length of an ellipse. From a more modern point of view,
- an elliptic function is defined as a doubly periodic function, i.e.
- a function which satisfies
- .. math ::
- f(z + 2 \omega_1) = f(z + 2 \omega_2) = f(z)
- for some half-periods `\omega_1, \omega_2` with
- `\mathrm{Im}[\omega_1 / \omega_2] > 0`. The canonical elliptic
- functions are the Jacobi elliptic functions. More broadly, this section
- includes quasi-doubly periodic functions (such as the Jacobi theta
- functions) and other functions useful in the study of elliptic functions.
- Many different conventions for the arguments of
- elliptic functions are in use. It is even standard to use
- different parameterizations for different functions in the same
- text or software (and mpmath is no exception).
- The usual parameters are the elliptic nome `q`, which usually
- must satisfy `|q| < 1`; the elliptic parameter `m` (an arbitrary
- complex number); the elliptic modulus `k` (an arbitrary complex
- number); and the half-period ratio `\tau`, which usually must
- satisfy `\mathrm{Im}[\tau] > 0`.
- These quantities can be expressed in terms of each other
- using the following relations:
- .. math ::
- m = k^2
- .. math ::
- \tau = i \frac{K(1-m)}{K(m)}
- .. math ::
- q = e^{i \pi \tau}
- .. math ::
- k = \frac{\vartheta_2^2(q)}{\vartheta_3^2(q)}
- In addition, an alternative definition is used for the nome in
- number theory, which we here denote by q-bar:
- .. math ::
- \bar{q} = q^2 = e^{2 i \pi \tau}
- For convenience, mpmath provides functions to convert
- between the various parameters (:func:`~mpmath.qfrom`, :func:`~mpmath.mfrom`,
- :func:`~mpmath.kfrom`, :func:`~mpmath.taufrom`, :func:`~mpmath.qbarfrom`).
- **References**
- 1. [AbramowitzStegun]_
- 2. [WhittakerWatson]_
- """
- from .functions import defun, defun_wrapped
- @defun_wrapped
- def eta(ctx, tau):
- r"""
- Returns the Dedekind eta function of tau in the upper half-plane.
- >>> from mpmath import *
- >>> mp.dps = 25; mp.pretty = True
- >>> eta(1j); gamma(0.25) / (2*pi**0.75)
- (0.7682254223260566590025942 + 0.0j)
- 0.7682254223260566590025942
- >>> tau = sqrt(2) + sqrt(5)*1j
- >>> eta(-1/tau); sqrt(-1j*tau) * eta(tau)
- (0.9022859908439376463573294 + 0.07985093673948098408048575j)
- (0.9022859908439376463573295 + 0.07985093673948098408048575j)
- >>> eta(tau+1); exp(pi*1j/12) * eta(tau)
- (0.4493066139717553786223114 + 0.3290014793877986663915939j)
- (0.4493066139717553786223114 + 0.3290014793877986663915939j)
- >>> f = lambda z: diff(eta, z) / eta(z)
- >>> chop(36*diff(f,tau)**2 - 24*diff(f,tau,2)*f(tau) + diff(f,tau,3))
- 0.0
- """
- if ctx.im(tau) <= 0.0:
- raise ValueError("eta is only defined in the upper half-plane")
- q = ctx.expjpi(tau/12)
- return q * ctx.qp(q**24)
- def nome(ctx, m):
- m = ctx.convert(m)
- if not m:
- return m
- if m == ctx.one:
- return m
- if ctx.isnan(m):
- return m
- if ctx.isinf(m):
- if m == ctx.ninf:
- return type(m)(-1)
- else:
- return ctx.mpc(-1)
- a = ctx.ellipk(ctx.one-m)
- b = ctx.ellipk(m)
- v = ctx.exp(-ctx.pi*a/b)
- if not ctx._im(m) and ctx._re(m) < 1:
- if ctx._is_real_type(m):
- return v.real
- else:
- return v.real + 0j
- elif m == 2:
- v = ctx.mpc(0, v.imag)
- return v
- @defun_wrapped
- def qfrom(ctx, q=None, m=None, k=None, tau=None, qbar=None):
- r"""
- Returns the elliptic nome `q`, given any of `q, m, k, \tau, \bar{q}`::
- >>> from mpmath import *
- >>> mp.dps = 25; mp.pretty = True
- >>> qfrom(q=0.25)
- 0.25
- >>> qfrom(m=mfrom(q=0.25))
- 0.25
- >>> qfrom(k=kfrom(q=0.25))
- 0.25
- >>> qfrom(tau=taufrom(q=0.25))
- (0.25 + 0.0j)
- >>> qfrom(qbar=qbarfrom(q=0.25))
- 0.25
- """
- if q is not None:
- return ctx.convert(q)
- if m is not None:
- return nome(ctx, m)
- if k is not None:
- return nome(ctx, ctx.convert(k)**2)
- if tau is not None:
- return ctx.expjpi(tau)
- if qbar is not None:
- return ctx.sqrt(qbar)
- @defun_wrapped
- def qbarfrom(ctx, q=None, m=None, k=None, tau=None, qbar=None):
- r"""
- Returns the number-theoretic nome `\bar q`, given any of
- `q, m, k, \tau, \bar{q}`::
- >>> from mpmath import *
- >>> mp.dps = 25; mp.pretty = True
- >>> qbarfrom(qbar=0.25)
- 0.25
- >>> qbarfrom(q=qfrom(qbar=0.25))
- 0.25
- >>> qbarfrom(m=extraprec(20)(mfrom)(qbar=0.25)) # ill-conditioned
- 0.25
- >>> qbarfrom(k=extraprec(20)(kfrom)(qbar=0.25)) # ill-conditioned
- 0.25
- >>> qbarfrom(tau=taufrom(qbar=0.25))
- (0.25 + 0.0j)
- """
- if qbar is not None:
- return ctx.convert(qbar)
- if q is not None:
- return ctx.convert(q) ** 2
- if m is not None:
- return nome(ctx, m) ** 2
- if k is not None:
- return nome(ctx, ctx.convert(k)**2) ** 2
- if tau is not None:
- return ctx.expjpi(2*tau)
- @defun_wrapped
- def taufrom(ctx, q=None, m=None, k=None, tau=None, qbar=None):
- r"""
- Returns the elliptic half-period ratio `\tau`, given any of
- `q, m, k, \tau, \bar{q}`::
- >>> from mpmath import *
- >>> mp.dps = 25; mp.pretty = True
- >>> taufrom(tau=0.5j)
- (0.0 + 0.5j)
- >>> taufrom(q=qfrom(tau=0.5j))
- (0.0 + 0.5j)
- >>> taufrom(m=mfrom(tau=0.5j))
- (0.0 + 0.5j)
- >>> taufrom(k=kfrom(tau=0.5j))
- (0.0 + 0.5j)
- >>> taufrom(qbar=qbarfrom(tau=0.5j))
- (0.0 + 0.5j)
- """
- if tau is not None:
- return ctx.convert(tau)
- if m is not None:
- m = ctx.convert(m)
- return ctx.j*ctx.ellipk(1-m)/ctx.ellipk(m)
- if k is not None:
- k = ctx.convert(k)
- return ctx.j*ctx.ellipk(1-k**2)/ctx.ellipk(k**2)
- if q is not None:
- return ctx.log(q) / (ctx.pi*ctx.j)
- if qbar is not None:
- qbar = ctx.convert(qbar)
- return ctx.log(qbar) / (2*ctx.pi*ctx.j)
- @defun_wrapped
- def kfrom(ctx, q=None, m=None, k=None, tau=None, qbar=None):
- r"""
- Returns the elliptic modulus `k`, given any of
- `q, m, k, \tau, \bar{q}`::
- >>> from mpmath import *
- >>> mp.dps = 25; mp.pretty = True
- >>> kfrom(k=0.25)
- 0.25
- >>> kfrom(m=mfrom(k=0.25))
- 0.25
- >>> kfrom(q=qfrom(k=0.25))
- 0.25
- >>> kfrom(tau=taufrom(k=0.25))
- (0.25 + 0.0j)
- >>> kfrom(qbar=qbarfrom(k=0.25))
- 0.25
- As `q \to 1` and `q \to -1`, `k` rapidly approaches
- `1` and `i \infty` respectively::
- >>> kfrom(q=0.75)
- 0.9999999999999899166471767
- >>> kfrom(q=-0.75)
- (0.0 + 7041781.096692038332790615j)
- >>> kfrom(q=1)
- 1
- >>> kfrom(q=-1)
- (0.0 + +infj)
- """
- if k is not None:
- return ctx.convert(k)
- if m is not None:
- return ctx.sqrt(m)
- if tau is not None:
- q = ctx.expjpi(tau)
- if qbar is not None:
- q = ctx.sqrt(qbar)
- if q == 1:
- return q
- if q == -1:
- return ctx.mpc(0,'inf')
- return (ctx.jtheta(2,0,q)/ctx.jtheta(3,0,q))**2
- @defun_wrapped
- def mfrom(ctx, q=None, m=None, k=None, tau=None, qbar=None):
- r"""
- Returns the elliptic parameter `m`, given any of
- `q, m, k, \tau, \bar{q}`::
- >>> from mpmath import *
- >>> mp.dps = 25; mp.pretty = True
- >>> mfrom(m=0.25)
- 0.25
- >>> mfrom(q=qfrom(m=0.25))
- 0.25
- >>> mfrom(k=kfrom(m=0.25))
- 0.25
- >>> mfrom(tau=taufrom(m=0.25))
- (0.25 + 0.0j)
- >>> mfrom(qbar=qbarfrom(m=0.25))
- 0.25
- As `q \to 1` and `q \to -1`, `m` rapidly approaches
- `1` and `-\infty` respectively::
- >>> mfrom(q=0.75)
- 0.9999999999999798332943533
- >>> mfrom(q=-0.75)
- -49586681013729.32611558353
- >>> mfrom(q=1)
- 1.0
- >>> mfrom(q=-1)
- -inf
- The inverse nome as a function of `q` has an integer
- Taylor series expansion::
- >>> taylor(lambda q: mfrom(q), 0, 7)
- [0.0, 16.0, -128.0, 704.0, -3072.0, 11488.0, -38400.0, 117632.0]
- """
- if m is not None:
- return m
- if k is not None:
- return k**2
- if tau is not None:
- q = ctx.expjpi(tau)
- if qbar is not None:
- q = ctx.sqrt(qbar)
- if q == 1:
- return ctx.convert(q)
- if q == -1:
- return q*ctx.inf
- v = (ctx.jtheta(2,0,q)/ctx.jtheta(3,0,q))**4
- if ctx._is_real_type(q) and q < 0:
- v = v.real
- return v
- jacobi_spec = {
- 'sn' : ([3],[2],[1],[4], 'sin', 'tanh'),
- 'cn' : ([4],[2],[2],[4], 'cos', 'sech'),
- 'dn' : ([4],[3],[3],[4], '1', 'sech'),
- 'ns' : ([2],[3],[4],[1], 'csc', 'coth'),
- 'nc' : ([2],[4],[4],[2], 'sec', 'cosh'),
- 'nd' : ([3],[4],[4],[3], '1', 'cosh'),
- 'sc' : ([3],[4],[1],[2], 'tan', 'sinh'),
- 'sd' : ([3,3],[2,4],[1],[3], 'sin', 'sinh'),
- 'cd' : ([3],[2],[2],[3], 'cos', '1'),
- 'cs' : ([4],[3],[2],[1], 'cot', 'csch'),
- 'dc' : ([2],[3],[3],[2], 'sec', '1'),
- 'ds' : ([2,4],[3,3],[3],[1], 'csc', 'csch'),
- 'cc' : None,
- 'ss' : None,
- 'nn' : None,
- 'dd' : None
- }
- @defun
- def ellipfun(ctx, kind, u=None, m=None, q=None, k=None, tau=None):
- try:
- S = jacobi_spec[kind]
- except KeyError:
- raise ValueError("First argument must be a two-character string "
- "containing 's', 'c', 'd' or 'n', e.g.: 'sn'")
- if u is None:
- def f(*args, **kwargs):
- return ctx.ellipfun(kind, *args, **kwargs)
- f.__name__ = kind
- return f
- prec = ctx.prec
- try:
- ctx.prec += 10
- u = ctx.convert(u)
- q = ctx.qfrom(m=m, q=q, k=k, tau=tau)
- if S is None:
- v = ctx.one + 0*q*u
- elif q == ctx.zero:
- if S[4] == '1': v = ctx.one
- else: v = getattr(ctx, S[4])(u)
- v += 0*q*u
- elif q == ctx.one:
- if S[5] == '1': v = ctx.one
- else: v = getattr(ctx, S[5])(u)
- v += 0*q*u
- else:
- t = u / ctx.jtheta(3, 0, q)**2
- v = ctx.one
- for a in S[0]: v *= ctx.jtheta(a, 0, q)
- for b in S[1]: v /= ctx.jtheta(b, 0, q)
- for c in S[2]: v *= ctx.jtheta(c, t, q)
- for d in S[3]: v /= ctx.jtheta(d, t, q)
- finally:
- ctx.prec = prec
- return +v
- @defun_wrapped
- def kleinj(ctx, tau=None, **kwargs):
- r"""
- Evaluates the Klein j-invariant, which is a modular function defined for
- `\tau` in the upper half-plane as
- .. math ::
- J(\tau) = \frac{g_2^3(\tau)}{g_2^3(\tau) - 27 g_3^2(\tau)}
- where `g_2` and `g_3` are the modular invariants of the Weierstrass
- elliptic function,
- .. math ::
- g_2(\tau) = 60 \sum_{(m,n) \in \mathbb{Z}^2 \setminus (0,0)} (m \tau+n)^{-4}
- g_3(\tau) = 140 \sum_{(m,n) \in \mathbb{Z}^2 \setminus (0,0)} (m \tau+n)^{-6}.
- An alternative, common notation is that of the j-function
- `j(\tau) = 1728 J(\tau)`.
- **Plots**
- .. literalinclude :: /plots/kleinj.py
- .. image :: /plots/kleinj.png
- .. literalinclude :: /plots/kleinj2.py
- .. image :: /plots/kleinj2.png
- **Examples**
- Verifying the functional equation `J(\tau) = J(\tau+1) = J(-\tau^{-1})`::
- >>> from mpmath import *
- >>> mp.dps = 25; mp.pretty = True
- >>> tau = 0.625+0.75*j
- >>> tau = 0.625+0.75*j
- >>> kleinj(tau)
- (-0.1507492166511182267125242 + 0.07595948379084571927228948j)
- >>> kleinj(tau+1)
- (-0.1507492166511182267125242 + 0.07595948379084571927228948j)
- >>> kleinj(-1/tau)
- (-0.1507492166511182267125242 + 0.07595948379084571927228946j)
- The j-function has a famous Laurent series expansion in terms of the nome
- `\bar{q}`, `j(\tau) = \bar{q}^{-1} + 744 + 196884\bar{q} + \ldots`::
- >>> mp.dps = 15
- >>> taylor(lambda q: 1728*q*kleinj(qbar=q), 0, 5, singular=True)
- [1.0, 744.0, 196884.0, 21493760.0, 864299970.0, 20245856256.0]
- The j-function admits exact evaluation at special algebraic points
- related to the Heegner numbers 1, 2, 3, 7, 11, 19, 43, 67, 163::
- >>> @extraprec(10)
- ... def h(n):
- ... v = (1+sqrt(n)*j)
- ... if n > 2:
- ... v *= 0.5
- ... return v
- ...
- >>> mp.dps = 25
- >>> for n in [1,2,3,7,11,19,43,67,163]:
- ... n, chop(1728*kleinj(h(n)))
- ...
- (1, 1728.0)
- (2, 8000.0)
- (3, 0.0)
- (7, -3375.0)
- (11, -32768.0)
- (19, -884736.0)
- (43, -884736000.0)
- (67, -147197952000.0)
- (163, -262537412640768000.0)
- Also at other special points, the j-function assumes explicit
- algebraic values, e.g.::
- >>> chop(1728*kleinj(j*sqrt(5)))
- 1264538.909475140509320227
- >>> identify(cbrt(_)) # note: not simplified
- '((100+sqrt(13520))/2)'
- >>> (50+26*sqrt(5))**3
- 1264538.909475140509320227
- """
- q = ctx.qfrom(tau=tau, **kwargs)
- t2 = ctx.jtheta(2,0,q)
- t3 = ctx.jtheta(3,0,q)
- t4 = ctx.jtheta(4,0,q)
- P = (t2**8 + t3**8 + t4**8)**3
- Q = 54*(t2*t3*t4)**8
- return P/Q
- def RF_calc(ctx, x, y, z, r):
- if y == z: return RC_calc(ctx, x, y, r)
- if x == z: return RC_calc(ctx, y, x, r)
- if x == y: return RC_calc(ctx, z, x, r)
- if not (ctx.isnormal(x) and ctx.isnormal(y) and ctx.isnormal(z)):
- if ctx.isnan(x) or ctx.isnan(y) or ctx.isnan(z):
- return x*y*z
- if ctx.isinf(x) or ctx.isinf(y) or ctx.isinf(z):
- return ctx.zero
- xm,ym,zm = x,y,z
- A0 = Am = (x+y+z)/3
- Q = ctx.root(3*r, -6) * max(abs(A0-x),abs(A0-y),abs(A0-z))
- g = ctx.mpf(0.25)
- pow4 = ctx.one
- while 1:
- xs = ctx.sqrt(xm)
- ys = ctx.sqrt(ym)
- zs = ctx.sqrt(zm)
- lm = xs*ys + xs*zs + ys*zs
- Am1 = (Am+lm)*g
- xm, ym, zm = (xm+lm)*g, (ym+lm)*g, (zm+lm)*g
- if pow4 * Q < abs(Am):
- break
- Am = Am1
- pow4 *= g
- t = pow4/Am
- X = (A0-x)*t
- Y = (A0-y)*t
- Z = -X-Y
- E2 = X*Y-Z**2
- E3 = X*Y*Z
- return ctx.power(Am,-0.5) * (9240-924*E2+385*E2**2+660*E3-630*E2*E3)/9240
- def RC_calc(ctx, x, y, r, pv=True):
- if not (ctx.isnormal(x) and ctx.isnormal(y)):
- if ctx.isinf(x) or ctx.isinf(y):
- return 1/(x*y)
- if y == 0:
- return ctx.inf
- if x == 0:
- return ctx.pi / ctx.sqrt(y) / 2
- raise ValueError
- # Cauchy principal value
- if pv and ctx._im(y) == 0 and ctx._re(y) < 0:
- return ctx.sqrt(x/(x-y)) * RC_calc(ctx, x-y, -y, r)
- if x == y:
- return 1/ctx.sqrt(x)
- extraprec = 2*max(0,-ctx.mag(x-y)+ctx.mag(x))
- ctx.prec += extraprec
- if ctx._is_real_type(x) and ctx._is_real_type(y):
- x = ctx._re(x)
- y = ctx._re(y)
- a = ctx.sqrt(x/y)
- if x < y:
- b = ctx.sqrt(y-x)
- v = ctx.acos(a)/b
- else:
- b = ctx.sqrt(x-y)
- v = ctx.acosh(a)/b
- else:
- sx = ctx.sqrt(x)
- sy = ctx.sqrt(y)
- v = ctx.acos(sx/sy)/(ctx.sqrt((1-x/y))*sy)
- ctx.prec -= extraprec
- return v
- def RJ_calc(ctx, x, y, z, p, r, integration):
- """
- With integration == 0, computes RJ only using Carlson's algorithm
- (may be wrong for some values).
- With integration == 1, uses an initial integration to make sure
- Carlson's algorithm is correct.
- With integration == 2, uses only integration.
- """
- if not (ctx.isnormal(x) and ctx.isnormal(y) and \
- ctx.isnormal(z) and ctx.isnormal(p)):
- if ctx.isnan(x) or ctx.isnan(y) or ctx.isnan(z) or ctx.isnan(p):
- return x*y*z
- if ctx.isinf(x) or ctx.isinf(y) or ctx.isinf(z) or ctx.isinf(p):
- return ctx.zero
- if not p:
- return ctx.inf
- if (not x) + (not y) + (not z) > 1:
- return ctx.inf
- # Check conditions and fall back on integration for argument
- # reduction if needed. The following conditions might be needlessly
- # restrictive.
- initial_integral = ctx.zero
- if integration >= 1:
- ok = (x.real >= 0 and y.real >= 0 and z.real >= 0 and p.real > 0)
- if not ok:
- if x == p or y == p or z == p:
- ok = True
- if not ok:
- if p.imag != 0 or p.real >= 0:
- if (x.imag == 0 and x.real >= 0 and ctx.conj(y) == z):
- ok = True
- if (y.imag == 0 and y.real >= 0 and ctx.conj(x) == z):
- ok = True
- if (z.imag == 0 and z.real >= 0 and ctx.conj(x) == y):
- ok = True
- if not ok or (integration == 2):
- N = ctx.ceil(-min(x.real, y.real, z.real, p.real)) + 1
- # Integrate around any singularities
- if all((t.imag >= 0 or t.real > 0) for t in [x, y, z, p]):
- margin = ctx.j
- elif all((t.imag < 0 or t.real > 0) for t in [x, y, z, p]):
- margin = -ctx.j
- else:
- margin = 1
- # Go through the upper half-plane, but low enough that any
- # parameter starting in the lower plane doesn't cross the
- # branch cut
- for t in [x, y, z, p]:
- if t.imag >= 0 or t.real > 0:
- continue
- margin = min(margin, abs(t.imag) * 0.5)
- margin *= ctx.j
- N += margin
- F = lambda t: 1/(ctx.sqrt(t+x)*ctx.sqrt(t+y)*ctx.sqrt(t+z)*(t+p))
- if integration == 2:
- return 1.5 * ctx.quadsubdiv(F, [0, N, ctx.inf])
- initial_integral = 1.5 * ctx.quadsubdiv(F, [0, N])
- x += N; y += N; z += N; p += N
- xm,ym,zm,pm = x,y,z,p
- A0 = Am = (x + y + z + 2*p)/5
- delta = (p-x)*(p-y)*(p-z)
- Q = ctx.root(0.25*r, -6) * max(abs(A0-x),abs(A0-y),abs(A0-z),abs(A0-p))
- g = ctx.mpf(0.25)
- pow4 = ctx.one
- S = 0
- while 1:
- sx = ctx.sqrt(xm)
- sy = ctx.sqrt(ym)
- sz = ctx.sqrt(zm)
- sp = ctx.sqrt(pm)
- lm = sx*sy + sx*sz + sy*sz
- Am1 = (Am+lm)*g
- xm = (xm+lm)*g; ym = (ym+lm)*g; zm = (zm+lm)*g; pm = (pm+lm)*g
- dm = (sp+sx) * (sp+sy) * (sp+sz)
- em = delta * pow4**3 / dm**2
- if pow4 * Q < abs(Am):
- break
- T = RC_calc(ctx, ctx.one, ctx.one+em, r) * pow4 / dm
- S += T
- pow4 *= g
- Am = Am1
- t = pow4 / Am
- X = (A0-x)*t
- Y = (A0-y)*t
- Z = (A0-z)*t
- P = (-X-Y-Z)/2
- E2 = X*Y + X*Z + Y*Z - 3*P**2
- E3 = X*Y*Z + 2*E2*P + 4*P**3
- E4 = (2*X*Y*Z + E2*P + 3*P**3)*P
- E5 = X*Y*Z*P**2
- P = 24024 - 5148*E2 + 2457*E2**2 + 4004*E3 - 4158*E2*E3 - 3276*E4 + 2772*E5
- Q = 24024
- v1 = pow4 * ctx.power(Am, -1.5) * P/Q
- v2 = 6*S
- return initial_integral + v1 + v2
- @defun
- def elliprf(ctx, x, y, z):
- r"""
- Evaluates the Carlson symmetric elliptic integral of the first kind
- .. math ::
- R_F(x,y,z) = \frac{1}{2}
- \int_0^{\infty} \frac{dt}{\sqrt{(t+x)(t+y)(t+z)}}
- which is defined for `x,y,z \notin (-\infty,0)`, and with
- at most one of `x,y,z` being zero.
- For real `x,y,z \ge 0`, the principal square root is taken in the integrand.
- For complex `x,y,z`, the principal square root is taken as `t \to \infty`
- and as `t \to 0` non-principal branches are chosen as necessary so as to
- make the integrand continuous.
- **Examples**
- Some basic values and limits::
- >>> from mpmath import *
- >>> mp.dps = 25; mp.pretty = True
- >>> elliprf(0,1,1); pi/2
- 1.570796326794896619231322
- 1.570796326794896619231322
- >>> elliprf(0,1,inf)
- 0.0
- >>> elliprf(1,1,1)
- 1.0
- >>> elliprf(2,2,2)**2
- 0.5
- >>> elliprf(1,0,0); elliprf(0,0,1); elliprf(0,1,0); elliprf(0,0,0)
- +inf
- +inf
- +inf
- +inf
- Representing complete elliptic integrals in terms of `R_F`::
- >>> m = mpf(0.75)
- >>> ellipk(m); elliprf(0,1-m,1)
- 2.156515647499643235438675
- 2.156515647499643235438675
- >>> ellipe(m); elliprf(0,1-m,1)-m*elliprd(0,1-m,1)/3
- 1.211056027568459524803563
- 1.211056027568459524803563
- Some symmetries and argument transformations::
- >>> x,y,z = 2,3,4
- >>> elliprf(x,y,z); elliprf(y,x,z); elliprf(z,y,x)
- 0.5840828416771517066928492
- 0.5840828416771517066928492
- 0.5840828416771517066928492
- >>> k = mpf(100000)
- >>> elliprf(k*x,k*y,k*z); k**(-0.5) * elliprf(x,y,z)
- 0.001847032121923321253219284
- 0.001847032121923321253219284
- >>> l = sqrt(x*y) + sqrt(y*z) + sqrt(z*x)
- >>> elliprf(x,y,z); 2*elliprf(x+l,y+l,z+l)
- 0.5840828416771517066928492
- 0.5840828416771517066928492
- >>> elliprf((x+l)/4,(y+l)/4,(z+l)/4)
- 0.5840828416771517066928492
- Comparing with numerical integration::
- >>> x,y,z = 2,3,4
- >>> elliprf(x,y,z)
- 0.5840828416771517066928492
- >>> f = lambda t: 0.5*((t+x)*(t+y)*(t+z))**(-0.5)
- >>> q = extradps(25)(quad)
- >>> q(f, [0,inf])
- 0.5840828416771517066928492
- With the following arguments, the square root in the integrand becomes
- discontinuous at `t = 1/2` if the principal branch is used. To obtain
- the right value, `-\sqrt{r}` must be taken instead of `\sqrt{r}`
- on `t \in (0, 1/2)`::
- >>> x,y,z = j-1,j,0
- >>> elliprf(x,y,z)
- (0.7961258658423391329305694 - 1.213856669836495986430094j)
- >>> -q(f, [0,0.5]) + q(f, [0.5,inf])
- (0.7961258658423391329305694 - 1.213856669836495986430094j)
- The so-called *first lemniscate constant*, a transcendental number::
- >>> elliprf(0,1,2)
- 1.31102877714605990523242
- >>> extradps(25)(quad)(lambda t: 1/sqrt(1-t**4), [0,1])
- 1.31102877714605990523242
- >>> gamma('1/4')**2/(4*sqrt(2*pi))
- 1.31102877714605990523242
- **References**
- 1. [Carlson]_
- 2. [DLMF]_ Chapter 19. Elliptic Integrals
- """
- x = ctx.convert(x)
- y = ctx.convert(y)
- z = ctx.convert(z)
- prec = ctx.prec
- try:
- ctx.prec += 20
- tol = ctx.eps * 2**10
- v = RF_calc(ctx, x, y, z, tol)
- finally:
- ctx.prec = prec
- return +v
- @defun
- def elliprc(ctx, x, y, pv=True):
- r"""
- Evaluates the degenerate Carlson symmetric elliptic integral
- of the first kind
- .. math ::
- R_C(x,y) = R_F(x,y,y) =
- \frac{1}{2} \int_0^{\infty} \frac{dt}{(t+y) \sqrt{(t+x)}}.
- If `y \in (-\infty,0)`, either a value defined by continuity,
- or with *pv=True* the Cauchy principal value, can be computed.
- If `x \ge 0, y > 0`, the value can be expressed in terms of
- elementary functions as
- .. math ::
- R_C(x,y) =
- \begin{cases}
- \dfrac{1}{\sqrt{y-x}}
- \cos^{-1}\left(\sqrt{\dfrac{x}{y}}\right), & x < y \\
- \dfrac{1}{\sqrt{y}}, & x = y \\
- \dfrac{1}{\sqrt{x-y}}
- \cosh^{-1}\left(\sqrt{\dfrac{x}{y}}\right), & x > y \\
- \end{cases}.
- **Examples**
- Some special values and limits::
- >>> from mpmath import *
- >>> mp.dps = 25; mp.pretty = True
- >>> elliprc(1,2)*4; elliprc(0,1)*2; +pi
- 3.141592653589793238462643
- 3.141592653589793238462643
- 3.141592653589793238462643
- >>> elliprc(1,0)
- +inf
- >>> elliprc(5,5)**2
- 0.2
- >>> elliprc(1,inf); elliprc(inf,1); elliprc(inf,inf)
- 0.0
- 0.0
- 0.0
- Comparing with the elementary closed-form solution::
- >>> elliprc('1/3', '1/5'); sqrt(7.5)*acosh(sqrt('5/3'))
- 2.041630778983498390751238
- 2.041630778983498390751238
- >>> elliprc('1/5', '1/3'); sqrt(7.5)*acos(sqrt('3/5'))
- 1.875180765206547065111085
- 1.875180765206547065111085
- Comparing with numerical integration::
- >>> q = extradps(25)(quad)
- >>> elliprc(2, -3, pv=True)
- 0.3333969101113672670749334
- >>> elliprc(2, -3, pv=False)
- (0.3333969101113672670749334 + 0.7024814731040726393156375j)
- >>> 0.5*q(lambda t: 1/(sqrt(t+2)*(t-3)), [0,3-j,6,inf])
- (0.3333969101113672670749334 + 0.7024814731040726393156375j)
- """
- x = ctx.convert(x)
- y = ctx.convert(y)
- prec = ctx.prec
- try:
- ctx.prec += 20
- tol = ctx.eps * 2**10
- v = RC_calc(ctx, x, y, tol, pv)
- finally:
- ctx.prec = prec
- return +v
- @defun
- def elliprj(ctx, x, y, z, p, integration=1):
- r"""
- Evaluates the Carlson symmetric elliptic integral of the third kind
- .. math ::
- R_J(x,y,z,p) = \frac{3}{2}
- \int_0^{\infty} \frac{dt}{(t+p)\sqrt{(t+x)(t+y)(t+z)}}.
- Like :func:`~mpmath.elliprf`, the branch of the square root in the integrand
- is defined so as to be continuous along the path of integration for
- complex values of the arguments.
- **Examples**
- Some values and limits::
- >>> from mpmath import *
- >>> mp.dps = 25; mp.pretty = True
- >>> elliprj(1,1,1,1)
- 1.0
- >>> elliprj(2,2,2,2); 1/(2*sqrt(2))
- 0.3535533905932737622004222
- 0.3535533905932737622004222
- >>> elliprj(0,1,2,2)
- 1.067937989667395702268688
- >>> 3*(2*gamma('5/4')**2-pi**2/gamma('1/4')**2)/(sqrt(2*pi))
- 1.067937989667395702268688
- >>> elliprj(0,1,1,2); 3*pi*(2-sqrt(2))/4
- 1.380226776765915172432054
- 1.380226776765915172432054
- >>> elliprj(1,3,2,0); elliprj(0,1,1,0); elliprj(0,0,0,0)
- +inf
- +inf
- +inf
- >>> elliprj(1,inf,1,0); elliprj(1,1,1,inf)
- 0.0
- 0.0
- >>> chop(elliprj(1+j, 1-j, 1, 1))
- 0.8505007163686739432927844
- Scale transformation::
- >>> x,y,z,p = 2,3,4,5
- >>> k = mpf(100000)
- >>> elliprj(k*x,k*y,k*z,k*p); k**(-1.5)*elliprj(x,y,z,p)
- 4.521291677592745527851168e-9
- 4.521291677592745527851168e-9
- Comparing with numerical integration::
- >>> elliprj(1,2,3,4)
- 0.2398480997495677621758617
- >>> f = lambda t: 1/((t+4)*sqrt((t+1)*(t+2)*(t+3)))
- >>> 1.5*quad(f, [0,inf])
- 0.2398480997495677621758617
- >>> elliprj(1,2+1j,3,4-2j)
- (0.216888906014633498739952 + 0.04081912627366673332369512j)
- >>> f = lambda t: 1/((t+4-2j)*sqrt((t+1)*(t+2+1j)*(t+3)))
- >>> 1.5*quad(f, [0,inf])
- (0.216888906014633498739952 + 0.04081912627366673332369511j)
- """
- x = ctx.convert(x)
- y = ctx.convert(y)
- z = ctx.convert(z)
- p = ctx.convert(p)
- prec = ctx.prec
- try:
- ctx.prec += 20
- tol = ctx.eps * 2**10
- v = RJ_calc(ctx, x, y, z, p, tol, integration)
- finally:
- ctx.prec = prec
- return +v
- @defun
- def elliprd(ctx, x, y, z):
- r"""
- Evaluates the degenerate Carlson symmetric elliptic integral
- of the third kind or Carlson elliptic integral of the
- second kind `R_D(x,y,z) = R_J(x,y,z,z)`.
- See :func:`~mpmath.elliprj` for additional information.
- **Examples**
- >>> from mpmath import *
- >>> mp.dps = 25; mp.pretty = True
- >>> elliprd(1,2,3)
- 0.2904602810289906442326534
- >>> elliprj(1,2,3,3)
- 0.2904602810289906442326534
- The so-called *second lemniscate constant*, a transcendental number::
- >>> elliprd(0,2,1)/3
- 0.5990701173677961037199612
- >>> extradps(25)(quad)(lambda t: t**2/sqrt(1-t**4), [0,1])
- 0.5990701173677961037199612
- >>> gamma('3/4')**2/sqrt(2*pi)
- 0.5990701173677961037199612
- """
- return ctx.elliprj(x,y,z,z)
- @defun
- def elliprg(ctx, x, y, z):
- r"""
- Evaluates the Carlson completely symmetric elliptic integral
- of the second kind
- .. math ::
- R_G(x,y,z) = \frac{1}{4} \int_0^{\infty}
- \frac{t}{\sqrt{(t+x)(t+y)(t+z)}}
- \left( \frac{x}{t+x} + \frac{y}{t+y} + \frac{z}{t+z}\right) dt.
- **Examples**
- Evaluation for real and complex arguments::
- >>> from mpmath import *
- >>> mp.dps = 25; mp.pretty = True
- >>> elliprg(0,1,1)*4; +pi
- 3.141592653589793238462643
- 3.141592653589793238462643
- >>> elliprg(0,0.5,1)
- 0.6753219405238377512600874
- >>> chop(elliprg(1+j, 1-j, 2))
- 1.172431327676416604532822
- A double integral that can be evaluated in terms of `R_G`::
- >>> x,y,z = 2,3,4
- >>> def f(t,u):
- ... st = fp.sin(t); ct = fp.cos(t)
- ... su = fp.sin(u); cu = fp.cos(u)
- ... return (x*(st*cu)**2 + y*(st*su)**2 + z*ct**2)**0.5 * st
- ...
- >>> nprint(mpf(fp.quad(f, [0,fp.pi], [0,2*fp.pi])/(4*fp.pi)), 13)
- 1.725503028069
- >>> nprint(elliprg(x,y,z), 13)
- 1.725503028069
- """
- x = ctx.convert(x)
- y = ctx.convert(y)
- z = ctx.convert(z)
- zeros = (not x) + (not y) + (not z)
- if zeros == 3:
- return (x+y+z)*0
- if zeros == 2:
- if x: return 0.5*ctx.sqrt(x)
- if y: return 0.5*ctx.sqrt(y)
- return 0.5*ctx.sqrt(z)
- if zeros == 1:
- if not z:
- x, z = z, x
- def terms():
- T1 = 0.5*z*ctx.elliprf(x,y,z)
- T2 = -0.5*(x-z)*(y-z)*ctx.elliprd(x,y,z)/3
- T3 = 0.5*ctx.sqrt(x)*ctx.sqrt(y)/ctx.sqrt(z)
- return T1,T2,T3
- return ctx.sum_accurately(terms)
- @defun_wrapped
- def ellipf(ctx, phi, m):
- r"""
- Evaluates the Legendre incomplete elliptic integral of the first kind
- .. math ::
- F(\phi,m) = \int_0^{\phi} \frac{dt}{\sqrt{1-m \sin^2 t}}
- or equivalently
- .. math ::
- F(\phi,m) = \int_0^{\sin \phi}
- \frac{dt}{\left(\sqrt{1-t^2}\right)\left(\sqrt{1-mt^2}\right)}.
- The function reduces to a complete elliptic integral of the first kind
- (see :func:`~mpmath.ellipk`) when `\phi = \frac{\pi}{2}`; that is,
- .. math ::
- F\left(\frac{\pi}{2}, m\right) = K(m).
- In the defining integral, it is assumed that the principal branch
- of the square root is taken and that the path of integration avoids
- crossing any branch cuts. Outside `-\pi/2 \le \Re(\phi) \le \pi/2`,
- the function extends quasi-periodically as
- .. math ::
- F(\phi + n \pi, m) = 2 n K(m) + F(\phi,m), n \in \mathbb{Z}.
- **Plots**
- .. literalinclude :: /plots/ellipf.py
- .. image :: /plots/ellipf.png
- **Examples**
- Basic values and limits::
- >>> from mpmath import *
- >>> mp.dps = 25; mp.pretty = True
- >>> ellipf(0,1)
- 0.0
- >>> ellipf(0,0)
- 0.0
- >>> ellipf(1,0); ellipf(2+3j,0)
- 1.0
- (2.0 + 3.0j)
- >>> ellipf(1,1); log(sec(1)+tan(1))
- 1.226191170883517070813061
- 1.226191170883517070813061
- >>> ellipf(pi/2, -0.5); ellipk(-0.5)
- 1.415737208425956198892166
- 1.415737208425956198892166
- >>> ellipf(pi/2+eps, 1); ellipf(-pi/2-eps, 1)
- +inf
- +inf
- >>> ellipf(1.5, 1)
- 3.340677542798311003320813
- Comparing with numerical integration::
- >>> z,m = 0.5, 1.25
- >>> ellipf(z,m)
- 0.5287219202206327872978255
- >>> quad(lambda t: (1-m*sin(t)**2)**(-0.5), [0,z])
- 0.5287219202206327872978255
- The arguments may be complex numbers::
- >>> ellipf(3j, 0.5)
- (0.0 + 1.713602407841590234804143j)
- >>> ellipf(3+4j, 5-6j)
- (1.269131241950351323305741 - 0.3561052815014558335412538j)
- >>> z,m = 2+3j, 1.25
- >>> k = 1011
- >>> ellipf(z+pi*k,m); ellipf(z,m) + 2*k*ellipk(m)
- (4086.184383622179764082821 - 3003.003538923749396546871j)
- (4086.184383622179764082821 - 3003.003538923749396546871j)
- For `|\Re(z)| < \pi/2`, the function can be expressed as a
- hypergeometric series of two variables
- (see :func:`~mpmath.appellf1`)::
- >>> z,m = 0.5, 0.25
- >>> ellipf(z,m)
- 0.5050887275786480788831083
- >>> sin(z)*appellf1(0.5,0.5,0.5,1.5,sin(z)**2,m*sin(z)**2)
- 0.5050887275786480788831083
- """
- z = phi
- if not (ctx.isnormal(z) and ctx.isnormal(m)):
- if m == 0:
- return z + m
- if z == 0:
- return z * m
- if m == ctx.inf or m == ctx.ninf: return z/m
- raise ValueError
- x = z.real
- ctx.prec += max(0, ctx.mag(x))
- pi = +ctx.pi
- away = abs(x) > pi/2
- if m == 1:
- if away:
- return ctx.inf
- if away:
- d = ctx.nint(x/pi)
- z = z-pi*d
- P = 2*d*ctx.ellipk(m)
- else:
- P = 0
- c, s = ctx.cos_sin(z)
- return s * ctx.elliprf(c**2, 1-m*s**2, 1) + P
- @defun_wrapped
- def ellipe(ctx, *args):
- r"""
- Called with a single argument `m`, evaluates the Legendre complete
- elliptic integral of the second kind, `E(m)`, defined by
- .. math :: E(m) = \int_0^{\pi/2} \sqrt{1-m \sin^2 t} \, dt \,=\,
- \frac{\pi}{2}
- \,_2F_1\left(\frac{1}{2}, -\frac{1}{2}, 1, m\right).
- Called with two arguments `\phi, m`, evaluates the incomplete elliptic
- integral of the second kind
- .. math ::
- E(\phi,m) = \int_0^{\phi} \sqrt{1-m \sin^2 t} \, dt =
- \int_0^{\sin z}
- \frac{\sqrt{1-mt^2}}{\sqrt{1-t^2}} \, dt.
- The incomplete integral reduces to a complete integral when
- `\phi = \frac{\pi}{2}`; that is,
- .. math ::
- E\left(\frac{\pi}{2}, m\right) = E(m).
- In the defining integral, it is assumed that the principal branch
- of the square root is taken and that the path of integration avoids
- crossing any branch cuts. Outside `-\pi/2 \le \Re(z) \le \pi/2`,
- the function extends quasi-periodically as
- .. math ::
- E(\phi + n \pi, m) = 2 n E(m) + E(\phi,m), n \in \mathbb{Z}.
- **Plots**
- .. literalinclude :: /plots/ellipe.py
- .. image :: /plots/ellipe.png
- **Examples for the complete integral**
- Basic values and limits::
- >>> from mpmath import *
- >>> mp.dps = 25; mp.pretty = True
- >>> ellipe(0)
- 1.570796326794896619231322
- >>> ellipe(1)
- 1.0
- >>> ellipe(-1)
- 1.910098894513856008952381
- >>> ellipe(2)
- (0.5990701173677961037199612 + 0.5990701173677961037199612j)
- >>> ellipe(inf)
- (0.0 + +infj)
- >>> ellipe(-inf)
- +inf
- Verifying the defining integral and hypergeometric
- representation::
- >>> ellipe(0.5)
- 1.350643881047675502520175
- >>> quad(lambda t: sqrt(1-0.5*sin(t)**2), [0, pi/2])
- 1.350643881047675502520175
- >>> pi/2*hyp2f1(0.5,-0.5,1,0.5)
- 1.350643881047675502520175
- Evaluation is supported for arbitrary complex `m`::
- >>> ellipe(0.5+0.25j)
- (1.360868682163129682716687 - 0.1238733442561786843557315j)
- >>> ellipe(3+4j)
- (1.499553520933346954333612 - 1.577879007912758274533309j)
- A definite integral::
- >>> quad(ellipe, [0,1])
- 1.333333333333333333333333
- **Examples for the incomplete integral**
- Basic values and limits::
- >>> ellipe(0,1)
- 0.0
- >>> ellipe(0,0)
- 0.0
- >>> ellipe(1,0)
- 1.0
- >>> ellipe(2+3j,0)
- (2.0 + 3.0j)
- >>> ellipe(1,1); sin(1)
- 0.8414709848078965066525023
- 0.8414709848078965066525023
- >>> ellipe(pi/2, -0.5); ellipe(-0.5)
- 1.751771275694817862026502
- 1.751771275694817862026502
- >>> ellipe(pi/2, 1); ellipe(-pi/2, 1)
- 1.0
- -1.0
- >>> ellipe(1.5, 1)
- 0.9974949866040544309417234
- Comparing with numerical integration::
- >>> z,m = 0.5, 1.25
- >>> ellipe(z,m)
- 0.4740152182652628394264449
- >>> quad(lambda t: sqrt(1-m*sin(t)**2), [0,z])
- 0.4740152182652628394264449
- The arguments may be complex numbers::
- >>> ellipe(3j, 0.5)
- (0.0 + 7.551991234890371873502105j)
- >>> ellipe(3+4j, 5-6j)
- (24.15299022574220502424466 + 75.2503670480325997418156j)
- >>> k = 35
- >>> z,m = 2+3j, 1.25
- >>> ellipe(z+pi*k,m); ellipe(z,m) + 2*k*ellipe(m)
- (48.30138799412005235090766 + 17.47255216721987688224357j)
- (48.30138799412005235090766 + 17.47255216721987688224357j)
- For `|\Re(z)| < \pi/2`, the function can be expressed as a
- hypergeometric series of two variables
- (see :func:`~mpmath.appellf1`)::
- >>> z,m = 0.5, 0.25
- >>> ellipe(z,m)
- 0.4950017030164151928870375
- >>> sin(z)*appellf1(0.5,0.5,-0.5,1.5,sin(z)**2,m*sin(z)**2)
- 0.4950017030164151928870376
- """
- if len(args) == 1:
- return ctx._ellipe(args[0])
- else:
- phi, m = args
- z = phi
- if not (ctx.isnormal(z) and ctx.isnormal(m)):
- if m == 0:
- return z + m
- if z == 0:
- return z * m
- if m == ctx.inf or m == ctx.ninf:
- return ctx.inf
- raise ValueError
- x = z.real
- ctx.prec += max(0, ctx.mag(x))
- pi = +ctx.pi
- away = abs(x) > pi/2
- if away:
- d = ctx.nint(x/pi)
- z = z-pi*d
- P = 2*d*ctx.ellipe(m)
- else:
- P = 0
- def terms():
- c, s = ctx.cos_sin(z)
- x = c**2
- y = 1-m*s**2
- RF = ctx.elliprf(x, y, 1)
- RD = ctx.elliprd(x, y, 1)
- return s*RF, -m*s**3*RD/3
- return ctx.sum_accurately(terms) + P
- @defun_wrapped
- def ellippi(ctx, *args):
- r"""
- Called with three arguments `n, \phi, m`, evaluates the Legendre
- incomplete elliptic integral of the third kind
- .. math ::
- \Pi(n; \phi, m) = \int_0^{\phi}
- \frac{dt}{(1-n \sin^2 t) \sqrt{1-m \sin^2 t}} =
- \int_0^{\sin \phi}
- \frac{dt}{(1-nt^2) \sqrt{1-t^2} \sqrt{1-mt^2}}.
- Called with two arguments `n, m`, evaluates the complete
- elliptic integral of the third kind
- `\Pi(n,m) = \Pi(n; \frac{\pi}{2},m)`.
- In the defining integral, it is assumed that the principal branch
- of the square root is taken and that the path of integration avoids
- crossing any branch cuts. Outside `-\pi/2 \le \Re(\phi) \le \pi/2`,
- the function extends quasi-periodically as
- .. math ::
- \Pi(n,\phi+k\pi,m) = 2k\Pi(n,m) + \Pi(n,\phi,m), k \in \mathbb{Z}.
- **Plots**
- .. literalinclude :: /plots/ellippi.py
- .. image :: /plots/ellippi.png
- **Examples for the complete integral**
- Some basic values and limits::
- >>> from mpmath import *
- >>> mp.dps = 25; mp.pretty = True
- >>> ellippi(0,-5); ellipk(-5)
- 0.9555039270640439337379334
- 0.9555039270640439337379334
- >>> ellippi(inf,2)
- 0.0
- >>> ellippi(2,inf)
- 0.0
- >>> abs(ellippi(1,5))
- +inf
- >>> abs(ellippi(0.25,1))
- +inf
- Evaluation in terms of simpler functions::
- >>> ellippi(0.25,0.25); ellipe(0.25)/(1-0.25)
- 1.956616279119236207279727
- 1.956616279119236207279727
- >>> ellippi(3,0); pi/(2*sqrt(-2))
- (0.0 - 1.11072073453959156175397j)
- (0.0 - 1.11072073453959156175397j)
- >>> ellippi(-3,0); pi/(2*sqrt(4))
- 0.7853981633974483096156609
- 0.7853981633974483096156609
- **Examples for the incomplete integral**
- Basic values and limits::
- >>> ellippi(0.25,-0.5); ellippi(0.25,pi/2,-0.5)
- 1.622944760954741603710555
- 1.622944760954741603710555
- >>> ellippi(1,0,1)
- 0.0
- >>> ellippi(inf,0,1)
- 0.0
- >>> ellippi(0,0.25,0.5); ellipf(0.25,0.5)
- 0.2513040086544925794134591
- 0.2513040086544925794134591
- >>> ellippi(1,1,1); (log(sec(1)+tan(1))+sec(1)*tan(1))/2
- 2.054332933256248668692452
- 2.054332933256248668692452
- >>> ellippi(0.25, 53*pi/2, 0.75); 53*ellippi(0.25,0.75)
- 135.240868757890840755058
- 135.240868757890840755058
- >>> ellippi(0.5,pi/4,0.5); 2*ellipe(pi/4,0.5)-1/sqrt(3)
- 0.9190227391656969903987269
- 0.9190227391656969903987269
- Complex arguments are supported::
- >>> ellippi(0.5, 5+6j-2*pi, -7-8j)
- (-0.3612856620076747660410167 + 0.5217735339984807829755815j)
- Some degenerate cases::
- >>> ellippi(1,1)
- +inf
- >>> ellippi(1,0)
- +inf
- >>> ellippi(1,2,0)
- +inf
- >>> ellippi(1,2,1)
- +inf
- >>> ellippi(1,0,1)
- 0.0
- """
- if len(args) == 2:
- n, m = args
- complete = True
- z = phi = ctx.pi/2
- else:
- n, phi, m = args
- complete = False
- z = phi
- if not (ctx.isnormal(n) and ctx.isnormal(z) and ctx.isnormal(m)):
- if ctx.isnan(n) or ctx.isnan(z) or ctx.isnan(m):
- raise ValueError
- if complete:
- if m == 0:
- if n == 1:
- return ctx.inf
- return ctx.pi/(2*ctx.sqrt(1-n))
- if n == 0: return ctx.ellipk(m)
- if ctx.isinf(n) or ctx.isinf(m): return ctx.zero
- else:
- if z == 0: return z
- if ctx.isinf(n): return ctx.zero
- if ctx.isinf(m): return ctx.zero
- if ctx.isinf(n) or ctx.isinf(z) or ctx.isinf(m):
- raise ValueError
- if complete:
- if m == 1:
- if n == 1:
- return ctx.inf
- return -ctx.inf/ctx.sign(n-1)
- away = False
- else:
- x = z.real
- ctx.prec += max(0, ctx.mag(x))
- pi = +ctx.pi
- away = abs(x) > pi/2
- if away:
- d = ctx.nint(x/pi)
- z = z-pi*d
- P = 2*d*ctx.ellippi(n,m)
- if ctx.isinf(P):
- return ctx.inf
- else:
- P = 0
- def terms():
- if complete:
- c, s = ctx.zero, ctx.one
- else:
- c, s = ctx.cos_sin(z)
- x = c**2
- y = 1-m*s**2
- RF = ctx.elliprf(x, y, 1)
- RJ = ctx.elliprj(x, y, 1, 1-n*s**2)
- return s*RF, n*s**3*RJ/3
- return ctx.sum_accurately(terms) + P
|