more hypergeometric series code; new implementation of arb_hypgeom_si

This commit is contained in:
fredrik 2021-12-21 19:20:21 +01:00
parent 0a7c01edf3
commit fcffcf32b8
16 changed files with 888 additions and 107 deletions

View file

@ -9,6 +9,7 @@
(at your option) any later version. See <http://www.gnu.org/licenses/>.
*/
#include "arb_hypgeom.h"
#include "acb_hypgeom.h"
void
@ -122,9 +123,15 @@ acb_hypgeom_si_1f2(acb_t res, const acb_t z, slong prec)
void
acb_hypgeom_si(acb_t res, const acb_t z, slong prec)
{
if (acb_is_real(z) && arb_is_finite(acb_realref(z)))
{
arb_hypgeom_si(acb_realref(res), acb_realref(z), prec);
arb_zero(acb_imagref(res));
return;
}
if (acb_hypgeom_u_use_asymp(z, prec))
acb_hypgeom_si_asymp(res, z, prec);
else
acb_hypgeom_si_1f2(res, z, prec);
}

View file

@ -100,7 +100,11 @@ void arb_hypgeom_ei(arb_t res, const arb_t z, slong prec);
void _arb_hypgeom_ei_series(arb_ptr g, arb_srcptr h, slong hlen, slong len, slong prec);
void arb_hypgeom_ei_series(arb_poly_t g, const arb_poly_t h, slong len, slong prec);
void _arb_hypgeom_si_asymp(arb_t res, const arb_t z, slong N, slong prec);
void _arb_hypgeom_si_1f2(arb_t res, const arb_t z, slong N, slong wp, slong prec);
void arb_hypgeom_si(arb_t res, const arb_t z, slong prec);
void _arb_hypgeom_si_series(arb_ptr g, arb_srcptr h, slong hlen, slong len, slong prec);
void arb_hypgeom_si_series(arb_poly_t g, const arb_poly_t h, slong len, slong prec);
@ -196,13 +200,15 @@ slong _arb_hypgeom_gamma_upper_singular_si_choose_N(mag_t err, slong n, const ar
void _arb_hypgeom_gamma_upper_singular_si_bsplit(arb_t res, slong n, const arb_t z, slong N, slong prec);
int arb_hypgeom_erf_bb(arb_t res, const arb_t z, int complementary, slong prec);
void arb_hypgeom_sum_fmpq_arb_forward(arb_t res, const fmpq * a, slong alen, const fmpq * b, slong blen, const arb_t z, slong N, slong prec);
void arb_hypgeom_sum_fmpq_arb_rs(arb_t res, const fmpq * a, slong alen, const fmpq * b, slong blen, const arb_t z, slong N, slong prec);
void arb_hypgeom_sum_fmpq_arb(arb_t res, const fmpq * a, slong alen, const fmpq * b, slong blen, const arb_t z, slong N, slong prec);
void arb_hypgeom_sum_fmpq_arb_forward(arb_t res, const fmpq * a, slong alen, const fmpq * b, slong blen, const arb_t z, int reciprocal, slong N, slong prec);
void arb_hypgeom_sum_fmpq_arb_rs(arb_t res, const fmpq * a, slong alen, const fmpq * b, slong blen, const arb_t z, int reciprocal, slong N, slong prec);
void arb_hypgeom_sum_fmpq_arb_bs(arb_t res, const fmpq * a, slong alen, const fmpq * b, slong blen, const arb_t z, int reciprocal, slong N, slong prec);
void arb_hypgeom_sum_fmpq_arb(arb_t res, const fmpq * a, slong alen, const fmpq * b, slong blen, const arb_t z, int reciprocal, slong N, slong prec);
void arb_hypgeom_sum_fmpq_imag_arb_forward(arb_t res1, arb_t res2, const fmpq * a, slong alen, const fmpq * b, slong blen, const arb_t z, slong N, slong prec);
void arb_hypgeom_sum_fmpq_imag_arb_rs(arb_t res1, arb_t res2, const fmpq * a, slong alen, const fmpq * b, slong blen, const arb_t z, slong N, slong prec);
void arb_hypgeom_sum_fmpq_imag_arb(arb_t res1, arb_t res2, const fmpq * a, slong alen, const fmpq * b, slong blen, const arb_t z, slong N, slong prec);
void arb_hypgeom_sum_fmpq_imag_arb_forward(arb_t res1, arb_t res2, const fmpq * a, slong alen, const fmpq * b, slong blen, const arb_t z, int reciprocal, slong N, slong prec);
void arb_hypgeom_sum_fmpq_imag_arb_rs(arb_t res1, arb_t res2, const fmpq * a, slong alen, const fmpq * b, slong blen, const arb_t z, int reciprocal, slong N, slong prec);
void arb_hypgeom_sum_fmpq_imag_arb_bs(arb_t res_real, arb_t res_imag, const fmpq * a, slong alen, const fmpq * b, slong blen, const arb_t z, int reciprocal, slong N, slong prec);
void arb_hypgeom_sum_fmpq_imag_arb(arb_t res1, arb_t res2, const fmpq * a, slong alen, const fmpq * b, slong blen, const arb_t z, int reciprocal, slong N, slong prec);
#ifdef __cplusplus

261
arb_hypgeom/si.c Normal file
View file

@ -0,0 +1,261 @@
/*
Copyright (C) 2021 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 "arb_hypgeom.h"
#define LOG2 0.69314718055994531
#define INV_LOG2 1.4426950408889634074
#define EXP1 2.7182818284590452
double arf_get_d_log2_abs_approx_clamped(const arf_t x);
/* todo: minima and maxima at multiples of pi */
void
arb_hypgeom_si_wide(arb_t res, const arb_t z, slong prec)
{
mag_set_ui_2exp_si(arb_radref(res), 1988502269, -30);
arf_zero(arb_midref(res));
}
void
_arb_hypgeom_si_asymp(arb_t res, const arb_t z, slong N, slong prec)
{
arb_t s, c, sz, cz, u;
fmpq a[1];
slong wp;
mag_t err, t;
int negative;
negative = arf_sgn(arb_midref(z)) < 0;
if (negative)
{
arb_neg(res, z);
_arb_hypgeom_si_asymp(res, res, N, prec);
arb_neg(res, res);
return;
}
N = FLINT_MAX(N, 1);
arb_init(s);
arb_init(c);
arb_init(sz);
arb_init(cz);
arb_init(u);
mag_init(err);
mag_init(t);
/* Error is bounded by first omitted term, N! / z^N */
arb_get_mag_lower(err, z);
/* Do something better on wide intervals */
if (mag_cmp_2exp_si(err, 1) < 0)
{
arb_hypgeom_si_wide(res, z, prec);
}
else
{
mag_inv(err, err);
mag_pow_ui(err, err, N);
mag_fac_ui(t, N);
mag_mul(err, err, t);
wp = prec * 1.001 + 5;
arb_set(u, z);
*fmpq_numref(&a[0]) = 1;
*fmpq_denref(&a[0]) = 1;
arb_hypgeom_sum_fmpq_imag_arb(c, s, a, 1, NULL, 0, u, 1, N, wp);
arb_add_error_mag(c, err);
arb_add_error_mag(s, err);
arb_sin_cos(sz, cz, z, wp);
arb_mul(s, s, sz, wp);
arb_addmul(s, c, cz, wp);
arb_div(s, s, z, wp);
arb_const_pi(u, wp);
arb_mul_2exp_si(u, u, -1);
arb_sub(res, u, s, prec);
}
arb_clear(s);
arb_clear(c);
arb_clear(sz);
arb_clear(cz);
arb_clear(u);
mag_clear(err);
mag_clear(t);
}
void
_arb_hypgeom_si_1f2(arb_t res, const arb_t z, slong N, slong wp, slong prec)
{
mag_t err, t;
arb_t s, u;
fmpq a[1];
fmpq b[3];
N = FLINT_MAX(N, 1);
mag_init(err);
mag_init(t);
arb_init(s);
arb_init(u);
arb_sqr(u, z, wp);
arb_mul_2exp_si(u, u, -2);
arb_neg(u, u);
*fmpq_numref(&a[0]) = 1;
*fmpq_denref(&a[0]) = 2;
*fmpq_numref(&b[0]) = 3;
*fmpq_denref(&b[0]) = 2;
*fmpq_numref(&b[1]) = 3;
*fmpq_denref(&b[1]) = 2;
*fmpq_numref(&b[2]) = 1;
*fmpq_denref(&b[2]) = 1;
/* Terms are bounded by u^N / (4 (N!)^2) */
arb_get_mag(err, u);
/* u^N */
mag_set(t, err);
mag_pow_ui(t, t, N);
/* geometric factor for u/N^2 */
mag_div_ui(err, err, N);
mag_div_ui(err, err, N);
mag_geom_series(err, err, 0);
mag_mul(t, t, err);
/* 1/(N!)^2 */
mag_rfac_ui(err, N);
mag_mul(err, err, err);
mag_mul(err, err, t);
/* 1/4 */
mag_mul_2exp_si(err, err, -2);
arb_hypgeom_sum_fmpq_arb(s, a, 1, b, 3, u, 0, N, wp);
arb_add_error_mag(s, err);
arb_mul(res, s, z, prec);
mag_clear(err);
mag_clear(t);
arb_clear(u);
arb_clear(s);
}
void
arb_hypgeom_si(arb_t res, const arb_t z, slong prec)
{
slong wp, N, acc;
double dz, du;
if (!arb_is_finite(z))
{
arb_indeterminate(res);
return;
}
if (arb_is_zero(z))
{
arb_zero(res);
return;
}
if (ARF_IS_LAGOM(arb_midref(z)))
{
acc = arb_rel_accuracy_bits(z);
acc = FLINT_MAX(acc, 0);
acc = FLINT_MIN(acc, prec);
acc += FLINT_MAX(0, ARF_EXP(arb_midref(z)));
prec = FLINT_MIN(prec, acc + 32);
}
dz = fabs(arf_get_d(arb_midref(z), ARF_RND_DOWN));
dz = FLINT_MIN(dz, 1e300);
if (dz > 2.0)
{
double log2_err, err_prev, log2dz;
log2dz = arf_get_d_log2_abs_approx_clamped(arb_midref(z));
err_prev = 0.0;
for (N = 1; N < 2 * prec; N++)
{
log2_err = ((N + 1.0) * (log(N + 1.0) - 1.0)) * INV_LOG2 - N * log2dz;
if (log2_err > err_prev)
break;
if (log2_err < -prec - 2)
{
_arb_hypgeom_si_asymp(res, z, N, prec);
return;
}
err_prev = log2_err;
}
}
if (arf_cmpabs_2exp_si(arb_midref(z), -30) < 0)
{
N = -arf_abs_bound_lt_2exp_si(arb_midref(z));
wp = prec * 1.001 + 10;
N = (prec + N - 1) / N;
}
else
{
du = 0.25 * dz * dz;
wp = prec * 1.001 + 10;
if (du > 1.0)
wp += dz * 1.4426950408889634;
N = (prec + 5) * LOG2 / (2 * d_lambertw((prec + 5) * LOG2 / (2 * EXP1 * sqrt(du)))) + 1;
}
if (arb_is_exact(z))
{
_arb_hypgeom_si_1f2(res, z, N, wp, prec);
}
else
{
mag_t err;
arb_t zmid;
mag_init(err);
arb_init(zmid);
arb_get_mid_arb(zmid, z);
/* |si'(z)| = |sinc(z)| <= min(1, 1/z) */
arb_get_mag_lower(err, z);
mag_inv(err, err);
if (mag_cmp_2exp_si(err, 0) > 0)
mag_one(err);
mag_mul(err, err, arb_radref(z));
/* |si(a) - si(b)| < 4 */
if (mag_cmp_2exp_si(err, 2) > 0)
mag_set_ui(err, 4);
_arb_hypgeom_si_1f2(res, zmid, N, wp, prec);
arb_add_error_mag(res, err);
arb_clear(zmid);
mag_clear(err);
}
}

View file

@ -12,11 +12,12 @@
#include "arb_hypgeom.h"
void
arb_hypgeom_sum_fmpq_arb(arb_t res, const fmpq * a, slong alen, const fmpq * b, slong blen, const arb_t z, slong N, slong prec)
arb_hypgeom_sum_fmpq_arb(arb_t res, const fmpq * a, slong alen, const fmpq * b, slong blen, const arb_t z, int reciprocal, slong N, slong prec)
{
if (N <= 2 || (prec <= 1024 && N <= 8) || (prec <= 4096 && N <= 4))
arb_hypgeom_sum_fmpq_arb_forward(res, a, alen, b, blen, z, N, prec);
arb_hypgeom_sum_fmpq_arb_forward(res, a, alen, b, blen, z, reciprocal, N, prec);
else if (prec >= 8192 && arb_bits(z) <= 0.001 * prec)
arb_hypgeom_sum_fmpq_arb_bs(res, a, alen, b, blen, z, reciprocal, N, prec);
else
arb_hypgeom_sum_fmpq_arb_rs(res, a, alen, b, blen, z, N, prec);
arb_hypgeom_sum_fmpq_arb_rs(res, a, alen, b, blen, z, reciprocal, N, prec);
}

View file

@ -0,0 +1,159 @@
/*
Copyright (C) 2021 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 "arb_hypgeom.h"
static void
factor(arb_t A, const fmpq * a, slong alen, const fmpq * b, slong blen, const fmpz_t bden, const arb_t z, slong k, slong prec)
{
slong i;
fmpz_t t, u;
fmpz_init(t);
fmpz_init(u);
if (alen == 0)
{
if (z == NULL)
arb_set_fmpz(A, bden);
else if (fmpz_is_one(bden))
arb_set(A, z);
else
arb_mul_fmpz(A, z, bden, prec);
}
else
{
/* product of a_i + k = p/q + k = (p + k*q)/q */
fmpz_mul_ui(t, fmpq_denref(a + 0), k);
fmpz_add(t, t, fmpq_numref(a + 0));
for (i = 1; i < alen; i++)
{
fmpz_mul_ui(u, fmpq_denref(a + i), k);
fmpz_add(u, u, fmpq_numref(a + i));
fmpz_mul(t, t, u);
}
if (!fmpz_is_one(bden))
fmpz_mul(t, t, bden);
if (z == NULL)
arb_set_fmpz(A, t);
else
arb_mul_fmpz(A, z, t, prec);
}
fmpz_clear(t);
fmpz_clear(u);
}
static void
bsplit(arb_t A1, arb_t B1, arb_t C1,
const fmpq * a, slong alen, const fmpz_t aden,
const fmpq * b, slong blen, const fmpz_t bden,
const arb_t z, int reciprocal,
slong aa,
slong bb,
slong prec)
{
if (bb - aa == 1)
{
factor(A1, a, alen, b, blen, bden, reciprocal ? NULL : z, aa, prec);
factor(C1, b, blen, a, alen, aden, reciprocal ? z : NULL, aa, prec);
/* arb_set(B1, C1); but we skip this */
}
else
{
slong m;
arb_t A2, B2, C2;
arb_init(A2);
arb_init(B2);
arb_init(C2);
m = aa + (bb - aa) / 2;
bsplit(A1, B1, C1, a, alen, aden, b, blen, bden, z, reciprocal, aa, m, prec);
bsplit(A2, B2, C2, a, alen, aden, b, blen, bden, z, reciprocal, m, bb, prec);
if (bb - m == 1) /* B2 = C2 */
{
if (m - aa == 1)
arb_add(B2, A1, C1, prec);
else
arb_add(B2, A1, B1, prec);
arb_mul(B1, B2, C2, prec);
}
else
{
if (m - aa == 1)
arb_mul(B1, C1, C2, prec);
else
arb_mul(B1, B1, C2, prec);
arb_addmul(B1, A1, B2, prec);
}
arb_mul(A1, A1, A2, prec);
arb_mul(C1, C1, C2, prec);
arb_clear(A2);
arb_clear(B2);
arb_clear(C2);
}
}
void
arb_hypgeom_sum_fmpq_arb_bs(arb_t res, const fmpq * a, slong alen, const fmpq * b, slong blen, const arb_t z, int reciprocal, slong N, slong prec)
{
arb_t u, v, w;
fmpz_t aden, bden;
slong i;
if (N <= 3)
{
arb_hypgeom_sum_fmpq_arb_forward(res, a, alen, b, blen, z, reciprocal, N, prec);
return;
}
arb_init(u);
arb_init(v);
arb_init(w);
fmpz_init(aden);
fmpz_init(bden);
fmpz_one(aden);
fmpz_one(bden);
for (i = 0; i < alen; i++)
fmpz_mul(aden, aden, fmpq_denref(a + i));
for (i = 0; i < blen; i++)
fmpz_mul(bden, bden, fmpq_denref(b + i));
/* we compute to N-1 instead of N to avoid dividing by 0 in the
denominator when computing a hypergeometric polynomial
that terminates right before a pole */
bsplit(u, v, w, a, alen, aden, b, blen, bden, z, reciprocal, 0, N - 1, prec);
arb_add(res, u, v, prec); /* s = s + t */
arb_div(res, res, w, prec);
arb_clear(u);
arb_clear(v);
arb_clear(w);
fmpz_clear(aden);
fmpz_clear(bden);
}

