586 changes: 586 additions & 0 deletions acb_dirichlet/lerch_phi_integral.c

Large diffs are not rendered by default.

199 changes: 199 additions & 0 deletions acb_dirichlet/test/t-lerch_phi.c
@@ -0,0 +1,199 @@
/*
Copyright (C) 2022 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_dirichlet.h"

void
acb_dirichlet_lerch_phi_test(acb_t res, const acb_t z, const acb_t s, const acb_t a, int algorithm, slong prec)
{
switch (algorithm % 3)
{
case 0:
acb_dirichlet_lerch_phi_direct(res, z, s, a, prec);
if (!acb_is_finite(res))
acb_dirichlet_lerch_phi(res, z, s, a, prec);
break;
case 1:
acb_dirichlet_lerch_phi_integral(res, z, s, a, prec);
break;
default:
acb_dirichlet_lerch_phi(res, z, s, a, prec);
break;
}
}

static void
acb_set_dddd(acb_t z, double a, double ar, double b, double br)
{
arf_set_d(arb_midref(acb_realref(z)), a);
mag_set_d(arb_radref(acb_realref(z)), ar);
arf_set_d(arb_midref(acb_imagref(z)), b);
mag_set_d(arb_radref(acb_imagref(z)), br);
}

#define NUM_TESTS 15

/* z, s, a, phi(z, s, a) */
const double testdata[NUM_TESTS][8] = {
{ 1.0, 0.00390625, 1.5, 0.0, 2.25, 0.0, 1.3422063426254739626, 0.13465412214420029791 },
{ 1.0, 0.0, 1.5, 0.0, 2.25, 0.0, 1.4975136076666680360, 0.0 },
{ 1.0, -0.00390625, 1.5, 0.0, 2.25, 0.0, 1.3422063426254739626, -0.13465412214420029791 },
{ 2.0, 0.00390625, 1.5, 0.0, 2.25, 0.0, -0.16955701974487404975, 0.61946301461898363407 },
{ 2.0, 0.0, 1.5, 0.0, 2.25, 0.0, -0.17140382129993246416, -0.62044054732729604008 },
{ 2.0, -0.00390625, 1.5, 0.0, 2.25, 0.0, -0.16955701974487404975, -0.61946301461898363407 },
{ 0.6875, 0.0, 0.0, 0.0, -1.0, 2.0, 3.2, 0.0 },
{ 0.0, 8.0, 1.0, 0.0, 1.0, -1.0, -0.21201062033942531891, 0.15142847985530888328 },
{ 0.0, 8.0, 1.0, 1.0, 1.0, -1.0, -0.18714764709994647918, -0.031327583631588241802 },
{ 1.875, 2.625, -1.0, 0.0, 1.0, 0.0, -0.10448979591836734694, -0.078367346938775510204 },
{ -2.5, -1.0, -3.75, 0.0, -3.75, 7.5, 195.9465378716877103, 889.34175804326870925 },
{ 0.0, -1.5, 1.0, 0.5, -1.0, 6.0, -0.20452787205323008395, 0.14062505401787546806 },
{ -3.0, 3.0, 9.5, 0.0, 0.0, -5.5, 5.5775511785583065106e-7, -4.488380385476415194e-7 },
{ -1.0, 0.0, -1.0, 0.0, -1.0, 0.0, -0.75, 0.0 },
{ -3.0, 0.0, -2.0, 0.0, -2.0, 0.0, 1.84375, 0.0 },
};

