attempt to improve bounds for Airy functions

This commit is contained in:
fredrik 2018-02-11 22:28:49 +01:00
parent a7d9c0b1c1
commit d64055fb3f
3 changed files with 362 additions and 143 deletions

View file

@ -102,8 +102,8 @@ estimate_airy(double x, double y, int ai)
/* error propagation based on derivatives */ /* error propagation based on derivatives */
void void
acb_hypgeom_airy_direct_prop(acb_t ai, acb_t aip, acb_t bi, acb_t bip, acb_hypgeom_airy_prop(acb_t ai, acb_t aip, acb_t bi, acb_t bip,
const acb_t z, slong n, slong prec) const acb_t z, slong n, int algo, slong prec)
{ {
mag_t aib, aipb, bib, bipb, zb, rad; mag_t aib, aipb, bib, bipb, zb, rad;
acb_t zz; acb_t zz;
@ -124,7 +124,10 @@ acb_hypgeom_airy_direct_prop(acb_t ai, acb_t aip, acb_t bi, acb_t bip,
acb_get_mag(zb, z); acb_get_mag(zb, z);
acb_hypgeom_airy_bound(aib, aipb, bib, bipb, z); acb_hypgeom_airy_bound(aib, aipb, bib, bipb, z);
if (algo == 0)
acb_hypgeom_airy_direct(ai, aip, bi, bip, zz, n, prec); acb_hypgeom_airy_direct(ai, aip, bi, bip, zz, n, prec);
else
acb_hypgeom_airy_asymp(ai, aip, bi, bip, zz, n, prec);
if (ai != NULL) if (ai != NULL)
{ {
@ -173,6 +176,24 @@ acb_hypgeom_airy_direct_prop(acb_t ai, acb_t aip, acb_t bi, acb_t bip,
acb_clear(zz); acb_clear(zz);
} }
void
acb_hypgeom_airy_direct_prop(acb_t ai, acb_t aip, acb_t bi, acb_t bip,
const acb_t z, slong n, slong prec)
{
acb_hypgeom_airy_prop(ai, aip, bi, bip, z, n, 0, prec);
}
void
acb_hypgeom_airy_asymp2(acb_t ai, acb_t aip, acb_t bi, acb_t bip,
const acb_t z, slong n, slong prec)
{
/* avoid singularity in asymptotic expansion near 0 */
if (acb_rel_accuracy_bits(z) > 3)
acb_hypgeom_airy_asymp(ai, aip, bi, bip, z, n, prec);
else
acb_hypgeom_airy_prop(ai, aip, bi, bip, z, n, 1, prec);
}
void void
acb_hypgeom_airy(acb_t ai, acb_t aip, acb_t bi, acb_t bip, const acb_t z, slong prec) acb_hypgeom_airy(acb_t ai, acb_t aip, acb_t bi, acb_t bip, const acb_t z, slong prec)
{ {
@ -210,7 +231,10 @@ acb_hypgeom_airy(acb_t ai, acb_t aip, acb_t bi, acb_t bip, const acb_t z, slong
n = wp / (-zmag) + 1; n = wp / (-zmag) + 1;
} }
if (acb_is_exact(z))
acb_hypgeom_airy_direct(ai, aip, bi, bip, z, n, wp); acb_hypgeom_airy_direct(ai, aip, bi, bip, z, n, wp);
else
acb_hypgeom_airy_direct_prop(ai, aip, bi, bip, z, n, wp);
} /* huge input -- use asymptotics and pick n without overflowing */ } /* huge input -- use asymptotics and pick n without overflowing */
else if ((arf_cmpabs_2exp_si(re, 64) > 0 || arf_cmpabs_2exp_si(im, 64) > 0)) else if ((arf_cmpabs_2exp_si(re, 64) > 0 || arf_cmpabs_2exp_si(im, 64) > 0))
{ {
@ -227,7 +251,7 @@ acb_hypgeom_airy(acb_t ai, acb_t aip, acb_t bi, acb_t bip, const acb_t z, slong
n = FLINT_MAX(n, 1); n = FLINT_MAX(n, 1);
} }
acb_hypgeom_airy_asymp(ai, aip, bi, bip, z, n, wp); acb_hypgeom_airy_asymp2(ai, aip, bi, bip, z, n, wp);
} }
else /* moderate input */ else /* moderate input */
{ {
@ -239,14 +263,18 @@ acb_hypgeom_airy(acb_t ai, acb_t aip, acb_t bi, acb_t bip, const acb_t z, slong
if (zmag >= 4.0 && (n = asymp_pick_terms(wp, log(zmag))) != -1) if (zmag >= 4.0 && (n = asymp_pick_terms(wp, log(zmag))) != -1)
{ {
acb_hypgeom_airy_asymp(ai, aip, bi, bip, z, n, wp); acb_hypgeom_airy_asymp2(ai, aip, bi, bip, z, n, wp);
} }
else if (zmag <= 1.5) else if (zmag <= 1.5)
{ {
t = 3 * (wp * LOG2) / (2 * z15 * EXP1); t = 3 * (wp * LOG2) / (2 * z15 * EXP1);
t = (wp * LOG2) / (2 * d_lambertw(t)); t = (wp * LOG2) / (2 * d_lambertw(t));
n = FLINT_MAX(t + 1, 2); n = FLINT_MAX(t + 1, 2);
if (acb_is_exact(z))
acb_hypgeom_airy_direct(ai, aip, bi, bip, z, n, wp); acb_hypgeom_airy_direct(ai, aip, bi, bip, z, n, wp);
else
acb_hypgeom_airy_direct_prop(ai, aip, bi, bip, z, n, wp);
} }
else else
{ {

View file

@ -24,65 +24,29 @@ arb_bound_exp_neg(mag_t b, const arb_t x)
arb_clear(t); arb_clear(t);
} }
/* todo -- should be lt in asymp code? */ /* Implements DLMF 9.7.17. We assume |zeta| >= 1/2 and |arg(z)| <= 2 pi/3 here,
static int ignoring the smaller points which must be dealt with separately. */
arg_le_2pi3(const acb_t z, const acb_t zeta)
{
if (arb_is_nonnegative(acb_realref(z)))
return 1;
if (arb_is_positive(acb_imagref(z)) &&
arb_is_nonnegative(acb_imagref(zeta)))
return 1;
if (arb_is_negative(acb_imagref(z)) &&
arb_is_nonpositive(acb_imagref(zeta)))
return 1;
return 0;
}
void void
acb_hypgeom_airy_bound_9_7_17(mag_t bound, const acb_t z, const acb_t zeta) acb_hypgeom_airy_bound_9_7_17(mag_t bound, const acb_t z, const acb_t zeta)
{ {
mag_t D, t, u, v, zeta_lower; mag_t D, t, u, v, zeta_lower, half;
mag_init(D); mag_init(D);
mag_init(t); mag_init(t);
mag_init(u); mag_init(u);
mag_init(v); mag_init(v);
mag_init(zeta_lower); mag_init(zeta_lower);
mag_init(half);
mag_one(half);
mag_mul_2exp_si(half, half, -1);
acb_get_mag_lower(zeta_lower, zeta); acb_get_mag_lower(zeta_lower, zeta);
mag_max(zeta_lower, zeta_lower, half); /* by assumption */
/* 2 chi(1) exp(7 pi / (72 |zeta|)) * c_1 */ /* 2 chi(1) exp(7 pi / (72 |zeta|)) * c_1 */
/* simplified bound */ /* simplified bound assuming |zeta| >= 1/2 */
if (mag_cmp_2exp_si(zeta_lower, -1) >= 0)
mag_one(D); mag_one(D);
else
mag_inf(D);
if (!arg_le_2pi3(z, zeta))
{
arb_get_mag_lower(u, acb_realref(zeta));
arb_get_mag(v, acb_imagref(zeta));
/* exp(7 pi / (36 u)) < exp(5/(8 u)) */
mag_set_ui_2exp_si(t, 5, -3);
mag_div(t, t, u);
mag_exp(t, t);
/* |1/cos(arg(zeta))| = sqrt(1+(v/u)^2) */
mag_div(v, v, u);
mag_mul(v, v, v);
mag_one(u);
mag_add(v, v, u);
mag_sqrt(v, v);
mag_mul(t, t, v);
/* c_1 * 4 chi(1) < 0.62 < 1 -- do nothing */
mag_max(D, D, t);
}
/* exp(-zeta) / (2 sqrt(pi)) * (1 + D / |zeta|) */ /* exp(-zeta) / (2 sqrt(pi)) * (1 + D / |zeta|) */
@ -106,38 +70,60 @@ acb_hypgeom_airy_bound_9_7_17(mag_t bound, const acb_t z, const acb_t zeta)
} }
void void
acb_hypgeom_airy_bound(mag_t ai, mag_t aip, mag_t bi, mag_t bip, const acb_t z) acb_hypgeom_airy_bound_arg_le_2pi3(mag_t A, mag_t B, const acb_t z, slong wp)
{ {
acb_t zeta; acb_t zeta, z1;
acb_t z1, z2;
arf_srcptr zre, zim;
mag_t A, B, D, zlo, zhi;
slong wp;
if (acb_contains_zero(z))
{
if (ai != NULL) mag_inf(ai);
if (aip != NULL) mag_inf(aip);
if (bi != NULL) mag_inf(bi);
if (bip != NULL) mag_inf(bip);
return;
}
acb_init(zeta); acb_init(zeta);
mag_init(A); acb_init(z1);
mag_init(B);
mag_init(D);
mag_init(zlo);
mag_init(zhi);
wp = MAG_BITS * 2; acb_set_round(zeta, z, wp);
zre = arb_midref(acb_realref(z)); acb_sqrt(zeta, zeta, wp);
zim = arb_midref(acb_imagref(z)); acb_cube(zeta, zeta, wp);
acb_mul_2exp_si(zeta, zeta, 1);
acb_div_ui(zeta, zeta, 3, wp);
/* near the negative half line, use acb_hypgeom_airy_bound_9_7_17(A, z, zeta);
|Ai(-z)|, |Bi(-z)| <= |Ai(z exp(pi i/3))| + |Ai(z exp(-pi i/3))| */
if (arf_sgn(zre) < 0 && arf_cmpabs(zre, zim) > 0) /* Use Bi(z) = w_1 Ai(z) + 2 w_2 Ai(z exp(+/- 2pi i / 3)),
where w_1, w_2 are roots of unity */
if (B != NULL)
{ {
arb_sqrt_ui(acb_imagref(z1), 3, wp);
arb_set_si(acb_realref(z1), -1);
acb_mul_2exp_si(z1, z1, -1);
/* multiply by exp(-2 pi i / 3) in upper half plane
and by exp(2 pi i / 3) in lower half plane, to stay close
to positive reals */
/* in principle, we should be computing the union of both cases,
but since Bi is conjugate symmetric with the maximum value
attained on the positive real line, it's sufficient to consider
the case centered on the midpoint */
if (arf_sgn(arb_midref(acb_imagref(z))) >= 0)
acb_conj(z1, z1);
acb_mul(z1, z1, z, wp);
acb_neg(zeta, zeta); /* same effect regardless of exp(+/-2 pi i/3) */
acb_hypgeom_airy_bound_9_7_17(B, z1, zeta);
mag_mul_2exp_si(B, B, 1);
mag_add(B, B, A);
}
acb_clear(zeta);
acb_clear(z1);
}
/* near the negative half line, use
|Ai(-z)|, |Bi(-z)| <= |Ai(z exp(pi i/3))| + |Ai(z exp(-pi i/3))| */
void
acb_hypgeom_airy_bound_arg_ge_2pi3(mag_t A, mag_t B, const acb_t z, slong wp)
{
acb_t zeta, z1, z2;
acb_init(zeta);
acb_init(z1); acb_init(z1);
acb_init(z2); acb_init(z2);
@ -164,56 +150,211 @@ acb_hypgeom_airy_bound(mag_t ai, mag_t aip, mag_t bi, mag_t bip, const acb_t z)
} }
else else
{ {
mag_t D;
mag_init(D);
acb_mul(z2, z2, zeta, wp); acb_mul(z2, z2, zeta, wp);
acb_neg(zeta, zeta); acb_neg(zeta, zeta);
acb_hypgeom_airy_bound_9_7_17(D, z2, zeta); acb_hypgeom_airy_bound_9_7_17(D, z2, zeta);
mag_add(A, A, D); mag_add(A, A, D);
mag_clear(D);
} }
if (B != NULL)
mag_set(B, A); mag_set(B, A);
acb_clear(zeta);
acb_clear(z1); acb_clear(z1);
acb_clear(z2); acb_clear(z2);
}
static int
arg_lt_2pi3_fast(const acb_t z)
{
arf_t t;
mag_t x, y, s;
int res;
if (arb_is_zero(acb_imagref(z)) && arb_is_nonnegative(acb_realref(z)))
return 1;
arf_init(t);
mag_init(x);
mag_init(y);
mag_init(s);
arf_set_mag(t, arb_radref(acb_realref(z)));
arf_sub(t, arb_midref(acb_realref(z)), t, MAG_BITS, ARF_RND_FLOOR);
if (arf_sgn(t) >= 0)
{
res = 1;
} }
else else
{ {
acb_set_round(zeta, z, wp); arf_get_mag(x, t);
acb_sqrt(zeta, zeta, wp); arb_get_mag_lower(y, acb_imagref(z));
acb_cube(zeta, zeta, wp); mag_set_ui(s, 3);
acb_mul_2exp_si(zeta, zeta, 1); mag_sqrt(s, s);
acb_div_ui(zeta, zeta, 3, wp); mag_mul(s, s, x);
res = mag_cmp(s, y) <= 0;
}
acb_hypgeom_airy_bound_9_7_17(A, z, zeta); arf_clear(t);
mag_clear(x);
mag_clear(y);
mag_clear(s);
/* Use Bi(z) = w_1 Ai(z) + 2 w_2 Ai(z exp(+/- 2pi i / 3)), return res;
where w_1, w_2 are roots of unity */ }
if (bi != NULL || bip != NULL)
static int
arg_gt_2pi3_fast(const acb_t z)
{
arf_t t;
mag_t x, y, s;
int res;
if (arb_is_zero(acb_imagref(z)) && arb_is_negative(acb_realref(z)))
return 1;
arf_init(t);
mag_init(x);
mag_init(y);
mag_init(s);
arf_set_mag(t, arb_radref(acb_realref(z)));
arf_add(t, arb_midref(acb_realref(z)), t, MAG_BITS, ARF_RND_CEIL);
if (arf_sgn(t) >= 0)
{ {
acb_init(z1); res = 0;
arb_sqrt_ui(acb_imagref(z1), 3, wp);
arb_set_si(acb_realref(z1), -1);
acb_mul_2exp_si(z1, z1, -1);
/* multiply by exp(-2 pi i / 3) in upper half plane
and by exp(2 pi i / 3) in lower half plane, to stay close
to positive reals */
if (arf_sgn(zim) >= 0)
acb_conj(z1, z1);
acb_mul(z1, z1, z, wp);
acb_neg(zeta, zeta); /* same effect regardless of exp(+/-2 pi i/3) */
acb_hypgeom_airy_bound_9_7_17(B, z1, zeta);
mag_mul_2exp_si(B, B, 1);
mag_add(B, B, A);
acb_clear(z1);
} }
else
{
arf_get_mag_lower(x, t);
arb_get_mag(y, acb_imagref(z));
mag_set_ui_lower(s, 3);
mag_sqrt_lower(s, s);
mag_mul_lower(s, s, x);
res = mag_cmp(s, y) >= 0;
} }
acb_get_mag(zhi, z); arf_clear(t);
mag_clear(x);
mag_clear(y);
mag_clear(s);
return res;
}
void
acb_hypgeom_airy_bound(mag_t ai, mag_t aip, mag_t bi, mag_t bip, const acb_t z)
{
acb_t zeta;
mag_t A, B, D, zlo, zhi;
slong wp;
int near_zero;
/* Fast and slightly tighter bounds for real z <= 0 */
/* Todo: could implement bounds for z >= 0 too */
if (acb_is_real(z) && arb_is_nonpositive(acb_realref(z)))
{
mag_init(zlo);
mag_init(zhi);
mag_init(A);
mag_init(B);
mag_init(D);
if (ai != NULL || bi != NULL)
{
acb_get_mag_lower(zlo, z); acb_get_mag_lower(zlo, z);
mag_rsqrt(A, zlo);
mag_sqrt(A, A);
mag_mul_ui(A, A, 150);
mag_set_ui(D, 160);
mag_min(A, A, D);
mag_mul_2exp_si(A, A, -8);
if (ai != NULL) mag_set(ai, A);
if (bi != NULL) mag_set(bi, A);
}
if (aip != NULL || bip != NULL)
{
acb_get_mag(zhi, z);
mag_sqrt(A, zhi);
mag_sqrt(A, A);
mag_mul_ui(A, A, 150);
mag_set_ui(D, 160);
mag_max(B, A, D);
mag_mul_2exp_si(B, B, -8);
mag_set_ui(D, 67);
mag_max(A, A, D);
mag_mul_2exp_si(A, A, -8);
if (aip != NULL) mag_set(aip, A);
if (bip != NULL) mag_set(bip, B);
}
mag_clear(zlo);
mag_clear(zhi);
mag_clear(A);
mag_clear(B);
mag_clear(D);
return;
}
acb_init(zeta);
mag_init(A);
mag_init(B);
mag_init(D);
mag_init(zlo);
mag_init(zhi);
wp = MAG_BITS * 2;
acb_get_mag_lower(zlo, z);
acb_get_mag(zhi, z);
if (mag_cmp_2exp_si(zhi, 0) <= 0)
{
/* inside unit circle -- don't look at asymptotics */
if (ai != NULL) mag_set_ui_2exp_si(ai, 159, -8);
if (aip != NULL) mag_set_ui_2exp_si(aip, 125, -8);
if (bi != NULL) mag_set_ui_2exp_si(bi, 310, -8);
if (bip != NULL) mag_set_ui_2exp_si(bip, 239, -8);
}
else
{
/* look at asymptotics outside unit circle */
near_zero = mag_cmp_2exp_si(zlo, 0) <= 0;
if (near_zero)
mag_one(zlo);
if (arg_lt_2pi3_fast(z))
{
acb_hypgeom_airy_bound_arg_le_2pi3(A, (bi != NULL || bip != NULL) ? B : NULL, z, wp);
}
else if (arg_gt_2pi3_fast(z))
{
acb_hypgeom_airy_bound_arg_ge_2pi3(A, (bi != NULL || bip != NULL) ? B : NULL, z, wp);
}
else
{
mag_t A2, B2;
mag_init(A2);
mag_init(B2);
acb_hypgeom_airy_bound_arg_le_2pi3(A, (bi != NULL || bip != NULL) ? B : NULL, z, wp);
acb_hypgeom_airy_bound_arg_ge_2pi3(A2, (bi != NULL || bip != NULL) ? B2 : NULL, z, wp);
mag_max(A, A, A2);
mag_max(B, B, A2);
mag_clear(A2);
mag_clear(B2);
}
/* bound |z|^(1/4) */ /* bound |z|^(1/4) */
mag_sqrt(zhi, zhi); mag_sqrt(zhi, zhi);
@ -228,6 +369,35 @@ acb_hypgeom_airy_bound(mag_t ai, mag_t aip, mag_t bi, mag_t bip, const acb_t z)
if (bi != NULL) mag_mul(bi, B, zlo); if (bi != NULL) mag_mul(bi, B, zlo);
if (bip != NULL) mag_mul(bip, B, zhi); if (bip != NULL) mag_mul(bip, B, zhi);
if (near_zero)
{
/* max by bounds on the unit circle */
if (ai != NULL)
{
mag_set_ui_2exp_si(D, 159, -8);
mag_max(ai, ai, D);
}
if (aip != NULL)
{
mag_set_ui_2exp_si(D, 125, -8);
mag_max(aip, aip, D);
}
if (bi != NULL)
{
mag_set_ui_2exp_si(D, 310, -8);
mag_max(bi, bi, D);
}
if (bip != NULL)
{
mag_set_ui_2exp_si(D, 239, -8);
mag_max(bip, bip, D);
}
}
}
acb_clear(zeta); acb_clear(zeta);
mag_clear(A); mag_clear(A);
mag_clear(B); mag_clear(B);

View file

@ -542,11 +542,25 @@ f_erf_bent(acb_ptr res, const acb_t z, void * param, slong order, slong prec)
return 0; return 0;
} }
/* f(z) = Ai(z) */
int
f_airy_ai(acb_ptr res, const acb_t z, void * param, slong order, slong prec)
{
if (order > 1)
flint_abort(); /* Would be needed for Taylor method. */
acb_hypgeom_airy(res, NULL, NULL, NULL, z, prec);
return 0;
}
/* ------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------- */
/* Main test program */ /* Main test program */
/* ------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------- */
#define NUM_INTEGRALS 26 #define NUM_INTEGRALS 27
const char * descr[NUM_INTEGRALS] = const char * descr[NUM_INTEGRALS] =
{ {
@ -576,6 +590,7 @@ const char * descr[NUM_INTEGRALS] =
"int_0^{1000} W_0(x) dx", "int_0^{1000} W_0(x) dx",
"int_0^pi max(sin(x), cos(x)) dx", "int_0^pi max(sin(x), cos(x)) dx",
"int_{-1}^1 erf(x/sqrt(0.0002)*0.5+1.5)*exp(-x) dx", "int_{-1}^1 erf(x/sqrt(0.0002)*0.5+1.5)*exp(-x) dx",
"int_{-10}^10 Ai(x) dx",
}; };
int main(int argc, char *argv[]) int main(int argc, char *argv[])
@ -975,6 +990,12 @@ int main(int argc, char *argv[])
acb_calc_integrate(s, f_erf_bent, NULL, a, b, goal, tol, options, prec); acb_calc_integrate(s, f_erf_bent, NULL, a, b, goal, tol, options, prec);
break; break;
case 26:
acb_set_si(a, -10);
acb_set_si(b, 10);
acb_calc_integrate(s, f_airy_ai, NULL, a, b, goal, tol, options, prec);
break;
default: default:
abort(); abort();
} }