mirror of
https://github.com/vale981/arb
synced 2025-03-05 09:21:38 -05:00
301 lines
8 KiB
C
301 lines
8 KiB
C
/*
|
|
Copyright (C) 2014 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"
|
|
|
|
#define D_ABS(x) ((x) < 0.0 ? (-(x)) : (x))
|
|
|
|
int
|
|
acb_hypgeom_pfq_choose_n_double(slong * nn,
|
|
const double * are, const double * aim, slong p,
|
|
const double * bre, const double * bim, slong q,
|
|
double log2_z,
|
|
slong n_skip, slong n_min, slong n_max, slong prec)
|
|
{
|
|
double increase, term, term_max, accuracy, accuracy_best, t, u;
|
|
double required_decrease;
|
|
slong k, n, n_best;
|
|
int success;
|
|
|
|
if (p < q)
|
|
required_decrease = 0.01;
|
|
else if (p == q)
|
|
required_decrease = 0.0001;
|
|
else
|
|
required_decrease = 0.01;
|
|
|
|
term = term_max = accuracy = accuracy_best = 0.0;
|
|
success = 0;
|
|
|
|
for (n = n_best = n_skip; n < n_max; n++)
|
|
{
|
|
t = 1.0;
|
|
|
|
for (k = 0; k < FLINT_MAX(p, q); k++)
|
|
{
|
|
if (k < p)
|
|
{
|
|
u = (are[k]+n-1)*(are[k]+n-1) + (aim[k]*aim[k]);
|
|
t *= D_ABS(u);
|
|
}
|
|
|
|
if (k < q)
|
|
{
|
|
u = (bre[k]+n-1)*(bre[k]+n-1) + (bim[k]*bim[k]);
|
|
u = D_ABS(u);
|
|
|
|
if (u > 1e-100)
|
|
t /= u;
|
|
}
|
|
}
|
|
|
|
increase = 0.5 * log(t) * 1.4426950408889634074 + log2_z;
|
|
|
|
term += increase;
|
|
term_max = FLINT_MAX(term_max, term);
|
|
accuracy = term_max - term;
|
|
|
|
if (accuracy > accuracy_best && n >= n_min && increase < -required_decrease)
|
|
{
|
|
n_best = n;
|
|
accuracy_best = accuracy;
|
|
}
|
|
|
|
if (accuracy_best > prec + 4)
|
|
{
|
|
success = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
*nn = n_best;
|
|
return success;
|
|
}
|
|
|
|
slong
|
|
acb_hypgeom_pfq_choose_n_max(acb_srcptr a, slong p,
|
|
acb_srcptr b, slong q, const acb_t z,
|
|
slong prec, slong n_max)
|
|
{
|
|
slong n_skip, n_min, n_terminating, nint;
|
|
slong k, n;
|
|
double log2_z;
|
|
double * are;
|
|
double * aim;
|
|
double * bre;
|
|
double * bim;
|
|
mag_t zmag;
|
|
int success;
|
|
|
|
if (acb_is_zero(z) || !acb_is_finite(z))
|
|
return 1;
|
|
|
|
mag_init(zmag);
|
|
|
|
are = flint_malloc(sizeof(double) * 2 * (p + q));
|
|
aim = are + p;
|
|
bre = aim + p;
|
|
bim = bre + q;
|
|
|
|
acb_get_mag(zmag, z);
|
|
log2_z = mag_get_d_log2_approx(zmag);
|
|
|
|
n_skip = 1;
|
|
n_min = 1;
|
|
n_terminating = WORD_MAX;
|
|
|
|
for (k = 0; k < p; k++)
|
|
{
|
|
are[k] = arf_get_d(arb_midref(acb_realref(a + k)), ARF_RND_DOWN);
|
|
aim[k] = arf_get_d(arb_midref(acb_imagref(a + k)), ARF_RND_DOWN);
|
|
|
|
/* If the series is terminating, stop at this n. */
|
|
if (acb_is_int(a + k) && are[k] <= 0.0)
|
|
{
|
|
n_terminating = FLINT_MIN(n_terminating, (slong) (-are[k] + 1));
|
|
n_terminating = FLINT_MAX(n_terminating, 1);
|
|
}
|
|
else if (are[k] <= 0.01 && D_ABS(aim[k]) < 0.01)
|
|
{
|
|
/* If very close to an integer, we don't want to deal with it
|
|
using doubles, so fast forward (todo: could work with the
|
|
log2 of the difference instead when this happens). */
|
|
nint = floor(are[k] + 0.5);
|
|
if (D_ABS(nint - are[k]) < 0.01)
|
|
n_skip = FLINT_MAX(n_skip, 2 - nint);
|
|
}
|
|
}
|
|
|
|
n_max = FLINT_MIN(n_max, n_terminating);
|
|
|
|
for (k = 0; k < q; k++)
|
|
{
|
|
bre[k] = arf_get_d(arb_midref(acb_realref(b + k)), ARF_RND_DOWN);
|
|
bim[k] = arf_get_d(arb_midref(acb_imagref(b + k)), ARF_RND_DOWN);
|
|
|
|
if (bre[k] <= 0.25)
|
|
{
|
|
n_min = FLINT_MAX(n_min, 2 - bre[k]);
|
|
|
|
/* Also avoid near-integers here (can even allow exact
|
|
integers when computing regularized hypergeometric functions). */
|
|
if (bre[k] <= 0.01 && D_ABS(bim[k]) < 0.01)
|
|
{
|
|
nint = floor(bre[k] + 0.5);
|
|
if (D_ABS(nint - bre[k]) < 0.01)
|
|
n_skip = FLINT_MAX(n_skip, 2 - nint);
|
|
}
|
|
}
|
|
}
|
|
|
|
success = acb_hypgeom_pfq_choose_n_double(&n, are, aim, p, bre, bim, q,
|
|
log2_z, n_skip, n_min, n_max, prec);
|
|
|
|
if (!success)
|
|
{
|
|
if (n_terminating <= n_max)
|
|
{
|
|
n = n_terminating;
|
|
}
|
|
else
|
|
{
|
|
n = FLINT_MAX(n_min, n);
|
|
n = FLINT_MIN(n_max, n);
|
|
}
|
|
}
|
|
|
|
flint_free(are);
|
|
mag_clear(zmag);
|
|
|
|
return n;
|
|
}
|
|
|
|
slong
|
|
acb_hypgeom_pfq_choose_n(acb_srcptr a, slong p,
|
|
acb_srcptr b, slong q,
|
|
const acb_t z, slong prec)
|
|
{
|
|
return acb_hypgeom_pfq_choose_n_max(a, p, b, q, z, prec,
|
|
FLINT_MIN(WORD_MAX / 2, 50 + 10.0 * prec));
|
|
}
|
|
|
|
slong
|
|
acb_hypgeom_pfq_series_choose_n(const acb_poly_struct * a, slong p,
|
|
const acb_poly_struct * b, slong q,
|
|
const acb_poly_t z, slong len, slong prec)
|
|
{
|
|
slong n_skip, n_min, n_max, n_terminating, nint;
|
|
slong k, n;
|
|
double log2_z;
|
|
double * are;
|
|
double * aim;
|
|
double * bre;
|
|
double * bim;
|
|
acb_t t;
|
|
mag_t zmag;
|
|
int success;
|
|
|
|
if (z->length == 0) /* todo: check finite */
|
|
return 1;
|
|
|
|
mag_init(zmag);
|
|
acb_init(t);
|
|
|
|
are = flint_malloc(sizeof(double) * 2 * (p + q));
|
|
aim = are + p;
|
|
bre = aim + p;
|
|
bim = bre + q;
|
|
|
|
acb_get_mag(zmag, z->coeffs);
|
|
log2_z = mag_get_d_log2_approx(zmag);
|
|
|
|
n_skip = 1;
|
|
n_min = 1;
|
|
n_max = FLINT_MIN(WORD_MAX / 2, 50 + 10.0 * prec);
|
|
n_terminating = WORD_MAX;
|
|
|
|
/* e.g. for exp(x + O(x^100)), make sure that we actually
|
|
run the recurrence up to order 100.
|
|
todo: compute correctly based on degrees of inputs */
|
|
n_min = FLINT_MAX(n_min, len);
|
|
n_max = FLINT_MAX(n_max, n_min);
|
|
|
|
for (k = 0; k < p; k++)
|
|
{
|
|
acb_poly_get_coeff_acb(t, a + k, 0);
|
|
|
|
are[k] = arf_get_d(arb_midref(acb_realref(t)), ARF_RND_DOWN);
|
|
aim[k] = arf_get_d(arb_midref(acb_imagref(t)), ARF_RND_DOWN);
|
|
|
|
/* If the series is terminating, stop at this n. */
|
|
if (acb_is_int(t) && are[k] <= 0.0 && acb_poly_length(a + k) <= 1)
|
|
{
|
|
n_terminating = FLINT_MIN(n_terminating, (slong) (-are[k] + 1));
|
|
n_terminating = FLINT_MAX(n_terminating, 1);
|
|
}
|
|
else if (are[k] <= 0.01 && D_ABS(aim[k]) < 0.01)
|
|
{
|
|
/* If very close to an integer, we don't want to deal with it
|
|
using doubles, so fast forward (todo: could work with the
|
|
log2 of the difference instead when this happens). */
|
|
nint = floor(are[k] + 0.5);
|
|
if (D_ABS(nint - are[k]) < 0.01)
|
|
n_skip = FLINT_MAX(n_skip, 2 - nint);
|
|
}
|
|
}
|
|
|
|
n_max = FLINT_MIN(n_max, n_terminating);
|
|
|
|
for (k = 0; k < q; k++)
|
|
{
|
|
acb_poly_get_coeff_acb(t, b + k, 0);
|
|
|
|
bre[k] = arf_get_d(arb_midref(acb_realref(t)), ARF_RND_DOWN);
|
|
bim[k] = arf_get_d(arb_midref(acb_imagref(t)), ARF_RND_DOWN);
|
|
|
|
if (bre[k] <= 0.25)
|
|
{
|
|
n_min = FLINT_MAX(n_min, 2 - bre[k]);
|
|
|
|
/* Also avoid near-integers here (can even allow exact
|
|
integers when computing regularized hypergeometric functions). */
|
|
if (bre[k] <= 0.01 && D_ABS(bim[k]) < 0.01)
|
|
{
|
|
nint = floor(bre[k] + 0.5);
|
|
if (D_ABS(nint - bre[k]) < 0.01)
|
|
n_skip = FLINT_MAX(n_skip, 2 - nint);
|
|
}
|
|
}
|
|
}
|
|
|
|
success = acb_hypgeom_pfq_choose_n_double(&n, are, aim, p, bre, bim, q,
|
|
log2_z, n_skip, n_min, n_max, prec);
|
|
|
|
if (!success)
|
|
{
|
|
if (n_terminating <= n_max)
|
|
{
|
|
n = n_terminating;
|
|
}
|
|
else
|
|
{
|
|
n = FLINT_MAX(n_min, n);
|
|
n = FLINT_MIN(n_max, n);
|
|
}
|
|
}
|
|
|
|
flint_free(are);
|
|
acb_clear(t);
|
|
mag_clear(zmag);
|
|
|
|
return n;
|
|
}
|
|
|