int main()
{
slong iter;
flint_rand_t state;

flint_printf("lerch_phi....");
fflush(stdout);

flint_randinit(state);

/* check test values */
for (iter = 0; iter < 5 * arb_test_multiplier(); iter++)
{
slong i;
acb_t z, s, a, p1, p2;
int alg;

acb_init(z);
acb_init(s);
acb_init(a);
acb_init(p1);
acb_init(p2);

for (i = 0; i < NUM_TESTS; i++)
{
alg = n_randlimb(state);

acb_set_dddd(z, testdata[i][0], 0.0, testdata[i][1], 0.0);
acb_set_dddd(s, testdata[i][2], 0.0, testdata[i][3], 0.0);
acb_set_dddd(a, testdata[i][4], 1e-14, testdata[i][5], 1e-14);
acb_set_dddd(p2, testdata[i][6], 1e-14, testdata[i][7], 1e-14);

acb_dirichlet_lerch_phi_test(p1, z, s, a, alg, 2 + n_randint(state, 100));

if (!acb_overlaps(p1, p2))
{
flint_printf("FAIL (test value)\n");
flint_printf("z = "); acb_printd(z, 15); flint_printf("\n\n");
flint_printf("s = "); acb_printd(s, 15); flint_printf("\n\n");
flint_printf("a = "); acb_printd(a, 15); flint_printf("\n\n");
flint_printf("p1 = "); acb_printd(p1, 15); flint_printf("\n\n");
flint_printf("p2 = "); acb_printd(p2, 15); flint_printf("\n\n");
flint_abort();
}
}

acb_clear(z);
acb_clear(s);
acb_clear(a);
acb_clear(p1);
acb_clear(p2);
}

for (iter = 0; iter < 500 * arb_test_multiplier(); iter++)
{
acb_t z, s, a, b, r1, r2, r3;
slong prec1, prec2;
int alg1, alg2, alg3;

/* flint_printf("iter %wd\n", iter); */

acb_init(z);
acb_init(s);
acb_init(a);
acb_init(b);
acb_init(r1);
acb_init(r2);
acb_init(r3);

prec1 = 2 + n_randint(state, 200);
prec2 = 2 + n_randint(state, 200);
alg1 = n_randlimb(state);
alg2 = n_randlimb(state);
alg3 = n_randlimb(state);

acb_randtest(z, state, 1 + n_randint(state, 500), 3);
acb_randtest(s, state, 1 + n_randint(state, 500), 3);
acb_randtest(a, state, 1 + n_randint(state, 500), 3);
acb_randtest(b, state, 1 + n_randint(state, 500), 10);
acb_randtest(r1, state, 1 + n_randint(state, 500), 10);
acb_randtest(r2, state, 1 + n_randint(state, 500), 10);

if (n_randint(state, 2))
acb_set_si(z, n_randint(state, 5) - 2);
if (n_randint(state, 2))
acb_set_si(a, n_randint(state, 5) - 2);
if (n_randint(state, 2))
acb_set_si(s, n_randint(state, 5) - 2);

acb_dirichlet_lerch_phi_test(r1, z, s, a, alg1, prec1);
acb_dirichlet_lerch_phi_test(r2, z, s, a, alg2, prec2);

if (!acb_overlaps(r1, r2))
{
flint_printf("FAIL: overlap\n\n");
flint_printf("iter = %wd\n\n", iter);
flint_printf("z = "); acb_printd(z, 30); flint_printf("\n\n");
flint_printf("s = "); acb_printd(s, 30); flint_printf("\n\n");
flint_printf("a = "); acb_printd(a, 30); flint_printf("\n\n");
flint_printf("r1 = "); acb_printd(r1, 30); flint_printf("\n\n");
flint_printf("r2 = "); acb_printd(r2, 30); flint_printf("\n\n");
flint_abort();
}

/* test phi(z,s,a) = z*phi(z,s,a+1) + a^-s */
acb_add_ui(b, a, 1, prec2);
acb_dirichlet_lerch_phi_test(r2, z, s, b, alg3, prec2);
acb_neg(r3, s);
acb_pow(r3, a, r3, prec2);
acb_addmul(r3, r2, z, prec2);

if (!acb_overlaps(r1, r3))
{
flint_printf("FAIL (2): overlap\n\n");
flint_printf("iter = %wd\n\n", iter);
flint_printf("z = "); acb_printd(z, 30); flint_printf("\n\n");
flint_printf("s = "); acb_printd(s, 30); flint_printf("\n\n");
flint_printf("a = "); acb_printd(a, 30); flint_printf("\n\n");
flint_printf("r1 = "); acb_printd(r1, 30); flint_printf("\n\n");
flint_printf("r2 = "); acb_printd(r2, 30); flint_printf("\n\n");
flint_printf("r3 = "); acb_printd(r3, 30); flint_printf("\n\n");
flint_abort();
}

acb_clear(z);
acb_clear(s);
acb_clear(a);
acb_clear(b);
acb_clear(r1);
acb_clear(r2);
acb_clear(r3);
}

flint_randclear(state);
flint_cleanup();
flint_printf("PASS\n");
return EXIT_SUCCESS;
}
28 changes: 28 additions & 0 deletions doc/source/acb_dirichlet.rst
Expand Up @@ -267,6 +267,34 @@ Hurwitz zeta function precomputation

Evaluates `\zeta(s,p/q)` using precomputed data, assuming that `0 < p/q \le 1`.

Lerch transcendent
-------------------------------------------------------------------------------

.. function:: void acb_dirichlet_lerch_phi_integral(acb_t res, const acb_t z, const acb_t s, const acb_t a, slong prec)
void acb_dirichlet_lerch_phi_direct(acb_t res, const acb_t z, const acb_t s, const acb_t a, slong prec)
void acb_dirichlet_lerch_phi(acb_t res, const acb_t z, const acb_t s, const acb_t a, slong prec)

Computes the Lerch transcendent

.. math ::
\Phi(z,s,a) = \sum_{k=0}^{\infty} \frac{z^k}{(k+a)^s}
which is analytically continued for `|z| \ge 1`.

The *direct* version evaluates a truncation of the defining series.
The *integral* version uses the Hankel contour integral

.. math ::
\Phi(z,s,a) = -\frac{\Gamma(1-s)}{2 \pi i} \int_C \frac{(-t)^{s-1} e^{-a t}}{1 - z e^{-t}} dt
where the path is deformed as needed to avoid poles and branch
cuts of the integrand.
The default method chooses an algorithm automatically and also
checks for some special cases where the function can be expressed
in terms of simpler functions (Hurwitz zeta, polylogarithms).

Stieltjes constants
-------------------------------------------------------------------------------

Expand Down