acb_dirichlet_l_fmpq_afe: handle the singular series

This commit is contained in:
fredrik 2021-11-27 13:29:53 +01:00
parent 11de2dd2f6
commit b924a3afcd
6 changed files with 277 additions and 36 deletions

View file

@ -135,13 +135,12 @@ acb_dirichlet_fmpq_sum_afe(acb_t res, const fmpq_t s, const dirichlet_group_t G,
arb_t ns, t, u, v, z, z0, z1, x, x2, Ga, Gz1, Gz0, expmz0, z0_prevn, Gz0_prevn, expmz0_prevn;
acb_t c;
fmpq_t s2;
int parity;
int parity, gamma_singular;
ulong q;
double abs_tol_mag;
double gammainc_mag, gamma_mag, ns_mag;
double aa, zz;
double cancellation;
#if VERBOSE
double t1, t2, t3;
@ -188,6 +187,10 @@ acb_dirichlet_fmpq_sum_afe(acb_t res, const fmpq_t s, const dirichlet_group_t G,
/* s2 = (s+parity)/2 */
fmpq_add_ui(s2, s, parity);
fmpq_div_2exp(s2, s2, 1);
gamma_singular = (fmpz_is_one(fmpq_denref(s2)) && fmpz_sgn(fmpq_numref(s2)) <= 0);
if (!gamma_singular)
arb_gamma_fmpq(Ga, s2, gamma_cached_prec);
for (n = 1; ; n += 1)
@ -222,28 +225,34 @@ acb_dirichlet_fmpq_sum_afe(acb_t res, const fmpq_t s, const dirichlet_group_t G,
/* Gamma((s+parity)/2, z) (want lower bound, to estimate cancellation) */
gammainc_mag = log_gamma_upper_approx(aa, zz) / log(2);
/* Gamma((s+parity)/2) */
gamma_mag = ARF_EXP(arb_midref(Ga));
/* n^-s */
ns_mag = -fmpq_get_d(s) * log(n) / log(2);
cancellation = FLINT_MAX(gamma_mag - gammainc_mag, 0);
cancellation = cancellation; /* suppress warning when VERBOSE == 0 */
/* Want Gamma(a,z) n^-s with abs_tol --> want Gamma(a,z) with abs_tol * n^s */
mag_set_ui_2exp_si(abs_tol_gamma, 1, abs_tol_mag - ns_mag);
/* Precision needed sans cancellation. */
/* wp = Precision needed sans cancellation. */
wp = gammainc_mag + ns_mag - abs_tol_mag + 5;
wp = FLINT_MAX(wp, 30);
/* Precision needed with cancellation. */
/* wp2 = Precision needed with cancellation. */
if (gamma_singular)
{
/* Max term is roughly n^(-s) * z^((s+parity)/2) * exp(z) */
wp2 = -abs_tol_mag + ns_mag + aa * log(zz) + zz / log(2) + 5;
wp2 = FLINT_MAX(wp2, 30);
}
else
{
/* Estimate of Gamma((s+parity)/2) */
gamma_mag = ARF_EXP(arb_midref(Ga));
wp2 = FLINT_MAX(gamma_mag, gammainc_mag) + ns_mag - abs_tol_mag + 5;
wp2 = FLINT_MAX(wp2, 30);
}
#if VERBOSE
printf(" abs_tol_gamma = "); mag_printd(abs_tol_gamma, 5); printf("\n");
printf(" gamma(a) = "); arb_printd(Ga, 10); printf("\n");
printf(" cancellation = %f\n", cancellation);
printf(" wp = %ld wp2 = %ld\n", wp, wp2);
#endif
@ -286,34 +295,56 @@ acb_dirichlet_fmpq_sum_afe(acb_t res, const fmpq_t s, const dirichlet_group_t G,
else
{
/* Otherwise fallback to the series at 0. */
if (gamma_singular)
{
slong nn;
nn = *fmpq_numref(s2);
if (COEFF_IS_MPZ(nn))
{
arb_indeterminate(Gz0);
}
else
{
nn = -nn;
NN = _arb_hypgeom_gamma_upper_singular_si_choose_N(AE, nn, z0, abs_tol_gamma);
_arb_hypgeom_gamma_upper_singular_si_bsplit(Gz0, nn, z0, NN, wp2);
arb_add_error_mag(Gz0, AE);
#if VERBOSE
flint_printf(" singular series with N = %wd, z0 = ", NN); arb_printd(z0, 10); printf(" ");
mag_printd(AE, 10); printf("\n");
#endif
}
}
else
{
NN = _arb_hypgeom_gamma_lower_fmpq_0_choose_N(AE, s2, z0, abs_tol_gamma);
_arb_hypgeom_gamma_lower_fmpq_0_bsplit(Gz0, s2, z0, NN, wp2);
arb_add_error_mag(Gz0, AE);
#if VERBOSE
flint_printf(" lower series with N = %wd, z0 = ", NN); arb_printd(z0, 10); printf(" ");
mag_printd(AE, 10); printf("\n");
#endif
_arb_hypgeom_gamma_lower_fmpq_0_bsplit(Gz0, s2, z0, NN, wp2);
arb_add_error_mag(Gz0, AE);
while (mag_cmp(arb_radref(Ga), abs_tol_gamma) > 0)
{
gamma_cached_prec *= 2;
arb_gamma_fmpq(Ga, s2, gamma_cached_prec);
}
#if VERBOSE
flint_printf(" lower series with N = %wd: ", NN); arb_printd(Gz0, 10); printf("\n");
#endif
arb_sub(Gz0, Ga, Gz0, wp);
#if VERBOSE
flint_printf(" G(a) - lower series: "); arb_printd(Gz0, 10); printf("\n");
#endif
}
}
}
else
{
_arb_gamma_upper_fmpq_step_bsplit(Gz0, s2, z0_prevn, z0, Gz0_prevn, expmz0_prevn, abs_tol_gamma, wp);
@ -437,17 +468,19 @@ acb_dirichlet_l_fmpq_afe(acb_t res, const fmpq_t s, const dirichlet_group_t G, c
q = (G == NULL) ? 1 : G->q;
parity = (G == NULL) ? 0 : dirichlet_parity_char(G, chi);
/* Limit at gamma(-n) is not implemented. */
/* Division by gamma((s+parity)/2) at a pole. */
if (fmpz_is_one(fmpq_denref(s)))
{
const fmpz * n = fmpq_numref(s);
if ((parity == 0 && fmpz_sgn(n) <= 0 && fmpz_is_even(n)) ||
(parity == 0 && fmpz_sgn(n) > 0 && fmpz_is_odd(n)) ||
(parity == 1 && fmpz_sgn(n) < 0 && fmpz_is_odd(n)) ||
(parity == 1 && fmpz_sgn(n) > 0 && fmpz_is_even(n)))
(parity == 1 && fmpz_sgn(n) < 0 && fmpz_is_odd(n)))
{
acb_indeterminate(res);
/* Special case for zeta */
if (q == 1 && fmpz_is_zero(n))
acb_set_d(res, -0.5);
else
acb_zero(res);
return;
}
}

View file

@ -45,7 +45,11 @@ int main()
if (dirichlet_char_is_primitive(G, chi))
{
if (n_randint(state, 2))
fmpq_set_si(x, -8 + (slong) n_randint(state, 8), 1);
else
fmpq_randtest(x, state, 2 + n_randint(state, 8));
acb_set_fmpq(s, x, prec);
if (n_randint(state, 2))

View file

@ -177,6 +177,8 @@ void _arb_hypgeom_gamma_upper_fmpq_inf_bsplit(arb_t res, const fmpq_t a, const a
slong _arb_hypgeom_gamma_lower_fmpq_0_choose_N(mag_t err, const fmpq_t a, const arb_t z, const mag_t abs_tol);
void _arb_hypgeom_gamma_lower_fmpq_0_bsplit(arb_t res, const fmpq_t a, const arb_t z, slong N, slong prec);
void _arb_gamma_upper_fmpq_step_bsplit(arb_t Gz1, const fmpq_t a, const arb_t z0, const arb_t z1, const arb_t Gz0, const arb_t expmz0, const mag_t abs_tol, slong prec);
slong _arb_hypgeom_gamma_upper_singular_si_choose_N(mag_t err, slong n, const arb_t z, const mag_t abs_tol);
void _arb_hypgeom_gamma_upper_singular_si_bsplit(arb_t res, slong n, const arb_t z, slong N, slong prec);
#ifdef __cplusplus
}

View file

@ -351,3 +351,137 @@ _arb_hypgeom_gamma_lower_fmpq_0_bsplit(arb_t res, const fmpq_t a, const arb_t z,
arb_clear(S);
arb_clear(Q);
}
/* bounded by 1/z^n * sum z^k / k! */
slong
_arb_hypgeom_gamma_upper_singular_si_choose_N(mag_t err, slong n, const arb_t z, const mag_t abs_tol)
{
slong k;
mag_t t, u, zm;
mag_init(t);
mag_init(u);
mag_init(zm);
arb_get_mag(zm, z);
arb_get_mag_lower(t, z);
mag_inv(t, t);
mag_pow_ui(t, t, n);
for (k = 1; ; k++)
{
mag_mul(t, t, zm);
mag_div_ui(t, t, k);
if (mag_cmp(t, abs_tol) < 0)
{
mag_div_ui(u, zm, k);
mag_geom_series(u, u, 0);
mag_mul(u, t, u);
if (mag_cmp(u, abs_tol) < 0)
{
mag_swap(err, t);
break;
}
}
}
mag_clear(t);
mag_clear(u);
mag_clear(zm);
return k;
}
static void
singular_bsplit(arb_t M, arb_t S, arb_t Q, slong n, const arb_t z, slong na, slong nb, int cont, slong prec)
{
if (nb - na == 0)
{
arb_zero(M);
arb_zero(S);
arb_one(Q);
}
else if (nb - na == 1)
{
fmpz_t t;
slong k;
k = na;
if (k == n)
arb_neg(M, z);
else
arb_mul_si(M, z, n - k, prec);
arb_set_si(S, (k != n) ? (k + 1) : 0);
fmpz_init_set_si(t, k + 1);
if (k != n)
fmpz_mul_si(t, t, k - n);
arb_set_fmpz(Q, t);
fmpz_clear(t);
}
else
{
slong m;
arb_t M2, S2, Q2;
m = na + (nb - na) / 2;
arb_init(M2);
arb_init(S2);
arb_init(Q2);
singular_bsplit(M, S, Q, n, z, na, m, 1, prec);
singular_bsplit(M2, S2, Q2, n, z, m, nb, cont, prec);
arb_mul(S, S, Q2, prec);
arb_addmul(S, M, S2, prec);
if (cont)
arb_mul(M, M, M2, prec);
arb_mul(Q, Q, Q2, prec);
arb_clear(M2);
arb_clear(S2);
arb_clear(Q2);
}
}
void
_arb_hypgeom_gamma_upper_singular_si_bsplit(arb_t res, slong n, const arb_t z, slong N, slong prec)
{
arb_t M, S, Q;
arb_init(M);
arb_init(S);
arb_init(Q);
N = FLINT_MAX(N, 0);
singular_bsplit(M, S, Q, n, z, 0, N, 0, prec);
/* (-1)**n/fac(n) * (digamma(n+1) - ln(z)) - (S/Q)/z**n */
arb_pow_ui(M, z, n, prec);
arb_mul(Q, Q, M, prec);
arb_div(S, S, Q, prec);
arb_set_ui(M, n + 1);
arb_digamma(M, M, prec);
arb_log(Q, z, prec);
arb_sub(M, M, Q, prec);
arb_fac_ui(Q, n, prec);
arb_div(M, M, Q, prec);
if (n & 1)
arb_neg(M, M);
arb_sub(res, M, S, prec);
arb_clear(M);
arb_clear(S);
arb_clear(Q);
}

View file

@ -141,6 +141,62 @@ int main()
mag_clear(err2);
}
for (iter = 0; iter < 10000 * arb_test_multiplier(); iter++)
{
slong na;
arb_t z, r1, r2;
mag_t tol1, tol2, err1, err2;
slong N1, N2, prec1, prec2;
mag_init(tol1);
mag_init(tol2);
mag_init(err1);
mag_init(err2);
arb_init(z);
arb_init(r1);
arb_init(r2);
prec1 = 2 + n_randint(state, 100);
prec2 = 2 + n_randint(state, 100);
na = n_randint(state, 50);
arb_set_ui(z, 1 + n_randint(state, 100));
arb_div_ui(z, z, 1 + n_randint(state, 100), 200);
mag_set_ui_2exp_si(tol1, 1, -(slong) n_randint(state, 100));
mag_set_ui_2exp_si(tol2, 1, -(slong) n_randint(state, 100));
N1 = _arb_hypgeom_gamma_upper_singular_si_choose_N(err1, na, z, tol1);
N2 = _arb_hypgeom_gamma_upper_singular_si_choose_N(err2, na, z, tol2);
_arb_hypgeom_gamma_upper_singular_si_bsplit(r1, na, z, N1, prec1);
arb_add_error_mag(r1, err1);
_arb_hypgeom_gamma_upper_singular_si_bsplit(r2, na, z, N2, prec2);
arb_add_error_mag(r2, err2);
if (!arb_overlaps(r1, r2))
{
flint_printf("FAIL: overlap\n\n");
flint_printf("na = %wd", na); flint_printf("\n\n");
flint_printf("z = "); arb_printn(z, 100, 0); flint_printf("\n\n");
flint_printf("r1 = "); arb_printn(r1, 100, 0); flint_printf("\n\n");
flint_printf("r2 = "); arb_printn(r2, 100, 0); flint_printf("\n\n");
flint_abort();
}
arb_clear(z);
arb_clear(r1);
arb_clear(r2);
mag_clear(tol1);
mag_clear(tol2);
mag_clear(err1);
mag_clear(err2);
}
flint_randclear(state);
flint_cleanup();
flint_printf("PASS\n");

View file

@ -313,6 +313,18 @@ Internal evaluation functions
the Taylor series at zero before term *N*.
The truncation error bound has to be added separately.
.. function:: slong _arb_hypgeom_gamma_upper_singular_si_choose_N(mag_t err, slong n, const arb_t z, const mag_t abs_tol)
Returns number of terms *N* and sets *err* to the truncation error for evaluating
`\Gamma(-n,z)` using the Taylor series at zero, targeting an absolute
tolerance of *abs_tol*.
.. function:: void _arb_hypgeom_gamma_upper_singular_si_bsplit(arb_t res, slong n, const arb_t z, slong N, slong prec)
Sets *res* to the approximation of `\Gamma(-n,z)` obtained by truncating
the Taylor series at zero before term *N*.
The truncation error bound has to be added separately.
.. function:: void _arb_gamma_upper_fmpq_step_bsplit(arb_t Gz1, const fmpq_t a, const arb_t z0, const arb_t z1, const arb_t Gz0, const arb_t expmz0, const mag_t abs_tol, slong prec)
Given *Gz0* and *expmz0* representing the values `\Gamma(a,z_0)` and `\exp(-z_0)`,