View file

@ -12,14 +12,17 @@
#include "arb_hypgeom.h"
void
arb_hypgeom_sum_fmpq_arb_forward(arb_t res, const fmpq * a, slong alen, const fmpq * b, slong blen, const arb_t z, slong N, slong prec)
arb_hypgeom_sum_fmpq_arb_forward(arb_t res, const fmpq * a, slong alen, const fmpq * b, slong blen, const arb_t z, int reciprocal, slong N, slong prec)
{
arb_t t, u, sden;
slong i, k, num_max_bits, den_max_bits, bits1, bits2, Nbits, den_prec;
if (N <= 1)
{
arb_zero(res);
if (N == 1)
arb_one(res);
else
arb_zero(res);
return;
}
@ -51,14 +54,23 @@ arb_hypgeom_sum_fmpq_arb_forward(arb_t res, const fmpq * a, slong alen, const fm
slong num, den;
den = 1;
for (i = 0; i < blen; i++)
den *= *fmpq_denref(b + i);
arb_mul_si(u, z, den, prec);
den = 1;
num = 1;
for (i = 0; i < alen; i++)
den *= *fmpq_denref(a + i);
arb_div_si(u, u, den, prec);
for (i = 0; i < blen; i++)
num *= *fmpq_denref(b + i);
if (reciprocal)
{
arb_mul_si(u, z, den, prec);
arb_set_si(t, num);
arb_div(u, t, u, prec);
}
else
{
arb_mul_si(u, z, num, prec);
arb_div_si(u, u, den, prec);
}
arb_one(res);
arb_one(t);
@ -105,14 +117,23 @@ arb_hypgeom_sum_fmpq_arb_forward(arb_t res, const fmpq * a, slong alen, const fm
fmpz_init(c);
fmpz_one(den);
for (i = 0; i < blen; i++)
fmpz_mul(den, den, fmpq_denref(b + i));
arb_mul_fmpz(u, z, den, prec);
fmpz_one(den);
fmpz_one(num);
for (i = 0; i < alen; i++)
fmpz_mul(den, den, fmpq_denref(a + i));
arb_div_fmpz(u, u, den, prec);
for (i = 0; i < blen; i++)
fmpz_mul(num, num, fmpq_denref(b + i));
if (reciprocal)
{
arb_mul_fmpz(u, z, den, prec);
arb_set_fmpz(t, num);
arb_div(u, t, u, prec);
}
else
{
arb_mul_fmpz(u, z, num, prec);
arb_div_fmpz(u, u, den, prec);
}
arb_one(res);
arb_one(t);
@ -169,4 +190,3 @@ arb_hypgeom_sum_fmpq_arb_forward(arb_t res, const fmpq * a, slong alen, const fm
arb_clear(u);
arb_clear(sden);
}

View file

@ -18,24 +18,24 @@ d_log2_fac(double n)
}
static slong
tail_precision(slong k, slong alen, slong blen, double log2z, double log2max, slong prec)
tail_precision(slong k, double min_k, slong alen, slong blen, double log2z, double log2max, slong prec)
{
double term_magnitude;
slong new_prec;
if (prec <= 128 || k <= 5)
if (prec <= 128 || k <= 5 || k <= min_k)
return prec;
term_magnitude = k * log2z;
if (alen != blen)
term_magnitude += (alen - blen) * d_log2_fac(k);
/* printf("term %ld, max %f, log2x %f, magn %f\n", k, log2z, log2max, term_magnitude); */
new_prec = prec - (log2max - term_magnitude) + 10;
new_prec = FLINT_MIN(new_prec, prec);
new_prec = FLINT_MAX(new_prec, 32);
/* printf("term %ld, max %f, log2x %f, magn %f new_prec %ld\n", k, log2z, log2max, term_magnitude, new_prec); */
return new_prec;
}
@ -70,10 +70,10 @@ arf_get_d_log2_abs_approx_clamped(const arf_t x)
}
void
arb_hypgeom_sum_fmpq_arb_rs(arb_t res, const fmpq * a, slong alen, const fmpq * b, slong blen, const arb_t z, slong N, slong prec)
arb_hypgeom_sum_fmpq_arb_rs(arb_t res, const fmpq * a, slong alen, const fmpq * b, slong blen, const arb_t z, int reciprocal, slong N, slong prec)
{
slong m, i, j, k, l, jlen, jbot, jtop, wp;
double log2z, log2max;
double log2z, log2max, adaptive_min_k;
int want_adaptive_precision;
arb_t s, t;
arb_ptr zpow;
@ -83,7 +83,10 @@ arb_hypgeom_sum_fmpq_arb_rs(arb_t res, const fmpq * a, slong alen, const fmpq *
if (N <= 1)
{
arb_zero(res);
if (N == 1)
arb_one(res);
else
arb_zero(res);
return;
}
@ -101,15 +104,24 @@ arb_hypgeom_sum_fmpq_arb_rs(arb_t res, const fmpq * a, slong alen, const fmpq *
zpow = _arb_vec_init(m + 1);
cs = _fmpz_vec_init(m + 1);
fmpz_one(den);
for (i = 0; i < blen; i++)
fmpz_mul(den, den, fmpq_denref(b + i));
arb_mul_fmpz(zpow + m, z, den, prec);
fmpz_one(c);
fmpz_one(den);
for (i = 0; i < alen; i++)
fmpz_mul(den, den, fmpq_denref(a + i));
arb_div_fmpz(zpow + m, zpow + m, den, prec);
for (i = 0; i < blen; i++)
fmpz_mul(c, c, fmpq_denref(b + i));
if (reciprocal)
{
arb_mul_fmpz(zpow + m, z, den, prec);
arb_set_fmpz(t, c);
arb_div(zpow + m, t, zpow + m, prec);
}
else
{
arb_mul_fmpz(zpow + m, z, c, prec);
arb_div_fmpz(zpow + m, zpow + m, den, prec);
}
want_adaptive_precision = N > 5;
@ -134,10 +146,12 @@ arb_hypgeom_sum_fmpq_arb_rs(arb_t res, const fmpq * a, slong alen, const fmpq *
log2max = 0.0;
log2z = 0.0;
adaptive_min_k = 0.0;
if (want_adaptive_precision)
{
log2z = arf_get_d_log2_abs_approx_clamped(arb_midref(z));
if (reciprocal)
log2z = -log2z;
/* Terms are increasing, so don't change the precision. */
if (alen >= blen && log2z >= 0.0)
@ -159,6 +173,12 @@ arb_hypgeom_sum_fmpq_arb_rs(arb_t res, const fmpq * a, slong alen, const fmpq *
/* r = 3 -> exp(3*z^(1/3)) */
/* ... */
log2max = r * exp(log2z * 0.693147180559945 / r) * 1.44269504088896;
/* fixme */
if (r == 1)
adaptive_min_k = exp(log2z * log(2));
else
adaptive_min_k = exp(0.5 * log2z * log(2));
}
}
}
@ -282,7 +302,7 @@ arb_hypgeom_sum_fmpq_arb_rs(arb_t res, const fmpq * a, slong alen, const fmpq *
}
if (want_adaptive_precision)
wp = tail_precision(k - jlen, alen, blen, log2z, log2max, prec);
wp = tail_precision(k - jlen, adaptive_min_k, alen, blen, log2z, log2max, prec);
else
wp = prec;

View file

@ -12,11 +12,13 @@
#include "arb_hypgeom.h"
void
arb_hypgeom_sum_fmpq_imag_arb(arb_t res1, arb_t res2, const fmpq * a, slong alen, const fmpq * b, slong blen, const arb_t z, slong N, slong prec)
arb_hypgeom_sum_fmpq_imag_arb(arb_t res1, arb_t res2, const fmpq * a, slong alen, const fmpq * b, slong blen, const arb_t z, int reciprocal, slong N, slong prec)
{
if (N <= 10 || (prec <= 1024 && N <= 16))
arb_hypgeom_sum_fmpq_imag_arb_forward(res1, res2, a, alen, b, blen, z, N, prec);
arb_hypgeom_sum_fmpq_imag_arb_forward(res1, res2, a, alen, b, blen, z, reciprocal, N, prec);
else if (prec >= 8192 && arb_bits(z) <= 0.001 * prec)
arb_hypgeom_sum_fmpq_imag_arb_bs(res1, res2, a, alen, b, blen, z, reciprocal, N, prec);
else
arb_hypgeom_sum_fmpq_imag_arb_rs(res1, res2, a, alen, b, blen, z, N, prec);
arb_hypgeom_sum_fmpq_imag_arb_rs(res1, res2, a, alen, b, blen, z, reciprocal, N, prec);
}

View file

@ -0,0 +1,172 @@
/*
Copyright (C) 2021 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 "arb_hypgeom.h"
static void
factor(arb_t A, const fmpq * a, slong alen, const fmpq * b, slong blen, const fmpz_t bden, const arb_t z, slong k, slong prec)
{
slong i;
fmpz_t t, u;
fmpz_init(t);
fmpz_init(u);
if (alen == 0)
{
if (z == NULL)
arb_set_fmpz(A, bden);
else if (fmpz_is_one(bden))
arb_set(A, z);
else
arb_mul_fmpz(A, z, bden, prec);
}
else
{
/* product of a_i + k = p/q + k = (p + k*q)/q */
fmpz_mul_ui(t, fmpq_denref(a + 0), k);
fmpz_add(t, t, fmpq_numref(a + 0));
for (i = 1; i < alen; i++)
{
fmpz_mul_ui(u, fmpq_denref(a + i), k);
fmpz_add(u, u, fmpq_numref(a + i));
fmpz_mul(t, t, u);
}
if (!fmpz_is_one(bden))
fmpz_mul(t, t, bden);
if (z == NULL)
arb_set_fmpz(A, t);
else
arb_mul_fmpz(A, z, t, prec);
}
fmpz_clear(t);
fmpz_clear(u);
}
static void
bsplit(acb_t A1, acb_t B1, acb_t C1,
const fmpq * a, slong alen, const fmpz_t aden,
const fmpq * b, slong blen, const fmpz_t bden,
const arb_t z, int reciprocal,
slong aa,
slong bb,
slong prec)
{
if (bb - aa == 1)
{
factor(acb_realref(A1), a, alen, b, blen, bden, reciprocal ? NULL : z, aa, prec);
factor(acb_realref(C1), b, blen, a, alen, aden, reciprocal ? z : NULL, aa, prec);
arb_zero(acb_imagref(A1));
arb_zero(acb_imagref(C1));
if (reciprocal)
acb_div_onei(C1, C1);
else
acb_mul_onei(A1, A1);
/* arb_set(B1, C1); but we skip this */
}
else
{
slong m;
acb_t A2, B2, C2;
acb_init(A2);
acb_init(B2);
acb_init(C2);
m = aa + (bb - aa) / 2;
bsplit(A1, B1, C1, a, alen, aden, b, blen, bden, z, reciprocal, aa, m, prec);
bsplit(A2, B2, C2, a, alen, aden, b, blen, bden, z, reciprocal, m, bb, prec);
if (bb - m == 1) /* B2 = C2 */
{
if (m - aa == 1)
acb_add(B2, A1, C1, prec);
else
acb_add(B2, A1, B1, prec);
acb_mul(B1, B2, C2, prec);
}
else
{
if (m - aa == 1)
acb_mul(B1, C1, C2, prec);
else
acb_mul(B1, B1, C2, prec);
acb_addmul(B1, A1, B2, prec);
}
acb_mul(A1, A1, A2, prec);
acb_mul(C1, C1, C2, prec);
acb_clear(A2);
acb_clear(B2);
acb_clear(C2);
}
}
void
arb_hypgeom_sum_fmpq_imag_arb_bs(arb_t res_real, arb_t res_imag, const fmpq * a, slong alen, const fmpq * b, slong blen, const arb_t z, int reciprocal, slong N, slong prec)
{
acb_t u, v, w;
fmpz_t aden, bden;
slong i;
if (N <= 3)
{
arb_hypgeom_sum_fmpq_imag_arb_forward(res_real, res_imag, a, alen, b, blen, z, reciprocal, N, prec);
return;
}
acb_init(u);
acb_init(v);
acb_init(w);
fmpz_init(aden);
fmpz_init(bden);
fmpz_one(aden);
fmpz_one(bden);
for (i = 0; i < alen; i++)
fmpz_mul(aden, aden, fmpq_denref(a + i));
for (i = 0; i < blen; i++)
fmpz_mul(bden, bden, fmpq_denref(b + i));
/* we compute to N-1 instead of N to avoid dividing by 0 in the
denominator when computing a hypergeometric polynomial
that terminates right before a pole */
bsplit(u, v, w, a, alen, aden, b, blen, bden, z, reciprocal, 0, N - 1, prec);
acb_add(u, u, v, prec); /* s = s + t */
acb_div(u, u, w, prec);
if (!acb_is_finite(u))
acb_indeterminate(u);
arb_swap(res_real, acb_realref(u));
arb_swap(res_imag, acb_imagref(u));
acb_clear(u);
acb_clear(v);
acb_clear(w);
fmpz_clear(aden);
fmpz_clear(bden);
}

View file

@ -12,7 +12,7 @@
#include "arb_hypgeom.h"
void
arb_hypgeom_sum_fmpq_imag_arb_forward(arb_t s_real, arb_t s_imag, const fmpq * a, slong alen, const fmpq * b, slong blen, const arb_t z, slong N, slong prec)
arb_hypgeom_sum_fmpq_imag_arb_forward(arb_t s_real, arb_t s_imag, const fmpq * a, slong alen, const fmpq * b, slong blen, const arb_t z, int reciprocal, slong N, slong prec)
{
arb_t t, u, sden;
slong i, k, num_max_bits, den_max_bits, bits1, bits2, Nbits, den_prec;;
@ -56,14 +56,23 @@ arb_hypgeom_sum_fmpq_imag_arb_forward(arb_t s_real, arb_t s_imag, const fmpq * a
slong num, den;
den = 1;
for (i = 0; i < blen; i++)
den *= *fmpq_denref(b + i);
arb_mul_si(u, z, den, prec);
den = 1;
num = 1;
for (i = 0; i < alen; i++)
den *= *fmpq_denref(a + i);
arb_div_si(u, u, den, prec);
for (i = 0; i < blen; i++)
num *= *fmpq_denref(b + i);
if (reciprocal)
{
arb_mul_si(u, z, den, prec);
arb_set_si(t, num);
arb_div(u, t, u, prec);
}
else
{
arb_mul_si(u, z, num, prec);
arb_div_si(u, u, den, prec);
}
arb_one(s_real);
arb_zero(s_imag);
@ -143,14 +152,23 @@ arb_hypgeom_sum_fmpq_imag_arb_forward(arb_t s_real, arb_t s_imag, const fmpq * a
fmpz_init(c);
fmpz_one(den);
for (i = 0; i < blen; i++)
fmpz_mul(den, den, fmpq_denref(b + i));
arb_mul_fmpz(u, z, den, prec);
fmpz_one(den);
fmpz_one(num);
for (i = 0; i < alen; i++)
fmpz_mul(den, den, fmpq_denref(a + i));
arb_div_fmpz(u, u, den, prec);
for (i = 0; i < blen; i++)
fmpz_mul(num, num, fmpq_denref(b + i));
if (reciprocal)
{
arb_mul_fmpz(u, z, den, prec);
arb_set_fmpz(t, num);
arb_div(u, t, u, prec);
}
else
{
arb_mul_fmpz(u, z, num, prec);
arb_div_fmpz(u, u, den, prec);
}
arb_one(s_real);
arb_zero(s_imag);

View file

@ -18,24 +18,24 @@ d_log2_fac(double n)
}
static slong
tail_precision(slong k, slong alen, slong blen, double log2z, double log2max, slong prec)
tail_precision(slong k, double min_k, slong alen, slong blen, double log2z, double log2max, slong prec)
{
double term_magnitude;
slong new_prec;
if (prec <= 128 || k <= 5)
if (prec <= 128 || k <= 5 || k <= min_k)
return prec;
term_magnitude = k * log2z;
if (alen != blen)
term_magnitude += (alen - blen) * d_log2_fac(k);
/* printf("term %ld, max %f, log2x %f, magn %f\n", k, log2z, log2max, term_magnitude); */
new_prec = prec - (log2max - term_magnitude) + 10;
new_prec = FLINT_MIN(new_prec, prec);
new_prec = FLINT_MAX(new_prec, 32);
/* printf("term %ld, max %f, log2x %f, magn %f new_prec %ld\n", k, log2z, log2max, term_magnitude, new_prec); */
return new_prec;
}
@ -43,10 +43,10 @@ tail_precision(slong k, slong alen, slong blen, double log2z, double log2max, sl
double arf_get_d_log2_abs_approx_clamped(const arf_t x);
void
arb_hypgeom_sum_fmpq_imag_arb_rs(arb_t res_real, arb_t res_imag, const fmpq * a, slong alen, const fmpq * b, slong blen, const arb_t z, slong N, slong prec)
arb_hypgeom_sum_fmpq_imag_arb_rs(arb_t res_real, arb_t res_imag, const fmpq * a, slong alen, const fmpq * b, slong blen, const arb_t z, int reciprocal, slong N, slong prec)
{
slong m, i, j, k, l, jlen, jbot, jtop, wp;
double log2z, log2max;
double log2z, log2max, adaptive_min_k;
int want_adaptive_precision;
arb_t s_real, s_imag, t_real, t_imag;
arb_ptr zpow;
@ -70,15 +70,24 @@ arb_hypgeom_sum_fmpq_imag_arb_rs(arb_t res_real, arb_t res_imag, const fmpq * a,
zpow = _arb_vec_init(m + 1);
cs = _fmpz_vec_init(m + 1);
fmpz_one(den);
for (i = 0; i < blen; i++)
fmpz_mul(den, den, fmpq_denref(b + i));
arb_mul_fmpz(zpow + m, z, den, prec);
fmpz_one(c);
fmpz_one(den);
for (i = 0; i < alen; i++)
fmpz_mul(den, den, fmpq_denref(a + i));
arb_div_fmpz(zpow + m, zpow + m, den, prec);
for (i = 0; i < blen; i++)
fmpz_mul(c, c, fmpq_denref(b + i));
if (reciprocal)
{
arb_mul_fmpz(zpow + m, z, den, prec);
arb_set_fmpz(t_real, c);
arb_div(zpow + m, t_real, zpow + m, prec);
}
else
{
arb_mul_fmpz(zpow + m, z, c, prec);
arb_div_fmpz(zpow + m, zpow + m, den, prec);
}
want_adaptive_precision = N > 5;
@ -103,10 +112,12 @@ arb_hypgeom_sum_fmpq_imag_arb_rs(arb_t res_real, arb_t res_imag, const fmpq * a,
log2max = 0.0;
log2z = 0.0;
adaptive_min_k = 0.0;
if (want_adaptive_precision)
{
log2z = arf_get_d_log2_abs_approx_clamped(arb_midref(z));
if (reciprocal)
log2z = -log2z;
/* Terms are increasing, so don't change the precision. */
if (alen >= blen && log2z >= 0.0)
@ -118,6 +129,7 @@ arb_hypgeom_sum_fmpq_imag_arb_rs(arb_t res_real, arb_t res_imag, const fmpq * a,
if (alen >= blen)
{
log2max = 0.0;
adaptive_min_k = N;
}
else
{
@ -128,6 +140,12 @@ arb_hypgeom_sum_fmpq_imag_arb_rs(arb_t res_real, arb_t res_imag, const fmpq * a,
/* r = 3 -> exp(3*z^(1/3)) */
/* ... */
log2max = r * exp(log2z * 0.693147180559945 / r) * 1.44269504088896;
/* fixme */
if (r == 1)
adaptive_min_k = exp(log2z * log(2));
else
adaptive_min_k = exp(0.5 * log2z * log(2));
}
}
}
@ -141,7 +159,7 @@ arb_hypgeom_sum_fmpq_imag_arb_rs(arb_t res_real, arb_t res_imag, const fmpq * a,
if (jtop > 0)
{
while (jlen <= j && jlen <= 8 && jbot >= 2)
while (jlen <= j && jlen <= 12 && jbot >= 2)
{
jbot--;
jlen++;
@ -251,7 +269,7 @@ arb_hypgeom_sum_fmpq_imag_arb_rs(arb_t res_real, arb_t res_imag, const fmpq * a,
}
if (want_adaptive_precision)
wp = tail_precision(k - jlen, alen, blen, log2z, log2max, prec);
wp = tail_precision(k - jlen, adaptive_min_k, alen, blen, log2z, log2max, prec);
else
wp = prec;

95
arb_hypgeom/test/t-si.c Normal file
View file

@ -0,0 +1,95 @@
/*
Copyright (C) 2018 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 "arb_hypgeom.h"
int main()
{
slong iter;
flint_rand_t state;
flint_printf("si....");
fflush(stdout);
flint_randinit(state);
for (iter = 0; iter < 10000 * arb_test_multiplier(); iter++)
{
arb_t x, s, t;
slong prec1, prec2;
arb_init(x);
arb_init(s);
arb_init(t);
if (n_randint(state, 10) == 0)
{
prec1 = 2 + n_randint(state, 2000);
prec2 = 2 + n_randint(state, 2000);
}
else
{
prec1 = 2 + n_randint(state, 200);
prec2 = 2 + n_randint(state, 200);
}
arb_randtest(x, state, 2 + n_randint(state, prec1), 2 + n_randint(state, 100));
arb_randtest(s, state, 2 + n_randint(state, prec1), 2 + n_randint(state, 100));
arb_randtest(t, state, 2 + n_randint(state, prec1), 2 + n_randint(state, 100));
switch (n_randint(state, 3))
{
case 0:
arb_hypgeom_si(s, x, prec1);
break;
case 1:
_arb_hypgeom_si_1f2(s, x, n_randint(state, prec1), prec1, prec1);
break;
default:
_arb_hypgeom_si_asymp(s, x, n_randint(state, prec1 / 2), prec1);
break;
}
switch (n_randint(state, 3))
{
case 0:
arb_hypgeom_si(t, x, prec2);
break;
case 1:
_arb_hypgeom_si_1f2(t, x, n_randint(state, prec2), prec2, prec2);
break;
default:
_arb_hypgeom_si_asymp(t, x, n_randint(state, prec2 / 2), prec2);
break;
}
arb_hypgeom_si(s, x, prec1);
_arb_hypgeom_si_asymp(t, x, n_randint(state, prec2 / 2), prec2);
if (!arb_overlaps(s, t))
{
flint_printf("FAIL: overlap\n\n");
flint_printf("x = "); arb_printn(x, 100, 0); flint_printf("\n\n");
flint_printf("s = "); arb_printn(s, 100, 0); flint_printf("\n\n");
flint_printf("t = "); arb_printn(t, 100, 0); flint_printf("\n\n");
flint_abort();
}
arb_clear(x);
arb_clear(s);
arb_clear(t);
}
flint_randclear(state);
flint_cleanup();
flint_printf("PASS\n");
return EXIT_SUCCESS;
}

View file

@ -24,9 +24,10 @@ int main()
for (iter = 0; iter < 10000 * arb_test_multiplier(); iter++)
{
arb_t s1, s2, z;
arb_t s1, s2, s3, z;
fmpq *a, *b;
slong alen, blen, N, prec;
int reciprocal;
alen = n_randint(state, 5);
blen = n_randint(state, 5);
@ -34,11 +35,13 @@ int main()
arb_init(s1);
arb_init(s2);
arb_init(s3);
arb_init(z);
a = _fmpq_vec_init(alen);
b = _fmpq_vec_init(blen);
prec = 2 + n_randint(state, 500);
reciprocal = n_randint(state, 2);
if (n_randint(state, 10) == 0)
arb_randtest_special(z, state, 1 + n_randint(state, 200), 1 + n_randint(state, 100));
@ -50,10 +53,11 @@ int main()
_fmpq_vec_randtest(a, state, alen, 1 + n_randint(state, 100));
_fmpq_vec_randtest(b, state, blen, 1 + n_randint(state, 100));
arb_hypgeom_sum_fmpq_arb_forward(s1, a, alen, b, blen, z, N, prec);
arb_hypgeom_sum_fmpq_arb_rs(s2, a, alen, b, blen, z, N, prec);
arb_hypgeom_sum_fmpq_arb_forward(s1, a, alen, b, blen, z, reciprocal, N, prec);
arb_hypgeom_sum_fmpq_arb_rs(s2, a, alen, b, blen, z, reciprocal, N, prec);
arb_hypgeom_sum_fmpq_arb_bs(s3, a, alen, b, blen, z, reciprocal, N, prec);
if (!arb_overlaps(s1, s2))
if (!arb_overlaps(s1, s2) || !arb_overlaps(s1, s3))
{
flint_printf("FAIL: overlap\n\n");
flint_printf("N = %wd\n\n", N);
@ -62,11 +66,13 @@ int main()
flint_printf("z = "); arb_printn(z, 100, 0); flint_printf("\n\n");
flint_printf("s1 = "); arb_printn(s1, 100, 0); flint_printf("\n\n");
flint_printf("s2 = "); arb_printn(s2, 100, 0); flint_printf("\n\n");
flint_printf("s3 = "); arb_printn(s3, 100, 0); flint_printf("\n\n");
flint_abort();
}
arb_clear(s1);
arb_clear(s2);
arb_clear(s3);
arb_clear(z);
_fmpq_vec_clear(a, alen);
_fmpq_vec_clear(b, blen);

View file

@ -24,9 +24,10 @@ int main()
for (iter = 0; iter < 10000 * arb_test_multiplier(); iter++)
{
arb_t s1, s2, r1, r2, z;
arb_t s1, s2, s3, r1, r2, r3, z;
fmpq *a, *b;
slong alen, blen, N, prec;
int reciprocal;
alen = n_randint(state, 5);
blen = n_randint(state, 5);
@ -34,13 +35,16 @@ int main()
arb_init(s1);
arb_init(s2);
arb_init(s3);
arb_init(r1);
arb_init(r2);
arb_init(r3);
arb_init(z);
a = _fmpq_vec_init(alen);
b = _fmpq_vec_init(blen);
prec = 2 + n_randint(state, 500);
reciprocal = n_randint(state, 2);
if (n_randint(state, 10) == 0)
arb_randtest_special(z, state, 1 + n_randint(state, 200), 1 + n_randint(state, 100));
@ -54,10 +58,12 @@ int main()
_fmpq_vec_randtest(a, state, alen, 1 + n_randint(state, 100));
_fmpq_vec_randtest(b, state, blen, 1 + n_randint(state, 100));
arb_hypgeom_sum_fmpq_imag_arb_forward(r1, s1, a, alen, b, blen, z, N, prec);
arb_hypgeom_sum_fmpq_imag_arb_rs(r2, s2, a, alen, b, blen, z, N, prec);
arb_hypgeom_sum_fmpq_imag_arb_forward(r1, s1, a, alen, b, blen, z, reciprocal, N, prec);
arb_hypgeom_sum_fmpq_imag_arb_rs(r2, s2, a, alen, b, blen, z, reciprocal, N, prec);
arb_hypgeom_sum_fmpq_imag_arb_bs(r3, s3, a, alen, b, blen, z, reciprocal, N, prec);
if (!arb_overlaps(r1, r2) || !arb_overlaps(s1, s2))
if (!arb_overlaps(r1, r2) || !arb_overlaps(s1, s2) ||
!arb_overlaps(r1, r3) || !arb_overlaps(s1, s3))
{
flint_printf("FAIL: overlap\n\n");
flint_printf("N = %wd\n\n", N);
@ -68,13 +74,17 @@ int main()
flint_printf("s1 = "); arb_printn(s1, 100, 0); flint_printf("\n\n");
flint_printf("r2 = "); arb_printn(r2, 100, 0); flint_printf("\n\n");
flint_printf("s2 = "); arb_printn(s2, 100, 0); flint_printf("\n\n");
flint_printf("r3 = "); arb_printn(r3, 100, 0); flint_printf("\n\n");
flint_printf("s3 = "); arb_printn(s3, 100, 0); flint_printf("\n\n");
flint_abort();
}
arb_clear(s1);
arb_clear(s2);
arb_clear(s3);
arb_clear(r1);
arb_clear(r2);
arb_clear(r3);
arb_clear(z);
_fmpq_vec_clear(a, alen);
_fmpq_vec_clear(b, blen);

View file

@ -70,24 +70,6 @@ arb_hypgeom_ei(arb_t res, const arb_t z, slong prec)
}
}
void
arb_hypgeom_si(arb_t res, const arb_t z, slong prec)
{
if (!arb_is_finite(z))
{
arb_indeterminate(res);
}
else
{
acb_t t;
acb_init(t);
arb_set(acb_realref(t), z);
acb_hypgeom_si(t, t, prec);
arb_swap(res, acb_realref(t));
acb_clear(t);
}
}
void
arb_hypgeom_ci(arb_t res, const arb_t z, slong prec)
{

View file

@ -410,7 +410,9 @@ Exponential and trigonometric integrals
Computes the exponential integral of the power series *z*,
truncated to length *len*.
.. function:: void arb_hypgeom_si(arb_t res, const arb_t z, slong prec)
.. function:: void _arb_hypgeom_si_asymp(arb_t res, const arb_t z, slong N, slong prec)
void _arb_hypgeom_si_1f2(arb_t res, const arb_t z, slong N, slong wp, slong prec)
void arb_hypgeom_si(arb_t res, const arb_t z, slong prec)
Computes the sine integral `\operatorname{Si}(z)`.
@ -652,25 +654,27 @@ Dilogarithm
Hypergeometric sums
-------------------------------------------------------------------------------
.. function:: void arb_hypgeom_sum_fmpq_arb_forward(arb_t res, const fmpq * a, slong alen, const fmpq * b, slong blen, const arb_t z, slong N, slong prec)
void arb_hypgeom_sum_fmpq_arb_rs(arb_t res, const fmpq * a, slong alen, const fmpq * b, slong blen, const arb_t z, slong N, slong prec)
void arb_hypgeom_sum_fmpq_arb(arb_t res, const fmpq * a, slong alen, const fmpq * b, slong blen, const arb_t z, slong N, slong prec)
.. function:: void arb_hypgeom_sum_fmpq_arb_forward(arb_t res, const fmpq * a, slong alen, const fmpq * b, slong blen, const arb_t z, int reciprocal, slong N, slong prec)
void arb_hypgeom_sum_fmpq_arb_rs(arb_t res, const fmpq * a, slong alen, const fmpq * b, slong blen, const arb_t z, int reciprocal, slong N, slong prec)
void arb_hypgeom_sum_fmpq_arb(arb_t res, const fmpq * a, slong alen, const fmpq * b, slong blen, const arb_t z, int reciprocal, slong N, slong prec)
Sets *res* to the finite hypergeometric sum
`\sum_{n=0}^{N-1} (\textbf{a})_n z^n / (\textbf{b})_n`
where `\textbf{x}_n = (x_1)_n (x_2)_n \cdots`,
given vectors of rational parameters *a* (of length *alen*)
and *b* (of length *blen*).
If *reciprocal* is set, replace `z` by `1 / z`.
The *forward* version uses the forward recurrence, optimized by
delaying divisions, the *rs* version
uses rectangular splitting, and the default version uses
an automatic algorithm choice.
.. function:: void arb_hypgeom_sum_fmpq_imag_arb_forward(arb_t res1, arb_t res2, const fmpq * a, slong alen, const fmpq * b, slong blen, const arb_t z, slong N, slong prec)
void arb_hypgeom_sum_fmpq_imag_arb_rs(arb_t res1, arb_t res2, const fmpq * a, slong alen, const fmpq * b, slong blen, const arb_t z, slong N, slong prec)
void arb_hypgeom_sum_fmpq_imag_arb(arb_t res1, arb_t res2, const fmpq * a, slong alen, const fmpq * b, slong blen, const arb_t z, slong N, slong prec)
.. function:: void arb_hypgeom_sum_fmpq_imag_arb_forward(arb_t res1, arb_t res2, const fmpq * a, slong alen, const fmpq * b, slong blen, const arb_t z, int reciprocal, slong N, slong prec)
void arb_hypgeom_sum_fmpq_imag_arb_rs(arb_t res1, arb_t res2, const fmpq * a, slong alen, const fmpq * b, slong blen, const arb_t z, int reciprocal, slong N, slong prec)
void arb_hypgeom_sum_fmpq_imag_arb_bs(arb_t res1, arb_t res2, const fmpq * a, slong alen, const fmpq * b, slong blen, const arb_t z, int reciprocal, slong N, slong prec)
void arb_hypgeom_sum_fmpq_imag_arb(arb_t res1, arb_t res2, const fmpq * a, slong alen, const fmpq * b, slong blen, const arb_t z, int reciprocal, slong N, slong prec)
Sets *res1* and *res2* to the real and imaginary part of the
finite hypergeometric sum
`\sum_{n=0}^{N-1} (\textbf{a})_n (i z^n) / (\textbf{b})_n`.
`\sum_{n=0}^{N-1} (\textbf{a})_n (i z)^n / (\textbf{b})_n`.
If *reciprocal* is set, replace `z` by `1 / z`.