2017-11-10 14:08:51 +01:00
|
|
|
/*
|
|
|
|
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 "acb_calc.h"
|
|
|
|
|
|
|
|
static void
|
|
|
|
quad_simple(acb_t res, acb_calc_func_t f, void * param,
|
|
|
|
const acb_t a, const acb_t b, slong prec)
|
|
|
|
{
|
|
|
|
acb_t mid, delta, wide;
|
|
|
|
mag_t tmpm;
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
/* wide = mid +- [delta] */
|
|
|
|
acb_set(wide, mid);
|
|
|
|
arb_get_mag(tmpm, acb_realref(delta));
|
|
|
|
arb_add_error_mag(acb_realref(wide), tmpm);
|
|
|
|
arb_get_mag(tmpm, acb_imagref(delta));
|
|
|
|
arb_add_error_mag(acb_imagref(wide), tmpm);
|
|
|
|
|
|
|
|
/* Direct evaluation: integral = (b-a) * f([a,b]). */
|
|
|
|
f(res, wide, param, 0, prec);
|
|
|
|
acb_mul(res, res, delta, prec);
|
|
|
|
acb_mul_2exp_si(res, res, 1);
|
|
|
|
|
|
|
|
acb_clear(mid);
|
|
|
|
acb_clear(delta);
|
|
|
|
acb_clear(wide);
|
|
|
|
mag_clear(tmpm);
|
|
|
|
}
|
|
|
|
|
2017-11-12 18:59:31 +01:00
|
|
|
static void
|
|
|
|
heap_up(acb_ptr as, acb_ptr bs, acb_ptr vs, mag_ptr ms, slong n)
|
|
|
|
{
|
|
|
|
slong i, max, l, r;
|
|
|
|
i = 0;
|
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
max = i;
|
|
|
|
l = 2 * i + 1;
|
|
|
|
r = 2 * i + 2;
|
|
|
|
if (l < n && mag_cmp(ms + l, ms + max) > 0)
|
|
|
|
max = l;
|
|
|
|
if (r < n && mag_cmp(ms + r, ms + max) > 0)
|
|
|
|
max = r;
|
|
|
|
if (max != i)
|
|
|
|
{
|
|
|
|
acb_swap(as + i, as + max);
|
|
|
|
acb_swap(bs + i, bs + max);
|
|
|
|
acb_swap(vs + i, vs + max);
|
|
|
|
mag_swap(ms + i, ms + max);
|
|
|
|
i = max;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
heap_down(acb_ptr as, acb_ptr bs, acb_ptr vs, mag_ptr ms, slong n)
|
|
|
|
{
|
|
|
|
slong j, k;
|
|
|
|
|
|
|
|
k = n - 1;
|
|
|
|
j = (k - 1) / 2;
|
|
|
|
|
|
|
|
while (k > 0 && mag_cmp(ms + j, ms + k) < 0)
|
|
|
|
{
|
|
|
|
acb_swap(as + j, as + k);
|
|
|
|
acb_swap(bs + j, bs + k);
|
|
|
|
acb_swap(vs + j, vs + k);
|
|
|
|
mag_swap(ms + j, ms + k);
|
|
|
|
k = j;
|
|
|
|
j = (j - 1) / 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
_acb_overlaps(acb_t tmp, const acb_t a, const acb_t b, slong prec)
|
|
|
|
{
|
|
|
|
acb_sub(tmp, a, b, prec);
|
|
|
|
return acb_contains_zero(tmp);
|
|
|
|
}
|
|
|
|
|
2017-11-16 13:27:29 +01:00
|
|
|
int
|
2017-11-10 14:08:51 +01:00
|
|
|
acb_calc_integrate(acb_t res, acb_calc_func_t f, void * param,
|
|
|
|
const acb_t a, const acb_t b,
|
|
|
|
slong goal, const mag_t tol,
|
2017-11-22 00:24:20 +01:00
|
|
|
const acb_calc_integrate_opt_t options,
|
2017-11-10 14:08:51 +01:00
|
|
|
slong prec)
|
|
|
|
{
|
|
|
|
acb_ptr as, bs, vs;
|
2017-11-12 18:59:31 +01:00
|
|
|
mag_ptr ms;
|
2017-11-10 14:08:51 +01:00
|
|
|
acb_t s, t, u;
|
|
|
|
mag_t tmpm, tmpn, new_tol;
|
2017-11-22 00:24:20 +01:00
|
|
|
slong depth_limit, eval_limit, deg_limit;
|
2017-11-12 18:59:31 +01:00
|
|
|
slong depth, depth_max, eval, feval, top;
|
2017-11-16 13:27:29 +01:00
|
|
|
slong leaf_interval_count;
|
2017-11-22 00:24:20 +01:00
|
|
|
slong alloc;
|
|
|
|
int stopping, real_error, use_heap, status, gl_status, verbose;
|
|
|
|
|
|
|
|
if (options == NULL)
|
|
|
|
{
|
|
|
|
acb_calc_integrate_opt_t opt;
|
|
|
|
acb_calc_integrate_opt_init(opt);
|
|
|
|
return acb_calc_integrate(res, f, param, a, b, goal, tol, opt, prec);
|
|
|
|
}
|
2017-11-16 13:27:29 +01:00
|
|
|
|
|
|
|
status = ARB_CALC_SUCCESS;
|
2017-11-10 14:08:51 +01:00
|
|
|
|
|
|
|
acb_init(s);
|
|
|
|
acb_init(t);
|
|
|
|
acb_init(u);
|
|
|
|
mag_init(tmpm);
|
|
|
|
mag_init(tmpn);
|
|
|
|
mag_init(new_tol);
|
|
|
|
|
2017-11-22 00:24:20 +01:00
|
|
|
depth_limit = options->depth_limit;
|
2017-11-10 14:08:51 +01:00
|
|
|
if (depth_limit <= 0)
|
|
|
|
depth_limit = 2 * prec;
|
|
|
|
depth_limit = FLINT_MAX(depth_limit, 1);
|
|
|
|
|
2017-11-22 00:24:20 +01:00
|
|
|
eval_limit = options->eval_limit;
|
2017-11-10 14:08:51 +01:00
|
|
|
if (eval_limit <= 0)
|
2017-11-12 17:14:25 +01:00
|
|
|
eval_limit = 1000 * prec + prec * prec;
|
2017-11-10 14:08:51 +01:00
|
|
|
eval_limit = FLINT_MAX(eval_limit, 1);
|
|
|
|
|
|
|
|
goal = FLINT_MAX(goal, 0);
|
2017-11-22 00:24:20 +01:00
|
|
|
deg_limit = options->deg_limit;
|
2017-11-10 14:08:51 +01:00
|
|
|
if (deg_limit <= 0)
|
2018-02-12 15:12:30 +01:00
|
|
|
deg_limit = 0.5 * FLINT_MIN(goal, prec) + 60;
|
2017-11-10 14:08:51 +01:00
|
|
|
|
2017-11-22 00:24:20 +01:00
|
|
|
verbose = options->verbose;
|
|
|
|
use_heap = options->use_heap;
|
2017-11-12 18:59:31 +01:00
|
|
|
|
2017-11-22 00:24:20 +01:00
|
|
|
alloc = 4;
|
|
|
|
as = _acb_vec_init(alloc);
|
|
|
|
bs = _acb_vec_init(alloc);
|
|
|
|
vs = _acb_vec_init(alloc);
|
|
|
|
ms = _mag_vec_init(alloc);
|
2017-11-10 14:08:51 +01:00
|
|
|
|
2017-11-12 18:59:31 +01:00
|
|
|
/* Compute initial crude estimate for the whole interval. */
|
2017-11-10 14:08:51 +01:00
|
|
|
acb_set(as, a);
|
|
|
|
acb_set(bs, b);
|
|
|
|
quad_simple(vs, f, param, as, bs, prec);
|
2017-11-12 18:59:31 +01:00
|
|
|
mag_hypot(ms, arb_radref(acb_realref(vs)), arb_radref(acb_imagref(vs)));
|
2017-11-10 14:08:51 +01:00
|
|
|
|
|
|
|
depth = depth_max = 1;
|
|
|
|
eval = 1;
|
|
|
|
stopping = 0;
|
2017-11-16 13:27:29 +01:00
|
|
|
leaf_interval_count = 0;
|
2017-11-10 14:08:51 +01:00
|
|
|
|
|
|
|
/* Adjust absolute tolerance based on new information. */
|
|
|
|
acb_get_mag_lower(tmpm, vs);
|
|
|
|
mag_mul_2exp_si(tmpm, tmpm, -goal);
|
|
|
|
mag_max(new_tol, tol, tmpm);
|
|
|
|
|
|
|
|
acb_zero(s);
|
|
|
|
|
|
|
|
while (depth >= 1)
|
|
|
|
{
|
|
|
|
if (stopping == 0 && eval >= eval_limit - 1)
|
|
|
|
{
|
2017-11-22 00:24:20 +01:00
|
|
|
if (verbose > 0)
|
2017-11-10 14:08:51 +01:00
|
|
|
flint_printf("stopping at eval_limit %wd\n", eval_limit);
|
2017-11-16 13:27:29 +01:00
|
|
|
status = ARB_CALC_NO_CONVERGENCE;
|
2017-11-10 14:08:51 +01:00
|
|
|
stopping = 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2017-11-12 18:59:31 +01:00
|
|
|
if (use_heap)
|
|
|
|
top = 0;
|
|
|
|
else
|
|
|
|
top = depth - 1;
|
2017-11-10 14:08:51 +01:00
|
|
|
|
|
|
|
/* We are done with this subinterval. */
|
2017-11-12 18:59:31 +01:00
|
|
|
if (mag_cmp(ms + top, new_tol) < 0 ||
|
|
|
|
_acb_overlaps(u, as + top, bs + top, prec) || stopping)
|
2017-11-10 14:08:51 +01:00
|
|
|
{
|
2017-11-12 18:59:31 +01:00
|
|
|
acb_add(s, s, vs + top, prec);
|
2017-11-16 13:27:29 +01:00
|
|
|
leaf_interval_count++;
|
|
|
|
|
2017-11-10 14:08:51 +01:00
|
|
|
depth--;
|
2017-11-12 18:59:31 +01:00
|
|
|
if (use_heap && depth > 0)
|
|
|
|
{
|
|
|
|
acb_swap(as, as + depth);
|
|
|
|
acb_swap(bs, bs + depth);
|
|
|
|
acb_swap(vs, vs + depth);
|
|
|
|
mag_swap(ms, ms + depth);
|
|
|
|
heap_up(as, bs, vs, ms, depth);
|
|
|
|
}
|
2017-11-10 14:08:51 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Attempt using Gauss-Legendre rule. */
|
2017-11-12 18:59:31 +01:00
|
|
|
if (acb_is_finite(vs + top))
|
2017-11-10 14:08:51 +01:00
|
|
|
{
|
2017-11-16 13:27:29 +01:00
|
|
|
gl_status = acb_calc_integrate_gl_auto_deg(u, &feval, f, param,
|
2017-11-22 00:24:20 +01:00
|
|
|
as + top, bs + top, new_tol, deg_limit, verbose > 1, prec);
|
2017-11-10 14:08:51 +01:00
|
|
|
eval += feval;
|
|
|
|
|
|
|
|
/* We are done with this subinterval. */
|
2017-11-16 13:27:29 +01:00
|
|
|
if (gl_status == ARB_CALC_SUCCESS)
|
2017-11-10 14:08:51 +01:00
|
|
|
{
|
2017-11-22 00:24:20 +01:00
|
|
|
/* We know that the result is real. */
|
|
|
|
real_error = acb_is_finite(vs + top) && acb_is_real(vs + top);
|
|
|
|
|
2017-11-10 14:08:51 +01:00
|
|
|
if (real_error)
|
2017-11-12 17:14:25 +01:00
|
|
|
arb_zero(acb_imagref(u));
|
2017-11-10 14:08:51 +01:00
|
|
|
|
2017-11-12 17:14:25 +01:00
|
|
|
acb_add(s, s, u, prec);
|
2017-11-16 13:27:29 +01:00
|
|
|
leaf_interval_count++;
|
|
|
|
|
2017-11-10 14:08:51 +01:00
|
|
|
/* Adjust absolute tolerance based on new information. */
|
2017-11-12 17:14:25 +01:00
|
|
|
acb_get_mag_lower(tmpm, u);
|
2017-11-10 14:08:51 +01:00
|
|
|
mag_mul_2exp_si(tmpm, tmpm, -goal);
|
|
|
|
mag_max(new_tol, new_tol, tmpm);
|
|
|
|
|
|
|
|
depth--;
|
2017-11-12 18:59:31 +01:00
|
|
|
if (use_heap && depth > 0)
|
|
|
|
{
|
|
|
|
acb_swap(as, as + depth);
|
|
|
|
acb_swap(bs, bs + depth);
|
|
|
|
acb_swap(vs, vs + depth);
|
|
|
|
mag_swap(ms, ms + depth);
|
|
|
|
heap_up(as, bs, vs, ms, depth);
|
|
|
|
}
|
2017-11-10 14:08:51 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (depth >= depth_limit - 1)
|
|
|
|
{
|
2017-11-22 00:24:20 +01:00
|
|
|
if (verbose > 0)
|
2017-11-10 14:08:51 +01:00
|
|
|
flint_printf("stopping at depth_limit %wd\n", depth_limit);
|
2017-11-16 13:27:29 +01:00
|
|
|
status = ARB_CALC_NO_CONVERGENCE;
|
2017-11-10 14:08:51 +01:00
|
|
|
stopping = 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2017-11-22 00:24:20 +01:00
|
|
|
if (depth >= alloc - 1)
|
|
|
|
{
|
|
|
|
slong k;
|
|
|
|
as = flint_realloc(as, 2 * alloc * sizeof(acb_struct));
|
|
|
|
bs = flint_realloc(bs, 2 * alloc * sizeof(acb_struct));
|
|
|
|
vs = flint_realloc(vs, 2 * alloc * sizeof(acb_struct));
|
|
|
|
ms = flint_realloc(ms, 2 * alloc * sizeof(mag_struct));
|
|
|
|
for (k = alloc; k < 2 * alloc; k++)
|
|
|
|
{
|
|
|
|
acb_init(as + k);
|
|
|
|
acb_init(bs + k);
|
|
|
|
acb_init(vs + k);
|
|
|
|
mag_init(ms + k);
|
|
|
|
}
|
|
|
|
alloc *= 2;
|
|
|
|
}
|
|
|
|
|
2017-11-10 14:08:51 +01:00
|
|
|
/* Bisection. */
|
2017-11-12 18:59:31 +01:00
|
|
|
/* Interval [depth] becomes [mid, b]. */
|
|
|
|
acb_set(bs + depth, bs + top);
|
|
|
|
acb_add(as + depth, as + top, bs + top, prec);
|
2017-11-10 14:08:51 +01:00
|
|
|
acb_mul_2exp_si(as + depth, as + depth, -1);
|
|
|
|
|
2017-11-12 18:59:31 +01:00
|
|
|
/* Interval [top] becomes [a, mid]. */
|
|
|
|
acb_set(bs + top, as + depth);
|
|
|
|
|
|
|
|
/* Evaluate on [a, mid] */
|
|
|
|
quad_simple(vs + top, f, param, as + top, bs + top, prec);
|
|
|
|
mag_hypot(ms + top, arb_radref(acb_realref(vs + top)), arb_radref(acb_imagref(vs + top)));
|
|
|
|
eval++;
|
|
|
|
/* Adjust absolute tolerance based on new information. */
|
|
|
|
acb_get_mag_lower(tmpm, vs + top);
|
|
|
|
mag_mul_2exp_si(tmpm, tmpm, -goal);
|
|
|
|
mag_max(new_tol, new_tol, tmpm);
|
2017-11-10 14:08:51 +01:00
|
|
|
|
2017-11-12 18:59:31 +01:00
|
|
|
/* Evaluate on [mid, b] */
|
|
|
|
quad_simple(vs + depth, f, param, as + depth, bs + depth, prec);
|
|
|
|
mag_hypot(ms + depth, arb_radref(acb_realref(vs + depth)), arb_radref(acb_imagref(vs + depth)));
|
|
|
|
eval++;
|
2017-11-10 14:08:51 +01:00
|
|
|
/* Adjust absolute tolerance based on new information. */
|
|
|
|
acb_get_mag_lower(tmpm, vs + depth);
|
|
|
|
mag_mul_2exp_si(tmpm, tmpm, -goal);
|
|
|
|
mag_max(new_tol, new_tol, tmpm);
|
|
|
|
|
2017-11-12 18:59:31 +01:00
|
|
|
/* Make the interval with the larger error the priority. */
|
|
|
|
if (mag_cmp(ms + top, ms + depth) < 0)
|
|
|
|
{
|
|
|
|
acb_swap(as + top, as + depth);
|
|
|
|
acb_swap(bs + top, bs + depth);
|
|
|
|
acb_swap(vs + top, vs + depth);
|
|
|
|
mag_swap(ms + top, ms + depth);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (use_heap)
|
|
|
|
{
|
|
|
|
heap_up(as, bs, vs, ms, depth);
|
|
|
|
heap_down(as, bs, vs, ms, depth + 1);
|
|
|
|
}
|
|
|
|
|
2017-11-10 14:08:51 +01:00
|
|
|
depth++;
|
|
|
|
depth_max = FLINT_MAX(depth, depth_max);
|
|
|
|
}
|
|
|
|
|
2017-11-22 00:24:20 +01:00
|
|
|
if (verbose > 0)
|
2017-11-10 14:08:51 +01:00
|
|
|
{
|
2017-11-16 13:27:29 +01:00
|
|
|
flint_printf("depth %wd/%wd, eval %wd/%wd, %wd leaf intervals\n",
|
|
|
|
depth_max, depth_limit, eval, eval_limit, leaf_interval_count);
|
2017-11-10 14:08:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
acb_set(res, s);
|
|
|
|
|
2017-11-22 00:24:20 +01:00
|
|
|
_acb_vec_clear(as, alloc);
|
|
|
|
_acb_vec_clear(bs, alloc);
|
|
|
|
_acb_vec_clear(vs, alloc);
|
|
|
|
_mag_vec_clear(ms, alloc);
|
2017-11-10 14:08:51 +01:00
|
|
|
acb_clear(s);
|
|
|
|
acb_clear(t);
|
|
|
|
acb_clear(u);
|
|
|
|
mag_clear(tmpm);
|
|
|
|
mag_clear(tmpn);
|
|
|
|
mag_clear(new_tol);
|
2017-11-16 13:27:29 +01:00
|
|
|
|
|
|
|
return status;
|
2017-11-10 14:08:51 +01:00
|
|
|
}
|
|
|
|
|