|
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 |
|
|
|
|
|
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] |
|
|
|
for data_index, data in enumerate([alpha_s, beta_s, b_s]): |
|
for i, x in enumerate(data): |
|
n, d = ctx.nint_distance(x) |
|
|
|
if n > 0: |
|
continue |
|
if d == ctx.ninf: |
|
|
|
|
|
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 |
|
|
|
|
|
|
|
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 |
|
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 |
|
|
|
|
|
|
|
|
|
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.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 |
|
|
|
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] |
|
|
|
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 |
|
|
|
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: |
|
|
|
|
|
|
|
|
|
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): |
|
|
|
|
|
_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 |
|
|
|
|
|
|
|
z = ctx.convert(_z) |
|
d = ctx.mpf(0) |
|
e = ctx.mpf(1) |
|
f = ctx.mpf(0) |
|
k = 0 |
|
|
|
|
|
|
|
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: |
|
|
|
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)) |
|
|
|
|
|
if (convergent or finite) and not zerodiv: |
|
return ctx.gammaprod([c, c-a-b], [c-a, c-b], _infsign=True) |
|
|
|
|
|
|
|
return ctx.hyp2f1(a,b,c,1-ctx.eps*2) * ctx.inf |
|
|
|
|
|
|
|
if not z: |
|
|
|
|
|
if c or a == 0 or b == 0: |
|
return 1+z |
|
|
|
return ctx.nan |
|
|
|
|
|
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: |
|
|
|
return ctx.inf |
|
|
|
absz = abs(z) |
|
|
|
|
|
|
|
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 |
|
|
|
|
|
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) |
|
|
|
|
|
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) |
|
|
|
|
|
elif abs(z/(z-1)) <= 0.75: |
|
v = ctx.hyp2f1(a, c-b, c, z/(z-1)) / (1-z)**a |
|
|
|
|
|
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 |
|
|
|
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 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if z == 1: |
|
|
|
|
|
S = ctx.re(sum(b_s)-sum(a_s)) |
|
if S <= 0: |
|
|
|
return ctx.hyper(a_s, b_s, 0.9, **kwargs) * ctx.inf |
|
if (p,q) == (3,2) and abs(z-1) < 0.05: |
|
|
|
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 |
|
|
|
|
|
|
|
|
|
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 |
|
|
|
|
|
ctx.prec += ctx.prec//2 |
|
if i == 4: |
|
raise ctx.NoConvergence(\ |
|
"Euler-Maclaurin summation did not converge") |
|
finally: |
|
ctx.prec = prec |
|
return +v |
|
|
|
|
|
|
|
|
|
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 |
|
|
|
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] |
|
|
|
|
|
|
|
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 |
|
|
|
|
|
asymp_extraprec = magz |
|
|
|
|
|
can_use_asymptotic = (not kwargs.get('force_series')) and \ |
|
(ctx.mag(absz) > 3) |
|
|
|
|
|
|
|
if can_use_asymptotic: |
|
|
|
try: |
|
try: |
|
ctx.prec += asymp_extraprec |
|
|
|
|
|
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: |
|
|
|
break |
|
|
|
if k > 5 and abs(tprev) / abs(t1) < 1.5: |
|
|
|
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 |
|
|
|
|
|
asymp_extraprec = z and magz//2 |
|
|
|
|
|
can_use_asymptotic = (not kwargs.get('force_series')) and \ |
|
(ctx.mag(absz) > 19) and \ |
|
(ctx.sqrt(absz) > 1.5*orig) |
|
|
|
|
|
|
|
|
|
|
|
if can_use_asymptotic: |
|
|
|
try: |
|
try: |
|
ctx.prec += asymp_extraprec |
|
|
|
|
|
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: |
|
|
|
break |
|
|
|
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], [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 |
|
|
|
|
|
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) |
|
|
|
|
|
asymp_extraprec = z and magz//2 |
|
orig = ctx.prec |
|
|
|
|
|
|
|
|
|
can_use_asymptotic = (not kwargs.get('force_series')) and \ |
|
(ctx.mag(absz) > 19) and (ctx.sqrt(absz) > 1.5*orig) |
|
|
|
if can_use_asymptotic: |
|
|
|
try: |
|
try: |
|
ctx.prec += asymp_extraprec |
|
|
|
|
|
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 |
|
|
|
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 |
|
|
|
|
|
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): |
|
|
|
|
|
if abs(x) > abs(y): |
|
x, y = y, x |
|
b1, b2 = b2, b1 |
|
def ok(x): |
|
return abs(x) < 0.99 |
|
|
|
if ctx.isnpint(a): |
|
pass |
|
elif ctx.isnpint(b1): |
|
pass |
|
elif ctx.isnpint(b2): |
|
x, y, b1, b2 = y, x, b2, b1 |
|
else: |
|
|
|
|
|
|
|
if not ok(x): |
|
u1 = (x-y)/(x-1) |
|
if not ok(u1): |
|
raise ValueError("Analytic continuation not implemented") |
|
|
|
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): |
|
|
|
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): |
|
|
|
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] |
|
|
|
for a in a_m_add_n: |
|
a = a+m |
|
inner_a.append(a) |
|
outer_a.append(a) |
|
|
|
for b in b_m_add_n: |
|
b = b+m |
|
inner_b.append(b) |
|
outer_b.append(b) |
|
|
|
for a in a_n_sub_m: |
|
inner_a.append(a-m) |
|
outer_b.append(a-m-1) |
|
|
|
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) |
|
|
|
for a in a_2m_add_n: |
|
inner_a.append(a+2*m) |
|
outer_a.append((a+2*m)*(1+a+2*m)) |
|
|
|
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)) |
|
|
|
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) |
|
|