mirror of
https://github.com/vale981/arb
synced 2025-03-05 09:21:38 -05:00
303 lines
7.3 KiB
C
303 lines
7.3 KiB
C
/*
|
|
Copyright (C) 2017 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"
|
|
#include "acb_calc.h"
|
|
|
|
/*
|
|
Gauss-Legendre quadrature nodes are cached to speed up multiple integrations
|
|
and adaptive subdivision. The steps of 2^(n/2) used here give slightly better
|
|
performance than steps of 2^n (we use at most 1.4x more points than
|
|
needed and not 2x more points) but may require more precomputation.
|
|
*/
|
|
|
|
#define GL_STEPS 38
|
|
|
|
const slong gl_steps[GL_STEPS] = {1, 2, 4, 6, 8, 12, 16, 22, 32, 46, 64,
|
|
90, 128, 182, 256, 362, 512, 724, 1024, 1448, 2048, 2896, 4096,
|
|
5792, 8192, 11586, 16384, 23170, 32768, 46340, 65536, 92682,
|
|
131072, 185364, 262144, 370728, 524288, 741456};
|
|
|
|
typedef struct
|
|
{
|
|
slong gl_prec[GL_STEPS];
|
|
arb_ptr gl_nodes[GL_STEPS];
|
|
arb_ptr gl_weights[GL_STEPS];
|
|
}
|
|
gl_cache_struct;
|
|
|
|
TLS_PREFIX gl_cache_struct * gl_cache = NULL;
|
|
|
|
void gl_cleanup()
|
|
{
|
|
slong i;
|
|
|
|
if (gl_cache == NULL)
|
|
return;
|
|
|
|
for (i = 0; i < GL_STEPS; i++)
|
|
{
|
|
if (gl_cache->gl_prec[i] != 0)
|
|
{
|
|
_arb_vec_clear(gl_cache->gl_nodes[i], (gl_steps[i] + 1) / 2);
|
|
_arb_vec_clear(gl_cache->gl_weights[i], (gl_steps[i] + 1) / 2);
|
|
}
|
|
}
|
|
|
|
flint_free(gl_cache);
|
|
gl_cache = NULL;
|
|
}
|
|
|
|
void gl_init()
|
|
{
|
|
gl_cache = flint_calloc(1, sizeof(gl_cache_struct));
|
|
flint_register_cleanup_function(gl_cleanup);
|
|
}
|
|
|
|
/* Compute GL node and weight of index k for n = gl_steps[i]. Cached. */
|
|
void
|
|
acb_calc_gl_node(arb_t x, arb_t w, slong i, slong k, slong prec)
|
|
{
|
|
slong n, kk, jj, wp;
|
|
|
|
if (i < 0 || i >= GL_STEPS || prec < 2)
|
|
flint_abort();
|
|
|
|
if (gl_cache == NULL)
|
|
gl_init();
|
|
|
|
n = gl_steps[i];
|
|
|
|
if (k < 0 || k >= n)
|
|
flint_abort();
|
|
|
|
if (2 * k < n)
|
|
kk = k;
|
|
else
|
|
kk = n - 1 - k;
|
|
|
|
if (gl_cache->gl_prec[i] < prec)
|
|
{
|
|
if (gl_cache->gl_prec[i] == 0)
|
|
{
|
|
gl_cache->gl_nodes[i] = _arb_vec_init((n + 1) / 2);
|
|
gl_cache->gl_weights[i] = _arb_vec_init((n + 1) / 2);
|
|
}
|
|
|
|
wp = FLINT_MAX(prec, gl_cache->gl_prec[i] * 2 + 30);
|
|
|
|
for (jj = 0; 2 * jj < n; jj++)
|
|
{
|
|
arb_hypgeom_legendre_p_ui_root(gl_cache->gl_nodes[i] + jj,
|
|
gl_cache->gl_weights[i] + jj, n, jj, wp);
|
|
}
|
|
|
|
gl_cache->gl_prec[i] = wp;
|
|
}
|
|
|
|
if (2 * k < n)
|
|
arb_set_round(x, gl_cache->gl_nodes[i] + kk, prec);
|
|
else
|
|
arb_neg_round(x, gl_cache->gl_nodes[i] + kk, prec);
|
|
|
|
arb_set_round(w, gl_cache->gl_weights[i] + kk, prec);
|
|
}
|
|
|
|
int
|
|
acb_calc_integrate_gl_auto_deg(acb_t res, slong * eval_count,
|
|
acb_calc_func_t f, void * param,
|
|
const acb_t a, const acb_t b, const mag_t tol,
|
|
slong deg_limit, int verbose, slong prec)
|
|
{
|
|
acb_t mid, delta, wide;
|
|
mag_t tmpm;
|
|
slong status;
|
|
acb_t s, v;
|
|
mag_t M, X, Y, rho, err, t, best_rho;
|
|
slong k, Xexp;
|
|
slong i, n, best_n;
|
|
|
|
status = ARB_CALC_NO_CONVERGENCE;
|
|
|
|
if (deg_limit <= 0)
|
|
{
|
|
acb_indeterminate(res);
|
|
eval_count[0] = 0;
|
|
return status;
|
|
}
|
|
|
|
acb_init(mid);
|
|
acb_init(delta);
|
|
acb_init(wide);
|
|
mag_init(tmpm);
|
|
|
|
/* delta = (b-a)/2 */
|
|
acb_sub(delta, b, a, prec);
|
|
acb_mul_2exp_si(delta, delta, -1);
|
|
|
|
/* mid = (a+b)/2 */
|
|
acb_add(mid, a, b, prec);
|
|
acb_mul_2exp_si(mid, mid, -1);
|
|
|
|
acb_init(s);
|
|
acb_init(v);
|
|
mag_init(M);
|
|
mag_init(X);
|
|
mag_init(Y);
|
|
mag_init(rho);
|
|
mag_init(t);
|
|
mag_init(err);
|
|
mag_init(best_rho);
|
|
|
|
best_n = -1;
|
|
eval_count[0] = 0;
|
|
|
|
mag_inf(err);
|
|
|
|
for (Xexp = 0; Xexp < prec /* && Xexp == 0 */; Xexp += FLINT_MAX(1, Xexp))
|
|
{
|
|
mag_one(X);
|
|
mag_mul_2exp_si(X, X, Xexp + 1);
|
|
|
|
/* rho = X + sqrt(X^2 - 1) (lower bound) */
|
|
mag_mul_lower(rho, X, X);
|
|
mag_one(t);
|
|
mag_sub_lower(rho, rho, t);
|
|
mag_sqrt_lower(rho, rho);
|
|
mag_add_lower(rho, rho, X);
|
|
|
|
/* Y = sqrt(X^2 - 1) (upper bound) */
|
|
mag_mul(Y, X, X);
|
|
mag_one(t);
|
|
mag_sub(Y, Y, t);
|
|
mag_sqrt(Y, Y);
|
|
|
|
acb_zero(wide);
|
|
mag_set(arb_radref(acb_realref(wide)), X);
|
|
mag_set(arb_radref(acb_imagref(wide)), Y);
|
|
|
|
/* transform to [a,b] */
|
|
acb_mul(wide, wide, delta, prec);
|
|
acb_add(wide, wide, mid, prec);
|
|
|
|
f(v, wide, param, 1, prec);
|
|
eval_count[0]++;
|
|
|
|
/* no chance */
|
|
if (!acb_is_finite(v))
|
|
break;
|
|
|
|
/* M = (b-a)/2 |f| */
|
|
acb_get_mag(M, v);
|
|
acb_get_mag(tmpm, delta);
|
|
mag_mul(M, M, tmpm);
|
|
|
|
/* Search for the smallest n that gives err < tol (if possible) */
|
|
for (i = 0; i < GL_STEPS && gl_steps[i] <= deg_limit; i++)
|
|
{
|
|
n = gl_steps[i];
|
|
|
|
/* (64/15) M / ((rho-1) rho^(2n-1)) */
|
|
mag_pow_ui_lower(t, rho, 2 * n - 1);
|
|
mag_one(tmpm);
|
|
mag_sub_lower(tmpm, rho, tmpm);
|
|
mag_mul_lower(t, t, tmpm);
|
|
mag_mul_ui_lower(t, t, 15);
|
|
mag_div(t, M, t);
|
|
mag_mul_2exp_si(t, t, 6);
|
|
|
|
if (mag_cmp(t, tol) < 0)
|
|
{
|
|
status = ARB_CALC_SUCCESS;
|
|
|
|
/* The best so far. */
|
|
if (best_n == -1 || n < best_n)
|
|
{
|
|
mag_set(err, t);
|
|
mag_set(best_rho, rho);
|
|
best_n = n;
|
|
}
|
|
|
|
/* Best possible n. */
|
|
if (n == 1)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Evaluate best found Gauss-Legendre quadrature rule. */
|
|
if (status == ARB_CALC_SUCCESS)
|
|
{
|
|
arb_t x, w;
|
|
arb_init(x);
|
|
arb_init(w);
|
|
|
|
if (verbose)
|
|
{
|
|
acb_get_mag(tmpm, delta);
|
|
flint_printf(" {GL deg %wd on [", best_n);
|
|
acb_printn(a, 10, ARB_STR_NO_RADIUS); flint_printf(", ");
|
|
acb_printn(b, 10, ARB_STR_NO_RADIUS);
|
|
flint_printf("], delta "); mag_printd(tmpm, 5);
|
|
flint_printf(", rho "); mag_printd(best_rho, 5);
|
|
flint_printf(", tol "); mag_printd(tol, 3);
|
|
flint_printf("}\n");
|
|
}
|
|
|
|
if (best_n == -1)
|
|
flint_abort();
|
|
|
|
for (i = 0; i < GL_STEPS; i++)
|
|
if (gl_steps[i] == best_n)
|
|
break;
|
|
|
|
acb_zero(s);
|
|
|
|
for (k = 0; k < best_n; k++)
|
|
{
|
|
acb_calc_gl_node(x, w, i, k, prec);
|
|
acb_mul_arb(wide, delta, x, prec);
|
|
acb_add(wide, wide, mid, prec);
|
|
f(v, wide, param, 0, prec);
|
|
acb_addmul_arb(s, v, w, prec);
|
|
}
|
|
|
|
eval_count[0] += best_n;
|
|
|
|
acb_mul(res, s, delta, prec);
|
|
acb_add_error_mag(res, err);
|
|
|
|
arb_clear(x);
|
|
arb_clear(w);
|
|
}
|
|
else
|
|
{
|
|
acb_indeterminate(res);
|
|
}
|
|
|
|
acb_clear(s);
|
|
acb_clear(v);
|
|
mag_clear(M);
|
|
mag_clear(X);
|
|
mag_clear(Y);
|
|
mag_clear(rho);
|
|
mag_clear(t);
|
|
mag_clear(err);
|
|
mag_clear(best_rho);
|
|
|
|
acb_clear(mid);
|
|
acb_clear(delta);
|
|
acb_clear(wide);
|
|
mag_clear(tmpm);
|
|
|
|
return status;
|
|
}
|
|
|