mirror of
https://github.com/vale981/arb
synced 2025-03-04 17:01:40 -05:00
408 lines
9.7 KiB
C
408 lines
9.7 KiB
C
/*
|
|
Copyright (C) 2015 Fredrik Johansson
|
|
|
|
This file is part of Arb.
|
|
|
|
Arb is free software: you can redistribute it and/or modify it under
|
|
the terms of the GNU Lesser General Public License (LGPL) as published
|
|
by the Free Software Foundation; either version 2.1 of the License, or
|
|
(at your option) any later version. See <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "acb_hypgeom.h"
|
|
|
|
void
|
|
arb_bound_exp_neg(mag_t b, const arb_t x)
|
|
{
|
|
arb_t t;
|
|
arb_init(t);
|
|
arf_set_mag(arb_midref(t), arb_radref(x));
|
|
arf_sub(arb_midref(t), arb_midref(x), arb_midref(t), MAG_BITS, ARF_RND_FLOOR);
|
|
arf_neg(arb_midref(t), arb_midref(t));
|
|
arb_exp(t, t, MAG_BITS);
|
|
arb_get_mag(b, t);
|
|
arb_clear(t);
|
|
}
|
|
|
|
/* Implements DLMF 9.7.17. We assume |zeta| >= 1/2 and |arg(z)| <= 2 pi/3 here,
|
|
ignoring the smaller points which must be dealt with separately. */
|
|
void
|
|
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, half;
|
|
|
|
mag_init(D);
|
|
mag_init(t);
|
|
mag_init(u);
|
|
mag_init(v);
|
|
mag_init(zeta_lower);
|
|
mag_init(half);
|
|
|
|
mag_one(half);
|
|
mag_mul_2exp_si(half, half, -1);
|
|
|
|
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 */
|
|
/* simplified bound assuming |zeta| >= 1/2 */
|
|
mag_one(D);
|
|
|
|
/* exp(-zeta) / (2 sqrt(pi)) * (1 + D / |zeta|) */
|
|
|
|
/* t = exp(-zeta) / (2 sqrt(pi)) < exp(-zeta) * (73/256) */
|
|
arb_bound_exp_neg(t, acb_realref(zeta));
|
|
mag_mul_ui(t, t, 73);
|
|
mag_mul_2exp_si(t, t, -8);
|
|
|
|
/* u = 1 + D / |zeta| */
|
|
mag_div(u, D, zeta_lower);
|
|
mag_one(v);
|
|
mag_add(u, u, v);
|
|
|
|
mag_mul(bound, t, u);
|
|
|
|
mag_clear(D);
|
|
mag_clear(t);
|
|
mag_clear(u);
|
|
mag_clear(v);
|
|
mag_clear(zeta_lower);
|
|
}
|
|
|
|
void
|
|
acb_hypgeom_airy_bound_arg_le_2pi3(mag_t A, mag_t B, const acb_t z, slong wp)
|
|
{
|
|
acb_t zeta, z1;
|
|
|
|
acb_init(zeta);
|
|
acb_init(z1);
|
|
|
|
acb_set_round(zeta, z, wp);
|
|
acb_sqrt(zeta, zeta, wp);
|
|
acb_cube(zeta, zeta, wp);
|
|
acb_mul_2exp_si(zeta, zeta, 1);
|
|
acb_div_ui(zeta, zeta, 3, wp);
|
|
|
|
acb_hypgeom_airy_bound_9_7_17(A, z, zeta);
|
|
|
|
/* 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(z2);
|
|
|
|
arb_sqrt_ui(acb_imagref(z1), 3, wp);
|
|
arb_one(acb_realref(z1));
|
|
acb_mul_2exp_si(z1, z1, -1);
|
|
acb_conj(z2, z1);
|
|
|
|
acb_neg_round(zeta, z, wp);
|
|
acb_mul(z1, z1, zeta, wp);
|
|
|
|
acb_sqrt(zeta, zeta, wp);
|
|
acb_cube(zeta, zeta, wp);
|
|
acb_mul_2exp_si(zeta, zeta, 1);
|
|
acb_div_ui(zeta, zeta, 3, wp);
|
|
acb_mul_onei(zeta, zeta);
|
|
|
|
acb_hypgeom_airy_bound_9_7_17(A, z1, zeta);
|
|
|
|
/* conjugate symmetry */
|
|
if (arb_is_zero(acb_imagref(z)))
|
|
{
|
|
mag_mul_2exp_si(A, A, 1);
|
|
}
|
|
else
|
|
{
|
|
mag_t D;
|
|
mag_init(D);
|
|
acb_mul(z2, z2, zeta, wp);
|
|
acb_neg(zeta, zeta);
|
|
acb_hypgeom_airy_bound_9_7_17(D, z2, zeta);
|
|
mag_add(A, A, D);
|
|
mag_clear(D);
|
|
}
|
|
|
|
if (B != NULL)
|
|
mag_set(B, A);
|
|
|
|
acb_clear(zeta);
|
|
acb_clear(z1);
|
|
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
|
|
{
|
|
arf_get_mag(x, t);
|
|
arb_get_mag_lower(y, acb_imagref(z));
|
|
mag_set_ui(s, 3);
|
|
mag_sqrt(s, s);
|
|
mag_mul(s, s, x);
|
|
res = mag_cmp(s, y) <= 0;
|
|
}
|
|
|
|
arf_clear(t);
|
|
mag_clear(x);
|
|
mag_clear(y);
|
|
mag_clear(s);
|
|
|
|
return res;
|
|
}
|
|
|
|
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)
|
|
{
|
|
res = 0;
|
|
}
|
|
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;
|
|
}
|
|
|
|
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);
|
|
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) */
|
|
mag_sqrt(zhi, zhi);
|
|
mag_sqrt(zhi, zhi);
|
|
|
|
/* bound |z|^(-1/4) */
|
|
mag_rsqrt(zlo, zlo);
|
|
mag_sqrt(zlo, zlo);
|
|
|
|
if (ai != NULL) mag_mul(ai, A, zlo);
|
|
if (aip != NULL) mag_mul(aip, A, zhi);
|
|
if (bi != NULL) mag_mul(bi, B, zlo);
|
|
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);
|
|
mag_clear(A);
|
|
mag_clear(B);
|
|
mag_clear(D);
|
|
mag_clear(zlo);
|
|
mag_clear(zhi);
|
|
}
|
|
|