123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413 |
- from ..libmp.backend import xrange
- from .functions import defun, defun_wrapped
- def _check_need_perturb(ctx, terms, prec, discard_known_zeros):
- perturb = recompute = False
- extraprec = 0
- discard = []
- for term_index, term in enumerate(terms):
- w_s, c_s, alpha_s, beta_s, a_s, b_s, z = term
- have_singular_nongamma_weight = False
- # Avoid division by zero in leading factors (TODO:
- # also check for near division by zero?)
- for k, w in enumerate(w_s):
- if not w:
- if ctx.re(c_s[k]) <= 0 and c_s[k]:
- perturb = recompute = True
- have_singular_nongamma_weight = True
- pole_count = [0, 0, 0]
- # Check for gamma and series poles and near-poles
- for data_index, data in enumerate([alpha_s, beta_s, b_s]):
- for i, x in enumerate(data):
- n, d = ctx.nint_distance(x)
- # Poles
- if n > 0:
- continue
- if d == ctx.ninf:
- # OK if we have a polynomial
- # ------------------------------
- ok = False
- if data_index == 2:
- for u in a_s:
- if ctx.isnpint(u) and u >= int(n):
- ok = True
- break
- if ok:
- continue
- pole_count[data_index] += 1
- # ------------------------------
- #perturb = recompute = True
- #return perturb, recompute, extraprec
- elif d < -4:
- extraprec += -d
- recompute = True
- if discard_known_zeros and pole_count[1] > pole_count[0] + pole_count[2] \
- and not have_singular_nongamma_weight:
- discard.append(term_index)
- elif sum(pole_count):
- perturb = recompute = True
- return perturb, recompute, extraprec, discard
- _hypercomb_msg = """
- hypercomb() failed to converge to the requested %i bits of accuracy
- using a working precision of %i bits. The function value may be zero or
- infinite; try passing zeroprec=N or infprec=M to bound finite values between
- 2^(-N) and 2^M. Otherwise try a higher maxprec or maxterms.
- """
- @defun
- def hypercomb(ctx, function, params=[], discard_known_zeros=True, **kwargs):
- orig = ctx.prec
- sumvalue = ctx.zero
- dist = ctx.nint_distance
- ninf = ctx.ninf
- orig_params = params[:]
- verbose = kwargs.get('verbose', False)
- maxprec = kwargs.get('maxprec', ctx._default_hyper_maxprec(orig))
- kwargs['maxprec'] = maxprec # For calls to hypsum
- zeroprec = kwargs.get('zeroprec')
- infprec = kwargs.get('infprec')
- perturbed_reference_value = None
- hextra = 0
- try:
- while 1:
- ctx.prec += 10
- if ctx.prec > maxprec:
- raise ValueError(_hypercomb_msg % (orig, ctx.prec))
- orig2 = ctx.prec
- params = orig_params[:]
- terms = function(*params)
- if verbose:
- print()
- print("ENTERING hypercomb main loop")
- print("prec =", ctx.prec)
- print("hextra", hextra)
- perturb, recompute, extraprec, discard = \
- _check_need_perturb(ctx, terms, orig, discard_known_zeros)
- ctx.prec += extraprec
- if perturb:
- if "hmag" in kwargs:
- hmag = kwargs["hmag"]
- elif ctx._fixed_precision:
- hmag = int(ctx.prec*0.3)
- else:
- hmag = orig + 10 + hextra
- h = ctx.ldexp(ctx.one, -hmag)
- ctx.prec = orig2 + 10 + hmag + 10
- for k in range(len(params)):
- params[k] += h
- # Heuristically ensure that the perturbations
- # are "independent" so that two perturbations
- # don't accidentally cancel each other out
- # in a subtraction.
- h += h/(k+1)
- if recompute:
- terms = function(*params)
- if discard_known_zeros:
- terms = [term for (i, term) in enumerate(terms) if i not in discard]
- if not terms:
- return ctx.zero
- evaluated_terms = []
- for term_index, term_data in enumerate(terms):
- w_s, c_s, alpha_s, beta_s, a_s, b_s, z = term_data
- if verbose:
- print()
- print(" Evaluating term %i/%i : %iF%i" % \
- (term_index+1, len(terms), len(a_s), len(b_s)))
- print(" powers", ctx.nstr(w_s), ctx.nstr(c_s))
- print(" gamma", ctx.nstr(alpha_s), ctx.nstr(beta_s))
- print(" hyper", ctx.nstr(a_s), ctx.nstr(b_s))
- print(" z", ctx.nstr(z))
- #v = ctx.hyper(a_s, b_s, z, **kwargs)
- #for a in alpha_s: v *= ctx.gamma(a)
- #for b in beta_s: v *= ctx.rgamma(b)
- #for w, c in zip(w_s, c_s): v *= ctx.power(w, c)
- v = ctx.fprod([ctx.hyper(a_s, b_s, z, **kwargs)] + \
- [ctx.gamma(a) for a in alpha_s] + \
- [ctx.rgamma(b) for b in beta_s] + \
- [ctx.power(w,c) for (w,c) in zip(w_s,c_s)])
- if verbose:
- print(" Value:", v)
- evaluated_terms.append(v)
- if len(terms) == 1 and (not perturb):
- sumvalue = evaluated_terms[0]
- break
- if ctx._fixed_precision:
- sumvalue = ctx.fsum(evaluated_terms)
- break
- sumvalue = ctx.fsum(evaluated_terms)
- term_magnitudes = [ctx.mag(x) for x in evaluated_terms]
- max_magnitude = max(term_magnitudes)
- sum_magnitude = ctx.mag(sumvalue)
- cancellation = max_magnitude - sum_magnitude
- if verbose:
- print()
- print(" Cancellation:", cancellation, "bits")
- print(" Increased precision:", ctx.prec - orig, "bits")
- precision_ok = cancellation < ctx.prec - orig
- if zeroprec is None:
- zero_ok = False
- else:
- zero_ok = max_magnitude - ctx.prec < -zeroprec
- if infprec is None:
- inf_ok = False
- else:
- inf_ok = max_magnitude > infprec
- if precision_ok and (not perturb) or ctx.isnan(cancellation):
- break
- elif precision_ok:
- if perturbed_reference_value is None:
- hextra += 20
- perturbed_reference_value = sumvalue
- continue
- elif ctx.mag(sumvalue - perturbed_reference_value) <= \
- ctx.mag(sumvalue) - orig:
- break
- elif zero_ok:
- sumvalue = ctx.zero
- break
- elif inf_ok:
- sumvalue = ctx.inf
- break
- elif 'hmag' in kwargs:
- break
- else:
- hextra *= 2
- perturbed_reference_value = sumvalue
- # Increase precision
- else:
- increment = min(max(cancellation, orig//2), max(extraprec,orig))
- ctx.prec += increment
- if verbose:
- print(" Must start over with increased precision")
- continue
- finally:
- ctx.prec = orig
- return +sumvalue
- @defun
- def hyper(ctx, a_s, b_s, z, **kwargs):
- """
- Hypergeometric function, general case.
- """
- z = ctx.convert(z)
- p = len(a_s)
- q = len(b_s)
- a_s = [ctx._convert_param(a) for a in a_s]
- b_s = [ctx._convert_param(b) for b in b_s]
- # Reduce degree by eliminating common parameters
- if kwargs.get('eliminate', True):
- elim_nonpositive = kwargs.get('eliminate_all', False)
- i = 0
- while i < q and a_s:
- b = b_s[i]
- if b in a_s and (elim_nonpositive or not ctx.isnpint(b[0])):
- a_s.remove(b)
- b_s.remove(b)
- p -= 1
- q -= 1
- else:
- i += 1
- # Handle special cases
- if p == 0:
- if q == 1: return ctx._hyp0f1(b_s, z, **kwargs)
- elif q == 0: return ctx.exp(z)
- elif p == 1:
- if q == 1: return ctx._hyp1f1(a_s, b_s, z, **kwargs)
- elif q == 2: return ctx._hyp1f2(a_s, b_s, z, **kwargs)
- elif q == 0: return ctx._hyp1f0(a_s[0][0], z)
- elif p == 2:
- if q == 1: return ctx._hyp2f1(a_s, b_s, z, **kwargs)
- elif q == 2: return ctx._hyp2f2(a_s, b_s, z, **kwargs)
- elif q == 3: return ctx._hyp2f3(a_s, b_s, z, **kwargs)
- elif q == 0: return ctx._hyp2f0(a_s, b_s, z, **kwargs)
- elif p == q+1:
- return ctx._hypq1fq(p, q, a_s, b_s, z, **kwargs)
- elif p > q+1 and not kwargs.get('force_series'):
- return ctx._hyp_borel(p, q, a_s, b_s, z, **kwargs)
- coeffs, types = zip(*(a_s+b_s))
- return ctx.hypsum(p, q, types, coeffs, z, **kwargs)
- @defun
- def hyp0f1(ctx,b,z,**kwargs):
- return ctx.hyper([],[b],z,**kwargs)
- @defun
- def hyp1f1(ctx,a,b,z,**kwargs):
- return ctx.hyper([a],[b],z,**kwargs)
- @defun
- def hyp1f2(ctx,a1,b1,b2,z,**kwargs):
- return ctx.hyper([a1],[b1,b2],z,**kwargs)
- @defun
- def hyp2f1(ctx,a,b,c,z,**kwargs):
- return ctx.hyper([a,b],[c],z,**kwargs)
- @defun
- def hyp2f2(ctx,a1,a2,b1,b2,z,**kwargs):
- return ctx.hyper([a1,a2],[b1,b2],z,**kwargs)
- @defun
- def hyp2f3(ctx,a1,a2,b1,b2,b3,z,**kwargs):
- return ctx.hyper([a1,a2],[b1,b2,b3],z,**kwargs)
- @defun
- def hyp2f0(ctx,a,b,z,**kwargs):
- return ctx.hyper([a,b],[],z,**kwargs)
- @defun
- def hyp3f2(ctx,a1,a2,a3,b1,b2,z,**kwargs):
- return ctx.hyper([a1,a2,a3],[b1,b2],z,**kwargs)
- @defun_wrapped
- def _hyp1f0(ctx, a, z):
- return (1-z) ** (-a)
- @defun
- def _hyp0f1(ctx, b_s, z, **kwargs):
- (b, btype), = b_s
- if z:
- magz = ctx.mag(z)
- else:
- magz = 0
- if magz >= 8 and not kwargs.get('force_series'):
- try:
- # http://functions.wolfram.com/HypergeometricFunctions/
- # Hypergeometric0F1/06/02/03/0004/
- # TODO: handle the all-real case more efficiently!
- # TODO: figure out how much precision is needed (exponential growth)
- orig = ctx.prec
- try:
- ctx.prec += 12 + magz//2
- def h():
- w = ctx.sqrt(-z)
- jw = ctx.j*w
- u = 1/(4*jw)
- c = ctx.mpq_1_2 - b
- E = ctx.exp(2*jw)
- T1 = ([-jw,E], [c,-1], [], [], [b-ctx.mpq_1_2, ctx.mpq_3_2-b], [], -u)
- T2 = ([jw,E], [c,1], [], [], [b-ctx.mpq_1_2, ctx.mpq_3_2-b], [], u)
- return T1, T2
- v = ctx.hypercomb(h, [], force_series=True)
- v = ctx.gamma(b)/(2*ctx.sqrt(ctx.pi))*v
- finally:
- ctx.prec = orig
- if ctx._is_real_type(b) and ctx._is_real_type(z):
- v = ctx._re(v)
- return +v
- except ctx.NoConvergence:
- pass
- return ctx.hypsum(0, 1, (btype,), [b], z, **kwargs)
- @defun
- def _hyp1f1(ctx, a_s, b_s, z, **kwargs):
- (a, atype), = a_s
- (b, btype), = b_s
- if not z:
- return ctx.one+z
- magz = ctx.mag(z)
- if magz >= 7 and not (ctx.isint(a) and ctx.re(a) <= 0):
- if ctx.isinf(z):
- if ctx.sign(a) == ctx.sign(b) == ctx.sign(z) == 1:
- return ctx.inf
- return ctx.nan * z
- try:
- try:
- ctx.prec += magz
- sector = ctx._im(z) < 0
- def h(a,b):
- if sector:
- E = ctx.expjpi(ctx.fneg(a, exact=True))
- else:
- E = ctx.expjpi(a)
- rz = 1/z
- T1 = ([E,z], [1,-a], [b], [b-a], [a, 1+a-b], [], -rz)
- T2 = ([ctx.exp(z),z], [1,a-b], [b], [a], [b-a, 1-a], [], rz)
- return T1, T2
- v = ctx.hypercomb(h, [a,b], force_series=True)
- if ctx._is_real_type(a) and ctx._is_real_type(b) and ctx._is_real_type(z):
- v = ctx._re(v)
- return +v
- except ctx.NoConvergence:
- pass
- finally:
- ctx.prec -= magz
- v = ctx.hypsum(1, 1, (atype, btype), [a, b], z, **kwargs)
- return v
- def _hyp2f1_gosper(ctx,a,b,c,z,**kwargs):
- # Use Gosper's recurrence
- # See http://www.math.utexas.edu/pipermail/maxima/2006/000126.html
- _a,_b,_c,_z = a, b, c, z
- orig = ctx.prec
- maxprec = kwargs.get('maxprec', 100*orig)
- extra = 10
- while 1:
- ctx.prec = orig + extra
- #a = ctx.convert(_a)
- #b = ctx.convert(_b)
- #c = ctx.convert(_c)
- z = ctx.convert(_z)
- d = ctx.mpf(0)
- e = ctx.mpf(1)
- f = ctx.mpf(0)
- k = 0
- # Common subexpression elimination, unfortunately making
- # things a bit unreadable. The formula is quite messy to begin
- # with, though...
- abz = a*b*z
- ch = c * ctx.mpq_1_2
- c1h = (c+1) * ctx.mpq_1_2
- nz = 1-z
- g = z/nz
- abg = a*b*g
- cba = c-b-a
- z2 = z-2
- tol = -ctx.prec - 10
- nstr = ctx.nstr
- nprint = ctx.nprint
- mag = ctx.mag
- maxmag = ctx.ninf
- while 1:
- kch = k+ch
- kakbz = (k+a)*(k+b)*z / (4*(k+1)*kch*(k+c1h))
- d1 = kakbz*(e-(k+cba)*d*g)
- e1 = kakbz*(d*abg+(k+c)*e)
- ft = d*(k*(cba*z+k*z2-c)-abz)/(2*kch*nz)
- f1 = f + e - ft
- maxmag = max(maxmag, mag(f1))
- if mag(f1-f) < tol:
- break
- d, e, f = d1, e1, f1
- k += 1
- cancellation = maxmag - mag(f1)
- if cancellation < extra:
- break
- else:
- extra += cancellation
- if extra > maxprec:
- raise ctx.NoConvergence
- return f1
- @defun
- def _hyp2f1(ctx, a_s, b_s, z, **kwargs):
- (a, atype), (b, btype) = a_s
- (c, ctype), = b_s
- if z == 1:
- # TODO: the following logic can be simplified
- convergent = ctx.re(c-a-b) > 0
- finite = (ctx.isint(a) and a <= 0) or (ctx.isint(b) and b <= 0)
- zerodiv = ctx.isint(c) and c <= 0 and not \
- ((ctx.isint(a) and c <= a <= 0) or (ctx.isint(b) and c <= b <= 0))
- #print "bz", a, b, c, z, convergent, finite, zerodiv
- # Gauss's theorem gives the value if convergent
- if (convergent or finite) and not zerodiv:
- return ctx.gammaprod([c, c-a-b], [c-a, c-b], _infsign=True)
- # Otherwise, there is a pole and we take the
- # sign to be that when approaching from below
- # XXX: this evaluation is not necessarily correct in all cases
- return ctx.hyp2f1(a,b,c,1-ctx.eps*2) * ctx.inf
- # Equal to 1 (first term), unless there is a subsequent
- # division by zero
- if not z:
- # Division by zero but power of z is higher than
- # first order so cancels
- if c or a == 0 or b == 0:
- return 1+z
- # Indeterminate
- return ctx.nan
- # Hit zero denominator unless numerator goes to 0 first
- if ctx.isint(c) and c <= 0:
- if (ctx.isint(a) and c <= a <= 0) or \
- (ctx.isint(b) and c <= b <= 0):
- pass
- else:
- # Pole in series
- return ctx.inf
- absz = abs(z)
- # Fast case: standard series converges rapidly,
- # possibly in finitely many terms
- if absz <= 0.8 or (ctx.isint(a) and a <= 0 and a >= -1000) or \
- (ctx.isint(b) and b <= 0 and b >= -1000):
- return ctx.hypsum(2, 1, (atype, btype, ctype), [a, b, c], z, **kwargs)
- orig = ctx.prec
- try:
- ctx.prec += 10
- # Use 1/z transformation
- if absz >= 1.3:
- def h(a,b):
- t = ctx.mpq_1-c; ab = a-b; rz = 1/z
- T1 = ([-z],[-a], [c,-ab],[b,c-a], [a,t+a],[ctx.mpq_1+ab], rz)
- T2 = ([-z],[-b], [c,ab],[a,c-b], [b,t+b],[ctx.mpq_1-ab], rz)
- return T1, T2
- v = ctx.hypercomb(h, [a,b], **kwargs)
- # Use 1-z transformation
- elif abs(1-z) <= 0.75:
- def h(a,b):
- t = c-a-b; ca = c-a; cb = c-b; rz = 1-z
- T1 = [], [], [c,t], [ca,cb], [a,b], [1-t], rz
- T2 = [rz], [t], [c,a+b-c], [a,b], [ca,cb], [1+t], rz
- return T1, T2
- v = ctx.hypercomb(h, [a,b], **kwargs)
- # Use z/(z-1) transformation
- elif abs(z/(z-1)) <= 0.75:
- v = ctx.hyp2f1(a, c-b, c, z/(z-1)) / (1-z)**a
- # Remaining part of unit circle
- else:
- v = _hyp2f1_gosper(ctx,a,b,c,z,**kwargs)
- finally:
- ctx.prec = orig
- return +v
- @defun
- def _hypq1fq(ctx, p, q, a_s, b_s, z, **kwargs):
- r"""
- Evaluates 3F2, 4F3, 5F4, ...
- """
- a_s, a_types = zip(*a_s)
- b_s, b_types = zip(*b_s)
- a_s = list(a_s)
- b_s = list(b_s)
- absz = abs(z)
- ispoly = False
- for a in a_s:
- if ctx.isint(a) and a <= 0:
- ispoly = True
- break
- # Direct summation
- if absz < 1 or ispoly:
- try:
- return ctx.hypsum(p, q, a_types+b_types, a_s+b_s, z, **kwargs)
- except ctx.NoConvergence:
- if absz > 1.1 or ispoly:
- raise
- # Use expansion at |z-1| -> 0.
- # Reference: Wolfgang Buhring, "Generalized Hypergeometric Functions at
- # Unit Argument", Proc. Amer. Math. Soc., Vol. 114, No. 1 (Jan. 1992),
- # pp.145-153
- # The current implementation has several problems:
- # 1. We only implement it for 3F2. The expansion coefficients are
- # given by extremely messy nested sums in the higher degree cases
- # (see reference). Is efficient sequential generation of the coefficients
- # possible in the > 3F2 case?
- # 2. Although the series converges, it may do so slowly, so we need
- # convergence acceleration. The acceleration implemented by
- # nsum does not always help, so results returned are sometimes
- # inaccurate! Can we do better?
- # 3. We should check conditions for convergence, and possibly
- # do a better job of cancelling out gamma poles if possible.
- if z == 1:
- # XXX: should also check for division by zero in the
- # denominator of the series (cf. hyp2f1)
- S = ctx.re(sum(b_s)-sum(a_s))
- if S <= 0:
- #return ctx.hyper(a_s, b_s, 1-ctx.eps*2, **kwargs) * ctx.inf
- return ctx.hyper(a_s, b_s, 0.9, **kwargs) * ctx.inf
- if (p,q) == (3,2) and abs(z-1) < 0.05: # and kwargs.get('sum1')
- #print "Using alternate summation (experimental)"
- a1,a2,a3 = a_s
- b1,b2 = b_s
- u = b1+b2-a3
- initial = ctx.gammaprod([b2-a3,b1-a3,a1,a2],[b2-a3,b1-a3,1,u])
- def term(k, _cache={0:initial}):
- u = b1+b2-a3+k
- if k in _cache:
- t = _cache[k]
- else:
- t = _cache[k-1]
- t *= (b1+k-a3-1)*(b2+k-a3-1)
- t /= k*(u-1)
- _cache[k] = t
- return t * ctx.hyp2f1(a1,a2,u,z)
- try:
- S = ctx.nsum(term, [0,ctx.inf], verbose=kwargs.get('verbose'),
- strict=kwargs.get('strict', True))
- return S * ctx.gammaprod([b1,b2],[a1,a2,a3])
- except ctx.NoConvergence:
- pass
- # Try to use convergence acceleration on and close to the unit circle.
- # Problem: the convergence acceleration degenerates as |z-1| -> 0,
- # except for special cases. Everywhere else, the Shanks transformation
- # is very efficient.
- if absz < 1.1 and ctx._re(z) <= 1:
- def term(kk, _cache={0:ctx.one}):
- k = int(kk)
- if k != kk:
- t = z ** ctx.mpf(kk) / ctx.fac(kk)
- for a in a_s: t *= ctx.rf(a,kk)
- for b in b_s: t /= ctx.rf(b,kk)
- return t
- if k in _cache:
- return _cache[k]
- t = term(k-1)
- m = k-1
- for j in xrange(p): t *= (a_s[j]+m)
- for j in xrange(q): t /= (b_s[j]+m)
- t *= z
- t /= k
- _cache[k] = t
- return t
- sum_method = kwargs.get('sum_method', 'r+s+e')
- try:
- return ctx.nsum(term, [0,ctx.inf], verbose=kwargs.get('verbose'),
- strict=kwargs.get('strict', True),
- method=sum_method.replace('e',''))
- except ctx.NoConvergence:
- if 'e' not in sum_method:
- raise
- pass
- if kwargs.get('verbose'):
- print("Attempting Euler-Maclaurin summation")
- """
- Somewhat slower version (one diffs_exp for each factor).
- However, this would be faster with fast direct derivatives
- of the gamma function.
- def power_diffs(k0):
- r = 0
- l = ctx.log(z)
- while 1:
- yield z**ctx.mpf(k0) * l**r
- r += 1
- def loggamma_diffs(x, reciprocal=False):
- sign = (-1) ** reciprocal
- yield sign * ctx.loggamma(x)
- i = 0
- while 1:
- yield sign * ctx.psi(i,x)
- i += 1
- def hyper_diffs(k0):
- b2 = b_s + [1]
- A = [ctx.diffs_exp(loggamma_diffs(a+k0)) for a in a_s]
- B = [ctx.diffs_exp(loggamma_diffs(b+k0,True)) for b in b2]
- Z = [power_diffs(k0)]
- C = ctx.gammaprod([b for b in b2], [a for a in a_s])
- for d in ctx.diffs_prod(A + B + Z):
- v = C * d
- yield v
- """
- def log_diffs(k0):
- b2 = b_s + [1]
- yield sum(ctx.loggamma(a+k0) for a in a_s) - \
- sum(ctx.loggamma(b+k0) for b in b2) + k0*ctx.log(z)
- i = 0
- while 1:
- v = sum(ctx.psi(i,a+k0) for a in a_s) - \
- sum(ctx.psi(i,b+k0) for b in b2)
- if i == 0:
- v += ctx.log(z)
- yield v
- i += 1
- def hyper_diffs(k0):
- C = ctx.gammaprod([b for b in b_s], [a for a in a_s])
- for d in ctx.diffs_exp(log_diffs(k0)):
- v = C * d
- yield v
- tol = ctx.eps / 1024
- prec = ctx.prec
- try:
- trunc = 50 * ctx.dps
- ctx.prec += 20
- for i in xrange(5):
- head = ctx.fsum(term(k) for k in xrange(trunc))
- tail, err = ctx.sumem(term, [trunc, ctx.inf], tol=tol,
- adiffs=hyper_diffs(trunc),
- verbose=kwargs.get('verbose'),
- error=True,
- _fast_abort=True)
- if err < tol:
- v = head + tail
- break
- trunc *= 2
- # Need to increase precision because calculation of
- # derivatives may be inaccurate
- ctx.prec += ctx.prec//2
- if i == 4:
- raise ctx.NoConvergence(\
- "Euler-Maclaurin summation did not converge")
- finally:
- ctx.prec = prec
- return +v
- # Use 1/z transformation
- # http://functions.wolfram.com/HypergeometricFunctions/
- # HypergeometricPFQ/06/01/05/02/0004/
- def h(*args):
- a_s = list(args[:p])
- b_s = list(args[p:])
- Ts = []
- recz = ctx.one/z
- negz = ctx.fneg(z, exact=True)
- for k in range(q+1):
- ak = a_s[k]
- C = [negz]
- Cp = [-ak]
- Gn = b_s + [ak] + [a_s[j]-ak for j in range(q+1) if j != k]
- Gd = a_s + [b_s[j]-ak for j in range(q)]
- Fn = [ak] + [ak-b_s[j]+1 for j in range(q)]
- Fd = [1-a_s[j]+ak for j in range(q+1) if j != k]
- Ts.append((C, Cp, Gn, Gd, Fn, Fd, recz))
- return Ts
- return ctx.hypercomb(h, a_s+b_s, **kwargs)
- @defun
- def _hyp_borel(ctx, p, q, a_s, b_s, z, **kwargs):
- if a_s:
- a_s, a_types = zip(*a_s)
- a_s = list(a_s)
- else:
- a_s, a_types = [], ()
- if b_s:
- b_s, b_types = zip(*b_s)
- b_s = list(b_s)
- else:
- b_s, b_types = [], ()
- kwargs['maxterms'] = kwargs.get('maxterms', ctx.prec)
- try:
- return ctx.hypsum(p, q, a_types+b_types, a_s+b_s, z, **kwargs)
- except ctx.NoConvergence:
- pass
- prec = ctx.prec
- try:
- tol = kwargs.get('asymp_tol', ctx.eps/4)
- ctx.prec += 10
- # hypsum is has a conservative tolerance. So we try again:
- def term(k, cache={0:ctx.one}):
- if k in cache:
- return cache[k]
- t = term(k-1)
- for a in a_s: t *= (a+(k-1))
- for b in b_s: t /= (b+(k-1))
- t *= z
- t /= k
- cache[k] = t
- return t
- s = ctx.one
- for k in xrange(1, ctx.prec):
- t = term(k)
- s += t
- if abs(t) <= tol:
- return s
- finally:
- ctx.prec = prec
- if p <= q+3:
- contour = kwargs.get('contour')
- if not contour:
- if ctx.arg(z) < 0.25:
- u = z / max(1, abs(z))
- if ctx.arg(z) >= 0:
- contour = [0, 2j, (2j+2)/u, 2/u, ctx.inf]
- else:
- contour = [0, -2j, (-2j+2)/u, 2/u, ctx.inf]
- #contour = [0, 2j/z, 2/z, ctx.inf]
- #contour = [0, 2j, 2/z, ctx.inf]
- #contour = [0, 2j, ctx.inf]
- else:
- contour = [0, ctx.inf]
- quad_kwargs = kwargs.get('quad_kwargs', {})
- def g(t):
- return ctx.exp(-t)*ctx.hyper(a_s, b_s+[1], t*z)
- I, err = ctx.quad(g, contour, error=True, **quad_kwargs)
- if err <= abs(I)*ctx.eps*8:
- return I
- raise ctx.NoConvergence
- @defun
- def _hyp2f2(ctx, a_s, b_s, z, **kwargs):
- (a1, a1type), (a2, a2type) = a_s
- (b1, b1type), (b2, b2type) = b_s
- absz = abs(z)
- magz = ctx.mag(z)
- orig = ctx.prec
- # Asymptotic expansion is ~ exp(z)
- asymp_extraprec = magz
- # Asymptotic series is in terms of 3F1
- can_use_asymptotic = (not kwargs.get('force_series')) and \
- (ctx.mag(absz) > 3)
- # TODO: much of the following could be shared with 2F3 instead of
- # copypasted
- if can_use_asymptotic:
- #print "using asymp"
- try:
- try:
- ctx.prec += asymp_extraprec
- # http://functions.wolfram.com/HypergeometricFunctions/
- # Hypergeometric2F2/06/02/02/0002/
- def h(a1,a2,b1,b2):
- X = a1+a2-b1-b2
- A2 = a1+a2
- B2 = b1+b2
- c = {}
- c[0] = ctx.one
- c[1] = (A2-1)*X+b1*b2-a1*a2
- s1 = 0
- k = 0
- tprev = 0
- while 1:
- if k not in c:
- uu1 = 1-B2+2*a1+a1**2+2*a2+a2**2-A2*B2+a1*a2+b1*b2+(2*B2-3*(A2+1))*k+2*k**2
- uu2 = (k-A2+b1-1)*(k-A2+b2-1)*(k-X-2)
- c[k] = ctx.one/k * (uu1*c[k-1]-uu2*c[k-2])
- t1 = c[k] * z**(-k)
- if abs(t1) < 0.1*ctx.eps:
- #print "Convergence :)"
- break
- # Quit if the series doesn't converge quickly enough
- if k > 5 and abs(tprev) / abs(t1) < 1.5:
- #print "No convergence :("
- raise ctx.NoConvergence
- s1 += t1
- tprev = t1
- k += 1
- S = ctx.exp(z)*s1
- T1 = [z,S], [X,1], [b1,b2],[a1,a2],[],[],0
- T2 = [-z],[-a1],[b1,b2,a2-a1],[a2,b1-a1,b2-a1],[a1,a1-b1+1,a1-b2+1],[a1-a2+1],-1/z
- T3 = [-z],[-a2],[b1,b2,a1-a2],[a1,b1-a2,b2-a2],[a2,a2-b1+1,a2-b2+1],[-a1+a2+1],-1/z
- return T1, T2, T3
- v = ctx.hypercomb(h, [a1,a2,b1,b2], force_series=True, maxterms=4*ctx.prec)
- if sum(ctx._is_real_type(u) for u in [a1,a2,b1,b2,z]) == 5:
- v = ctx.re(v)
- return v
- except ctx.NoConvergence:
- pass
- finally:
- ctx.prec = orig
- return ctx.hypsum(2, 2, (a1type, a2type, b1type, b2type), [a1, a2, b1, b2], z, **kwargs)
- @defun
- def _hyp1f2(ctx, a_s, b_s, z, **kwargs):
- (a1, a1type), = a_s
- (b1, b1type), (b2, b2type) = b_s
- absz = abs(z)
- magz = ctx.mag(z)
- orig = ctx.prec
- # Asymptotic expansion is ~ exp(sqrt(z))
- asymp_extraprec = z and magz//2
- # Asymptotic series is in terms of 3F0
- can_use_asymptotic = (not kwargs.get('force_series')) and \
- (ctx.mag(absz) > 19) and \
- (ctx.sqrt(absz) > 1.5*orig) # and \
- # ctx._hyp_check_convergence([a1, a1-b1+1, a1-b2+1], [],
- # 1/absz, orig+40+asymp_extraprec)
- # TODO: much of the following could be shared with 2F3 instead of
- # copypasted
- if can_use_asymptotic:
- #print "using asymp"
- try:
- try:
- ctx.prec += asymp_extraprec
- # http://functions.wolfram.com/HypergeometricFunctions/
- # Hypergeometric1F2/06/02/03/
- def h(a1,b1,b2):
- X = ctx.mpq_1_2*(a1-b1-b2+ctx.mpq_1_2)
- c = {}
- c[0] = ctx.one
- c[1] = 2*(ctx.mpq_1_4*(3*a1+b1+b2-2)*(a1-b1-b2)+b1*b2-ctx.mpq_3_16)
- c[2] = 2*(b1*b2+ctx.mpq_1_4*(a1-b1-b2)*(3*a1+b1+b2-2)-ctx.mpq_3_16)**2+\
- ctx.mpq_1_16*(-16*(2*a1-3)*b1*b2 + \
- 4*(a1-b1-b2)*(-8*a1**2+11*a1+b1+b2-2)-3)
- s1 = 0
- s2 = 0
- k = 0
- tprev = 0
- while 1:
- if k not in c:
- uu1 = (3*k**2+(-6*a1+2*b1+2*b2-4)*k + 3*a1**2 - \
- (b1-b2)**2 - 2*a1*(b1+b2-2) + ctx.mpq_1_4)
- uu2 = (k-a1+b1-b2-ctx.mpq_1_2)*(k-a1-b1+b2-ctx.mpq_1_2)*\
- (k-a1+b1+b2-ctx.mpq_5_2)
- c[k] = ctx.one/(2*k)*(uu1*c[k-1]-uu2*c[k-2])
- w = c[k] * (-z)**(-0.5*k)
- t1 = (-ctx.j)**k * ctx.mpf(2)**(-k) * w
- t2 = ctx.j**k * ctx.mpf(2)**(-k) * w
- if abs(t1) < 0.1*ctx.eps:
- #print "Convergence :)"
- break
- # Quit if the series doesn't converge quickly enough
- if k > 5 and abs(tprev) / abs(t1) < 1.5:
- #print "No convergence :("
- raise ctx.NoConvergence
- s1 += t1
- s2 += t2
- tprev = t1
- k += 1
- S = ctx.expj(ctx.pi*X+2*ctx.sqrt(-z))*s1 + \
- ctx.expj(-(ctx.pi*X+2*ctx.sqrt(-z)))*s2
- T1 = [0.5*S, ctx.pi, -z], [1, -0.5, X], [b1, b2], [a1],\
- [], [], 0
- T2 = [-z], [-a1], [b1,b2],[b1-a1,b2-a1], \
- [a1,a1-b1+1,a1-b2+1], [], 1/z
- return T1, T2
- v = ctx.hypercomb(h, [a1,b1,b2], force_series=True, maxterms=4*ctx.prec)
- if sum(ctx._is_real_type(u) for u in [a1,b1,b2,z]) == 4:
- v = ctx.re(v)
- return v
- except ctx.NoConvergence:
- pass
- finally:
- ctx.prec = orig
- #print "not using asymp"
- return ctx.hypsum(1, 2, (a1type, b1type, b2type), [a1, b1, b2], z, **kwargs)
- @defun
- def _hyp2f3(ctx, a_s, b_s, z, **kwargs):
- (a1, a1type), (a2, a2type) = a_s
- (b1, b1type), (b2, b2type), (b3, b3type) = b_s
- absz = abs(z)
- magz = ctx.mag(z)
- # Asymptotic expansion is ~ exp(sqrt(z))
- asymp_extraprec = z and magz//2
- orig = ctx.prec
- # Asymptotic series is in terms of 4F1
- # The square root below empirically provides a plausible criterion
- # for the leading series to converge
- can_use_asymptotic = (not kwargs.get('force_series')) and \
- (ctx.mag(absz) > 19) and (ctx.sqrt(absz) > 1.5*orig)
- if can_use_asymptotic:
- #print "using asymp"
- try:
- try:
- ctx.prec += asymp_extraprec
- # http://functions.wolfram.com/HypergeometricFunctions/
- # Hypergeometric2F3/06/02/03/01/0002/
- def h(a1,a2,b1,b2,b3):
- X = ctx.mpq_1_2*(a1+a2-b1-b2-b3+ctx.mpq_1_2)
- A2 = a1+a2
- B3 = b1+b2+b3
- A = a1*a2
- B = b1*b2+b3*b2+b1*b3
- R = b1*b2*b3
- c = {}
- c[0] = ctx.one
- c[1] = 2*(B - A + ctx.mpq_1_4*(3*A2+B3-2)*(A2-B3) - ctx.mpq_3_16)
- c[2] = ctx.mpq_1_2*c[1]**2 + ctx.mpq_1_16*(-16*(2*A2-3)*(B-A) + 32*R +\
- 4*(-8*A2**2 + 11*A2 + 8*A + B3 - 2)*(A2-B3)-3)
- s1 = 0
- s2 = 0
- k = 0
- tprev = 0
- while 1:
- if k not in c:
- uu1 = (k-2*X-3)*(k-2*X-2*b1-1)*(k-2*X-2*b2-1)*\
- (k-2*X-2*b3-1)
- uu2 = (4*(k-1)**3 - 6*(4*X+B3)*(k-1)**2 + \
- 2*(24*X**2+12*B3*X+4*B+B3-1)*(k-1) - 32*X**3 - \
- 24*B3*X**2 - 4*B - 8*R - 4*(4*B+B3-1)*X + 2*B3-1)
- uu3 = (5*(k-1)**2+2*(-10*X+A2-3*B3+3)*(k-1)+2*c[1])
- c[k] = ctx.one/(2*k)*(uu1*c[k-3]-uu2*c[k-2]+uu3*c[k-1])
- w = c[k] * ctx.power(-z, -0.5*k)
- t1 = (-ctx.j)**k * ctx.mpf(2)**(-k) * w
- t2 = ctx.j**k * ctx.mpf(2)**(-k) * w
- if abs(t1) < 0.1*ctx.eps:
- break
- # Quit if the series doesn't converge quickly enough
- if k > 5 and abs(tprev) / abs(t1) < 1.5:
- raise ctx.NoConvergence
- s1 += t1
- s2 += t2
- tprev = t1
- k += 1
- S = ctx.expj(ctx.pi*X+2*ctx.sqrt(-z))*s1 + \
- ctx.expj(-(ctx.pi*X+2*ctx.sqrt(-z)))*s2
- T1 = [0.5*S, ctx.pi, -z], [1, -0.5, X], [b1, b2, b3], [a1, a2],\
- [], [], 0
- T2 = [-z], [-a1], [b1,b2,b3,a2-a1],[a2,b1-a1,b2-a1,b3-a1], \
- [a1,a1-b1+1,a1-b2+1,a1-b3+1], [a1-a2+1], 1/z
- T3 = [-z], [-a2], [b1,b2,b3,a1-a2],[a1,b1-a2,b2-a2,b3-a2], \
- [a2,a2-b1+1,a2-b2+1,a2-b3+1],[-a1+a2+1], 1/z
- return T1, T2, T3
- v = ctx.hypercomb(h, [a1,a2,b1,b2,b3], force_series=True, maxterms=4*ctx.prec)
- if sum(ctx._is_real_type(u) for u in [a1,a2,b1,b2,b3,z]) == 6:
- v = ctx.re(v)
- return v
- except ctx.NoConvergence:
- pass
- finally:
- ctx.prec = orig
- return ctx.hypsum(2, 3, (a1type, a2type, b1type, b2type, b3type), [a1, a2, b1, b2, b3], z, **kwargs)
- @defun
- def _hyp2f0(ctx, a_s, b_s, z, **kwargs):
- (a, atype), (b, btype) = a_s
- # We want to try aggressively to use the asymptotic expansion,
- # and fall back only when absolutely necessary
- try:
- kwargsb = kwargs.copy()
- kwargsb['maxterms'] = kwargsb.get('maxterms', ctx.prec)
- return ctx.hypsum(2, 0, (atype,btype), [a,b], z, **kwargsb)
- except ctx.NoConvergence:
- if kwargs.get('force_series'):
- raise
- pass
- def h(a, b):
- w = ctx.sinpi(b)
- rz = -1/z
- T1 = ([ctx.pi,w,rz],[1,-1,a],[],[a-b+1,b],[a],[b],rz)
- T2 = ([-ctx.pi,w,rz],[1,-1,1+a-b],[],[a,2-b],[a-b+1],[2-b],rz)
- return T1, T2
- return ctx.hypercomb(h, [a, 1+a-b], **kwargs)
- @defun
- def meijerg(ctx, a_s, b_s, z, r=1, series=None, **kwargs):
- an, ap = a_s
- bm, bq = b_s
- n = len(an)
- p = n + len(ap)
- m = len(bm)
- q = m + len(bq)
- a = an+ap
- b = bm+bq
- a = [ctx.convert(_) for _ in a]
- b = [ctx.convert(_) for _ in b]
- z = ctx.convert(z)
- if series is None:
- if p < q: series = 1
- if p > q: series = 2
- if p == q:
- if m+n == p and abs(z) > 1:
- series = 2
- else:
- series = 1
- if kwargs.get('verbose'):
- print("Meijer G m,n,p,q,series =", m,n,p,q,series)
- if series == 1:
- def h(*args):
- a = args[:p]
- b = args[p:]
- terms = []
- for k in range(m):
- bases = [z]
- expts = [b[k]/r]
- gn = [b[j]-b[k] for j in range(m) if j != k]
- gn += [1-a[j]+b[k] for j in range(n)]
- gd = [a[j]-b[k] for j in range(n,p)]
- gd += [1-b[j]+b[k] for j in range(m,q)]
- hn = [1-a[j]+b[k] for j in range(p)]
- hd = [1-b[j]+b[k] for j in range(q) if j != k]
- hz = (-ctx.one)**(p-m-n) * z**(ctx.one/r)
- terms.append((bases, expts, gn, gd, hn, hd, hz))
- return terms
- else:
- def h(*args):
- a = args[:p]
- b = args[p:]
- terms = []
- for k in range(n):
- bases = [z]
- if r == 1:
- expts = [a[k]-1]
- else:
- expts = [(a[k]-1)/ctx.convert(r)]
- gn = [a[k]-a[j] for j in range(n) if j != k]
- gn += [1-a[k]+b[j] for j in range(m)]
- gd = [a[k]-b[j] for j in range(m,q)]
- gd += [1-a[k]+a[j] for j in range(n,p)]
- hn = [1-a[k]+b[j] for j in range(q)]
- hd = [1+a[j]-a[k] for j in range(p) if j != k]
- hz = (-ctx.one)**(q-m-n) / z**(ctx.one/r)
- terms.append((bases, expts, gn, gd, hn, hd, hz))
- return terms
- return ctx.hypercomb(h, a+b, **kwargs)
- @defun_wrapped
- def appellf1(ctx,a,b1,b2,c,x,y,**kwargs):
- # Assume x smaller
- # We will use x for the outer loop
- if abs(x) > abs(y):
- x, y = y, x
- b1, b2 = b2, b1
- def ok(x):
- return abs(x) < 0.99
- # Finite cases
- if ctx.isnpint(a):
- pass
- elif ctx.isnpint(b1):
- pass
- elif ctx.isnpint(b2):
- x, y, b1, b2 = y, x, b2, b1
- else:
- #print x, y
- # Note: ok if |y| > 1, because
- # 2F1 implements analytic continuation
- if not ok(x):
- u1 = (x-y)/(x-1)
- if not ok(u1):
- raise ValueError("Analytic continuation not implemented")
- #print "Using analytic continuation"
- return (1-x)**(-b1)*(1-y)**(c-a-b2)*\
- ctx.appellf1(c-a,b1,c-b1-b2,c,u1,y,**kwargs)
- return ctx.hyper2d({'m+n':[a],'m':[b1],'n':[b2]}, {'m+n':[c]}, x,y, **kwargs)
- @defun
- def appellf2(ctx,a,b1,b2,c1,c2,x,y,**kwargs):
- # TODO: continuation
- return ctx.hyper2d({'m+n':[a],'m':[b1],'n':[b2]},
- {'m':[c1],'n':[c2]}, x,y, **kwargs)
- @defun
- def appellf3(ctx,a1,a2,b1,b2,c,x,y,**kwargs):
- outer_polynomial = ctx.isnpint(a1) or ctx.isnpint(b1)
- inner_polynomial = ctx.isnpint(a2) or ctx.isnpint(b2)
- if not outer_polynomial:
- if inner_polynomial or abs(x) > abs(y):
- x, y = y, x
- a1,a2,b1,b2 = a2,a1,b2,b1
- return ctx.hyper2d({'m':[a1,b1],'n':[a2,b2]}, {'m+n':[c]},x,y,**kwargs)
- @defun
- def appellf4(ctx,a,b,c1,c2,x,y,**kwargs):
- # TODO: continuation
- return ctx.hyper2d({'m+n':[a,b]}, {'m':[c1],'n':[c2]},x,y,**kwargs)
- @defun
- def hyper2d(ctx, a, b, x, y, **kwargs):
- r"""
- Sums the generalized 2D hypergeometric series
- .. math ::
- \sum_{m=0}^{\infty} \sum_{n=0}^{\infty}
- \frac{P((a),m,n)}{Q((b),m,n)}
- \frac{x^m y^n} {m! n!}
- where `(a) = (a_1,\ldots,a_r)`, `(b) = (b_1,\ldots,b_s)` and where
- `P` and `Q` are products of rising factorials such as `(a_j)_n` or
- `(a_j)_{m+n}`. `P` and `Q` are specified in the form of dicts, with
- the `m` and `n` dependence as keys and parameter lists as values.
- The supported rising factorials are given in the following table
- (note that only a few are supported in `Q`):
- +------------+-------------------+--------+
- | Key | Rising factorial | `Q` |
- +============+===================+========+
- | ``'m'`` | `(a_j)_m` | Yes |
- +------------+-------------------+--------+
- | ``'n'`` | `(a_j)_n` | Yes |
- +------------+-------------------+--------+
- | ``'m+n'`` | `(a_j)_{m+n}` | Yes |
- +------------+-------------------+--------+
- | ``'m-n'`` | `(a_j)_{m-n}` | No |
- +------------+-------------------+--------+
- | ``'n-m'`` | `(a_j)_{n-m}` | No |
- +------------+-------------------+--------+
- | ``'2m+n'`` | `(a_j)_{2m+n}` | No |
- +------------+-------------------+--------+
- | ``'2m-n'`` | `(a_j)_{2m-n}` | No |
- +------------+-------------------+--------+
- | ``'2n-m'`` | `(a_j)_{2n-m}` | No |
- +------------+-------------------+--------+
- For example, the Appell F1 and F4 functions
- .. math ::
- F_1 = \sum_{m=0}^{\infty} \sum_{n=0}^{\infty}
- \frac{(a)_{m+n} (b)_m (c)_n}{(d)_{m+n}}
- \frac{x^m y^n}{m! n!}
- F_4 = \sum_{m=0}^{\infty} \sum_{n=0}^{\infty}
- \frac{(a)_{m+n} (b)_{m+n}}{(c)_m (d)_{n}}
- \frac{x^m y^n}{m! n!}
- can be represented respectively as
- ``hyper2d({'m+n':[a], 'm':[b], 'n':[c]}, {'m+n':[d]}, x, y)``
- ``hyper2d({'m+n':[a,b]}, {'m':[c], 'n':[d]}, x, y)``
- More generally, :func:`~mpmath.hyper2d` can evaluate any of the 34 distinct
- convergent second-order (generalized Gaussian) hypergeometric
- series enumerated by Horn, as well as the Kampe de Feriet
- function.
- The series is computed by rewriting it so that the inner
- series (i.e. the series containing `n` and `y`) has the form of an
- ordinary generalized hypergeometric series and thereby can be
- evaluated efficiently using :func:`~mpmath.hyper`. If possible,
- manually swapping `x` and `y` and the corresponding parameters
- can sometimes give better results.
- **Examples**
- Two separable cases: a product of two geometric series, and a
- product of two Gaussian hypergeometric functions::
- >>> from mpmath import *
- >>> mp.dps = 25; mp.pretty = True
- >>> x, y = mpf(0.25), mpf(0.5)
- >>> hyper2d({'m':1,'n':1}, {}, x,y)
- 2.666666666666666666666667
- >>> 1/(1-x)/(1-y)
- 2.666666666666666666666667
- >>> hyper2d({'m':[1,2],'n':[3,4]}, {'m':[5],'n':[6]}, x,y)
- 4.164358531238938319669856
- >>> hyp2f1(1,2,5,x)*hyp2f1(3,4,6,y)
- 4.164358531238938319669856
- Some more series that can be done in closed form::
- >>> hyper2d({'m':1,'n':1},{'m+n':1},x,y)
- 2.013417124712514809623881
- >>> (exp(x)*x-exp(y)*y)/(x-y)
- 2.013417124712514809623881
- Six of the 34 Horn functions, G1-G3 and H1-H3::
- >>> from mpmath import *
- >>> mp.dps = 10; mp.pretty = True
- >>> x, y = 0.0625, 0.125
- >>> a1,a2,b1,b2,c1,c2,d = 1.1,-1.2,-1.3,-1.4,1.5,-1.6,1.7
- >>> hyper2d({'m+n':a1,'n-m':b1,'m-n':b2},{},x,y) # G1
- 1.139090746
- >>> nsum(lambda m,n: rf(a1,m+n)*rf(b1,n-m)*rf(b2,m-n)*\
- ... x**m*y**n/fac(m)/fac(n), [0,inf], [0,inf])
- 1.139090746
- >>> hyper2d({'m':a1,'n':a2,'n-m':b1,'m-n':b2},{},x,y) # G2
- 0.9503682696
- >>> nsum(lambda m,n: rf(a1,m)*rf(a2,n)*rf(b1,n-m)*rf(b2,m-n)*\
- ... x**m*y**n/fac(m)/fac(n), [0,inf], [0,inf])
- 0.9503682696
- >>> hyper2d({'2n-m':a1,'2m-n':a2},{},x,y) # G3
- 1.029372029
- >>> nsum(lambda m,n: rf(a1,2*n-m)*rf(a2,2*m-n)*\
- ... x**m*y**n/fac(m)/fac(n), [0,inf], [0,inf])
- 1.029372029
- >>> hyper2d({'m-n':a1,'m+n':b1,'n':c1},{'m':d},x,y) # H1
- -1.605331256
- >>> nsum(lambda m,n: rf(a1,m-n)*rf(b1,m+n)*rf(c1,n)/rf(d,m)*\
- ... x**m*y**n/fac(m)/fac(n), [0,inf], [0,inf])
- -1.605331256
- >>> hyper2d({'m-n':a1,'m':b1,'n':[c1,c2]},{'m':d},x,y) # H2
- -2.35405404
- >>> nsum(lambda m,n: rf(a1,m-n)*rf(b1,m)*rf(c1,n)*rf(c2,n)/rf(d,m)*\
- ... x**m*y**n/fac(m)/fac(n), [0,inf], [0,inf])
- -2.35405404
- >>> hyper2d({'2m+n':a1,'n':b1},{'m+n':c1},x,y) # H3
- 0.974479074
- >>> nsum(lambda m,n: rf(a1,2*m+n)*rf(b1,n)/rf(c1,m+n)*\
- ... x**m*y**n/fac(m)/fac(n), [0,inf], [0,inf])
- 0.974479074
- **References**
- 1. [SrivastavaKarlsson]_
- 2. [Weisstein]_ http://mathworld.wolfram.com/HornFunction.html
- 3. [Weisstein]_ http://mathworld.wolfram.com/AppellHypergeometricFunction.html
- """
- x = ctx.convert(x)
- y = ctx.convert(y)
- def parse(dct, key):
- args = dct.pop(key, [])
- try:
- args = list(args)
- except TypeError:
- args = [args]
- return [ctx.convert(arg) for arg in args]
- a_s = dict(a)
- b_s = dict(b)
- a_m = parse(a, 'm')
- a_n = parse(a, 'n')
- a_m_add_n = parse(a, 'm+n')
- a_m_sub_n = parse(a, 'm-n')
- a_n_sub_m = parse(a, 'n-m')
- a_2m_add_n = parse(a, '2m+n')
- a_2m_sub_n = parse(a, '2m-n')
- a_2n_sub_m = parse(a, '2n-m')
- b_m = parse(b, 'm')
- b_n = parse(b, 'n')
- b_m_add_n = parse(b, 'm+n')
- if a: raise ValueError("unsupported key: %r" % a.keys()[0])
- if b: raise ValueError("unsupported key: %r" % b.keys()[0])
- s = 0
- outer = ctx.one
- m = ctx.mpf(0)
- ok_count = 0
- prec = ctx.prec
- maxterms = kwargs.get('maxterms', 20*prec)
- try:
- ctx.prec += 10
- tol = +ctx.eps
- while 1:
- inner_sign = 1
- outer_sign = 1
- inner_a = list(a_n)
- inner_b = list(b_n)
- outer_a = [a+m for a in a_m]
- outer_b = [b+m for b in b_m]
- # (a)_{m+n} = (a)_m (a+m)_n
- for a in a_m_add_n:
- a = a+m
- inner_a.append(a)
- outer_a.append(a)
- # (b)_{m+n} = (b)_m (b+m)_n
- for b in b_m_add_n:
- b = b+m
- inner_b.append(b)
- outer_b.append(b)
- # (a)_{n-m} = (a-m)_n / (a-m)_m
- for a in a_n_sub_m:
- inner_a.append(a-m)
- outer_b.append(a-m-1)
- # (a)_{m-n} = (-1)^(m+n) (1-a-m)_m / (1-a-m)_n
- for a in a_m_sub_n:
- inner_sign *= (-1)
- outer_sign *= (-1)**(m)
- inner_b.append(1-a-m)
- outer_a.append(-a-m)
- # (a)_{2m+n} = (a)_{2m} (a+2m)_n
- for a in a_2m_add_n:
- inner_a.append(a+2*m)
- outer_a.append((a+2*m)*(1+a+2*m))
- # (a)_{2m-n} = (-1)^(2m+n) (1-a-2m)_{2m} / (1-a-2m)_n
- for a in a_2m_sub_n:
- inner_sign *= (-1)
- inner_b.append(1-a-2*m)
- outer_a.append((a+2*m)*(1+a+2*m))
- # (a)_{2n-m} = 4^n ((a-m)/2)_n ((a-m+1)/2)_n / (a-m)_m
- for a in a_2n_sub_m:
- inner_sign *= 4
- inner_a.append(0.5*(a-m))
- inner_a.append(0.5*(a-m+1))
- outer_b.append(a-m-1)
- inner = ctx.hyper(inner_a, inner_b, inner_sign*y,
- zeroprec=ctx.prec, **kwargs)
- term = outer * inner * outer_sign
- if abs(term) < tol:
- ok_count += 1
- else:
- ok_count = 0
- if ok_count >= 3 or not outer:
- break
- s += term
- for a in outer_a: outer *= a
- for b in outer_b: outer /= b
- m += 1
- outer = outer * x / m
- if m > maxterms:
- raise ctx.NoConvergence("maxterms exceeded in hyper2d")
- finally:
- ctx.prec = prec
- return +s
- """
- @defun
- def kampe_de_feriet(ctx,a,b,c,d,e,f,x,y,**kwargs):
- return ctx.hyper2d({'m+n':a,'m':b,'n':c},
- {'m+n':d,'m':e,'n':f}, x,y, **kwargs)
- """
- @defun
- def bihyper(ctx, a_s, b_s, z, **kwargs):
- r"""
- Evaluates the bilateral hypergeometric series
- .. math ::
- \,_AH_B(a_1, \ldots, a_k; b_1, \ldots, b_B; z) =
- \sum_{n=-\infty}^{\infty}
- \frac{(a_1)_n \ldots (a_A)_n}
- {(b_1)_n \ldots (b_B)_n} \, z^n
- where, for direct convergence, `A = B` and `|z| = 1`, although a
- regularized sum exists more generally by considering the
- bilateral series as a sum of two ordinary hypergeometric
- functions. In order for the series to make sense, none of the
- parameters may be integers.
- **Examples**
- The value of `\,_2H_2` at `z = 1` is given by Dougall's formula::
- >>> from mpmath import *
- >>> mp.dps = 25; mp.pretty = True
- >>> a,b,c,d = 0.5, 1.5, 2.25, 3.25
- >>> bihyper([a,b],[c,d],1)
- -14.49118026212345786148847
- >>> gammaprod([c,d,1-a,1-b,c+d-a-b-1],[c-a,d-a,c-b,d-b])
- -14.49118026212345786148847
- The regularized function `\,_1H_0` can be expressed as the
- sum of one `\,_2F_0` function and one `\,_1F_1` function::
- >>> a = mpf(0.25)
- >>> z = mpf(0.75)
- >>> bihyper([a], [], z)
- (0.2454393389657273841385582 + 0.2454393389657273841385582j)
- >>> hyper([a,1],[],z) + (hyper([1],[1-a],-1/z)-1)
- (0.2454393389657273841385582 + 0.2454393389657273841385582j)
- >>> hyper([a,1],[],z) + hyper([1],[2-a],-1/z)/z/(a-1)
- (0.2454393389657273841385582 + 0.2454393389657273841385582j)
- **References**
- 1. [Slater]_ (chapter 6: "Bilateral Series", pp. 180-189)
- 2. [Wikipedia]_ http://en.wikipedia.org/wiki/Bilateral_hypergeometric_series
- """
- z = ctx.convert(z)
- c_s = a_s + b_s
- p = len(a_s)
- q = len(b_s)
- if (p, q) == (0,0) or (p, q) == (1,1):
- return ctx.zero * z
- neg = (p-q) % 2
- def h(*c_s):
- a_s = list(c_s[:p])
- b_s = list(c_s[p:])
- aa_s = [2-b for b in b_s]
- bb_s = [2-a for a in a_s]
- rp = [(-1)**neg * z] + [1-b for b in b_s] + [1-a for a in a_s]
- rc = [-1] + [1]*len(b_s) + [-1]*len(a_s)
- T1 = [], [], [], [], a_s + [1], b_s, z
- T2 = rp, rc, [], [], aa_s + [1], bb_s, (-1)**neg / z
- return T1, T2
- return ctx.hypercomb(h, c_s, **kwargs)
|