diff --git a/doc/source/ca.rst b/doc/source/ca.rst index 891b511b4a..d6cad232e7 100644 --- a/doc/source/ca.rst +++ b/doc/source/ca.rst @@ -1231,6 +1231,37 @@ Special functions Sets *res* to the imaginary error function of *x*. +.. function:: void ca_elliptic_k(ca_t res, const ca_t m, ca_ctx_t ctx) + + Sets *res* to the complete elliptic integral of the first kind `K(m)` of *m*, defined by + + .. math:: + + K(m) = \int_0^{\pi/2} \frac{dt}{\sqrt{1-m \sin^2 t}} + = \int_0^1 + \frac{dt}{\left(\sqrt{1-t^2}\right)\left(\sqrt{1-mt^2}\right)}. + +.. function:: void ca_elliptic_e(ca_t res, const ca_t m, ca_ctx_t ctx) + + Sets *res* to the complete elliptic integral of the second kind `E(m)` of *m*, defined by + + .. math:: + + E(m) = \int_0^{\pi/2} \sqrt{1-m \sin^2 t} \, dt = + \int_0^1 + \frac{\sqrt{1-mt^2}}{\sqrt{1-t^2}} \, dt. + +.. function:: void ca_elliptic_pi(ca_t res, const ca_t n, const ca_t m, ca_ctx_t ctx) + + Sets *res* to the complete elliptic integral of the third kind `\Pi(n,m)` of *n* and *m*, defined by + + .. math:: + + \Pi(n, m) = \int_0^{\pi/2} + \frac{dt}{(1-n \sin^2 t) \sqrt{1-m \sin^2 t}} = + \int_0^1 + \frac{dt}{(1-nt^2) \sqrt{1-t^2} \sqrt{1-mt^2}}. + Numerical evaluation ------------------------------------------------------------------------------- diff --git a/src/ca.h b/src/ca.h index ee12508ee6..cdc6244c7b 100644 --- a/src/ca.h +++ b/src/ca.h @@ -480,6 +480,10 @@ void ca_erfi(ca_t res, const ca_t x, ca_ctx_t ctx); void ca_gamma(ca_t res, const ca_t x, ca_ctx_t ctx); +void ca_elliptic_k(ca_t res, const ca_t m, ca_ctx_t ctx); +void ca_elliptic_e(ca_t res, const ca_t m, ca_ctx_t ctx); +void ca_elliptic_pi(ca_t res, const ca_t n, const ca_t m, ca_ctx_t ctx); + /* Numerical evaluation */ void ca_get_acb_raw(acb_t res, const ca_t x, slong prec, ca_ctx_t ctx); diff --git a/src/ca/elliptic.c b/src/ca/elliptic.c new file mode 100644 index 0000000000..1dee10ae6b --- /dev/null +++ b/src/ca/elliptic.c @@ -0,0 +1,156 @@ +/* + Copyright (C) 2020 Fredrik Johansson + + This file is part of FLINT. + + FLINT 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 3 of the License, or + (at your option) any later version. See . +*/ + +#include "ca.h" + +void +ca_elliptic_k(ca_t res, const ca_t m, ca_ctx_t ctx) +{ + if (CA_IS_SPECIAL(m)) + { + if (ca_check_is_pos_inf(m, ctx) == T_TRUE || ca_check_is_neg_inf(m, ctx) == T_TRUE || + ca_check_is_pos_i_inf(m, ctx) == T_TRUE || ca_check_is_neg_i_inf(m, ctx) == T_TRUE) + ca_zero(res, ctx); + else if (ca_check_is_undefined(m, ctx) == T_TRUE || ca_check_is_uinf(m, ctx) == T_TRUE) + ca_undefined(res, ctx); + else + ca_unknown(res, ctx); + return; + } + + if (ca_check_is_zero(m, ctx) == T_TRUE) + { + ca_pi(res, ctx); + ca_div_ui(res, res, 2, ctx); + return; + } + else if (ca_check_is_one(m, ctx) == T_TRUE) + { + ca_uinf(res, ctx); + return; + } + + _ca_make_field_element(res, _ca_ctx_get_field_fx(ctx, CA_EllipticK, m), ctx); + fmpz_mpoly_q_gen(CA_MPOLY_Q(res), 0, CA_MCTX_1(ctx)); +} + +void +ca_elliptic_e(ca_t res, const ca_t m, ca_ctx_t ctx) +{ + if (CA_IS_SPECIAL(m)) + { + if (ca_check_is_pos_inf(m, ctx) == T_TRUE) + ca_pos_i_inf(res, ctx); + else if (ca_check_is_neg_inf(m, ctx) == T_TRUE) + ca_pos_inf(res, ctx); + else if (ca_check_is_pos_i_inf(m, ctx) == T_TRUE) + { + ca_pos_inf(res, ctx); + ca_t phase; + ca_init(phase, ctx); + ca_i(phase, ctx); + ca_sqrt(phase, phase, ctx); + ca_pow_ui(phase, phase, 3, ctx); + ca_neg(phase, phase, ctx); + ca_mul(res, res, phase, ctx); + ca_clear(phase, ctx); + } + else if (ca_check_is_neg_i_inf(m, ctx) == T_TRUE) + { + ca_pos_inf(res, ctx); + ca_t phase; + ca_init(phase, ctx); + ca_i(phase, ctx); + ca_sqrt(phase, phase, ctx); + ca_mul(res, res, phase, ctx); + ca_clear(phase, ctx); + } + else if (ca_check_is_undefined(m, ctx) == T_TRUE || ca_check_is_uinf(m, ctx) == T_TRUE) + ca_undefined(res, ctx); + else + ca_unknown(res, ctx); + return; + } + + if (ca_check_is_zero(m, ctx) == T_TRUE) + { + ca_pi(res, ctx); + ca_div_ui(res, res, 2, ctx); + return; + } + else if (ca_check_is_one(m, ctx) == T_TRUE) + { + ca_one(res, ctx); + return; + } + + _ca_make_field_element(res, _ca_ctx_get_field_fx(ctx, CA_EllipticE, m), ctx); + fmpz_mpoly_q_gen(CA_MPOLY_Q(res), 0, CA_MCTX_1(ctx)); +} + +void +ca_elliptic_pi(ca_t res, const ca_t n, const ca_t m, ca_ctx_t ctx) +{ + if (CA_IS_SPECIAL(n)) + { + if (ca_check_is_pos_inf(n, ctx) == T_TRUE || ca_check_is_neg_inf(n, ctx) == T_TRUE || + ca_check_is_pos_i_inf(n, ctx) == T_TRUE || ca_check_is_neg_i_inf(n, ctx) == T_TRUE) + ca_zero(res, ctx); + else if (ca_check_is_undefined(n, ctx) == T_TRUE || ca_check_is_uinf(n, ctx) == T_TRUE) + ca_undefined(res, ctx); + else + ca_unknown(res, ctx); + return; + } + + if (CA_IS_SPECIAL(m)) + { + if (ca_check_is_pos_inf(m, ctx) == T_TRUE || ca_check_is_neg_inf(m, ctx) == T_TRUE || + ca_check_is_pos_i_inf(m, ctx) == T_TRUE || ca_check_is_neg_i_inf(m, ctx) == T_TRUE) + ca_zero(res, ctx); + else if (ca_check_is_undefined(m, ctx) == T_TRUE || ca_check_is_uinf(m, ctx) == T_TRUE) + ca_undefined(res, ctx); + else + ca_unknown(res, ctx); + return; + } + + if (ca_check_is_one(n, ctx) == T_TRUE || ca_check_is_one(m, ctx) == T_TRUE) + { + ca_uinf(res, ctx); + return; + } + + if (ca_check_is_zero(n, ctx) == T_TRUE) + { + _ca_make_field_element(res, _ca_ctx_get_field_fx(ctx, CA_EllipticK, m), ctx); + fmpz_mpoly_q_gen(CA_MPOLY_Q(res), 0, CA_MCTX_1(ctx)); + return; + } + else if (ca_check_is_zero(m, ctx) == T_TRUE) + { + ca_t nc; + ca_init(nc, ctx); + ca_one(nc, ctx); + ca_sub(nc, nc, n, ctx); + ca_sqrt(nc, nc, ctx); + ca_inv(nc, nc, ctx); + ca_div_ui(nc, nc, 2, ctx); + ca_pi(res, ctx); + ca_mul(res, res, nc, ctx); + ca_clear(nc, ctx); + return; + } + + _ca_make_field_element(res, _ca_ctx_get_field_fxy(ctx, CA_EllipticPi, n, m), ctx); + fmpz_mpoly_q_gen(CA_MPOLY_Q(res), 0, CA_MCTX_1(ctx)); +} + diff --git a/src/ca/test/main.c b/src/ca/test/main.c index 13dfc46875..c9512b2d55 100644 --- a/src/ca/test/main.c +++ b/src/ca/test/main.c @@ -19,6 +19,7 @@ #include "t-ctx_init_clear.c" #include "t-div.c" #include "t-erf.c" +#include "t-elliptic.c" #include "t-exp.c" #include "t-field_init_clear.c" #include "t-fmpz_mpoly_evaluate.c" @@ -53,6 +54,7 @@ test_struct tests[] = TEST_FUNCTION(ca_ctx_init_clear), TEST_FUNCTION(ca_div), TEST_FUNCTION(ca_erf), + TEST_FUNCTION(ca_elliptic), TEST_FUNCTION(ca_exp), TEST_FUNCTION(ca_field_init_clear), TEST_FUNCTION(ca_fmpz_mpoly_evaluate), diff --git a/src/ca/test/t-elliptic.c b/src/ca/test/t-elliptic.c new file mode 100644 index 0000000000..bf239a3f0b --- /dev/null +++ b/src/ca/test/t-elliptic.c @@ -0,0 +1,153 @@ +/* + Copyright (C) 2020 Fredrik Johansson + + This file is part of FLINT. + + FLINT 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 3 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb_elliptic.h" +#include "ca.h" + +TEST_FUNCTION_START(ca_elliptic, state) +{ + slong iter; + + for (iter = 0; iter < 1000 * 0.1 * flint_test_multiplier(); iter++) + { + ca_ctx_t ctx; + ca_t x, y, ekx, eex, epxy; + acb_t ay, ekax, eeax, epaxy, aekx, aeex, aepxy; + + ca_ctx_init(ctx); + ca_init(x, ctx); + ca_init(y, ctx); + ca_init(ekx, ctx); + ca_init(eex, ctx); + ca_init(epxy, ctx); + acb_init(ekax); + acb_init(eeax); + acb_init(epaxy); + acb_init(aekx); + acb_init(aeex); + acb_init(aepxy); + acb_init(ay); + + ca_randtest(x, state, 3, 5, ctx); + if (n_randint(state, 2)) + ca_randtest(y, state, 3, 5, ctx); + else + ca_randtest_rational(y, state, 5, ctx); + if (n_randint(state, 2)) + ca_add(y, y, x, ctx); + + ca_elliptic_k(ekx, x, ctx); + ca_elliptic_e(eex, x, ctx); + ca_elliptic_pi(epxy, x, y, ctx); + + ca_get_acb(aekx, ekx, 53, ctx); + ca_get_acb(aeex, eex, 53, ctx); + ca_get_acb(aepxy, epxy, 53, ctx); + + ca_get_acb(ekax, x, 53, ctx); + acb_elliptic_k(ekax, ekax, 53); + ca_get_acb(eeax, x, 53, ctx); + acb_elliptic_e(eeax, eeax, 53); + ca_get_acb(epaxy, x, 53, ctx); + ca_get_acb(ay, y, 53, ctx); + acb_elliptic_pi(epaxy, epaxy, ay, 53); + + if (!acb_overlaps(aekx, ekax) || !acb_overlaps(aeex, eeax) || + !acb_overlaps(aepxy, epaxy)) + { + flint_printf("FAIL (Test 1)\n"); + flint_printf("x = "); ca_print(x, ctx); printf("\n\n"); + flint_printf("y = "); ca_print(y, ctx); printf("\n\n"); + flint_printf("ekx = "); ca_print(ekx, ctx); printf("\n\n"); + flint_printf("eex = "); ca_print(eex, ctx); printf("\n\n"); + flint_printf("epxy = "); ca_print(epxy, ctx); printf("\n\n"); + flint_abort(); + } + + /* Test the Legendre relation. */ + ca_si_sub(y, 1, x, ctx); + ca_t eky, eey, p; + acb_t ekay, eeay, ap, az; + ca_init(eky, ctx); + ca_init(eey, ctx); + ca_init(p, ctx); + acb_init(ekay); + acb_init(eeay); + acb_init(ap); + acb_init(az); + acb_zero(az); + + ca_pi(p, ctx); + ca_div_si(p, p, 2, ctx); + ca_get_acb(ap, p, 53, ctx); + ca_elliptic_k(eky, y, ctx); + ca_elliptic_e(eey, y, ctx); + + ca_mul(eey, eey, ekx, ctx); + ca_mul(eex, eex, eky, ctx); + ca_mul(ekx, ekx, eky, ctx); + ca_sub(p, p, eey, ctx); + ca_sub(p, p, eex, ctx); + ca_add(p, p, ekx, ctx); + ca_get_acb(ay, p, 53, ctx); + + ca_get_acb(ekay, y, 53, ctx); + acb_elliptic_k(ekay, ekay, 53); + ca_get_acb(eeay, y, 53, ctx); + acb_elliptic_e(eeay, eeay, 53); + + acb_mul(eeay, eeay, ekax, 53); + acb_mul(eeax, eeax, ekay, 53); + acb_mul(ekax, ekax, ekay, 53); + acb_sub(ap, ap, eeay, 53); + acb_sub(ap, ap, eeax, 53); + acb_add(ap, ap, ekax, 53); + + if (!acb_overlaps(ap, ay) || !acb_contains(ay, az)) + { + flint_printf("FAIL (Test 2)\n"); + flint_printf("x = "); ca_print(x, ctx); printf("\n\n"); + flint_printf("y = "); ca_print(y, ctx); printf("\n\n"); + flint_printf("ekx = "); ca_print(ekx, ctx); printf("\n\n"); + flint_printf("eex = "); ca_print(eex, ctx); printf("\n\n"); + flint_printf("eky = "); ca_print(eky, ctx); printf("\n\n"); + flint_printf("eey = "); ca_print(eey, ctx); printf("\n\n"); + flint_printf("ap = %{acb} \n", ap); + flint_printf("ay = %{acb} \n", ay); + + flint_abort(); + } + + ca_clear(x, ctx); + ca_clear(y, ctx); + ca_clear(ekx, ctx); + ca_clear(eex, ctx); + ca_clear(eky, ctx); + ca_clear(eey, ctx); + ca_clear(p, ctx); + ca_clear(epxy, ctx); + acb_clear(ay); + acb_clear(ap); + acb_clear(az); + acb_clear(ekax); + acb_clear(eeax); + acb_clear(ekay); + acb_clear(eeay); + acb_clear(epaxy); + acb_clear(aekx); + acb_clear(aeex); + acb_clear(aepxy); + ca_ctx_clear(ctx); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/ca_ext/get_acb_raw.c b/src/ca_ext/get_acb_raw.c index 3342f5b07e..415a3cf052 100644 --- a/src/ca_ext/get_acb_raw.c +++ b/src/ca_ext/get_acb_raw.c @@ -10,6 +10,7 @@ */ #include "acb_hypgeom.h" +#include "acb_elliptic.h" #include "ca_ext.h" #define ARB_CONST(f) \ @@ -122,6 +123,10 @@ ca_ext_get_acb_raw(acb_t res, ca_ext_t x, slong prec, ca_ctx_t ctx) case CA_Erfi: ACB_UNARY(acb_hypgeom_erfi) case CA_RiemannZeta: ACB_UNARY(acb_zeta) case CA_HurwitzZeta: ACB_BINARY(acb_hurwitz_zeta) + /* Complete elliptic integrals */ + case CA_EllipticK: ACB_UNARY(acb_elliptic_k) + case CA_EllipticE: ACB_UNARY(acb_elliptic_e) + case CA_EllipticPi: ACB_BINARY(acb_elliptic_pi) default: flint_throw(FLINT_ERROR, "ca_ext_get_acb_raw: unknown function\n"); } diff --git a/src/ca_field.h b/src/ca_field.h index c6e8e1b09e..6989b71430 100644 --- a/src/ca_field.h +++ b/src/ca_field.h @@ -41,6 +41,7 @@ int ca_field_cmp(const ca_field_t K1, const ca_field_t K2, ca_ctx_t ctx); void ca_field_build_ideal(ca_field_t K, ca_ctx_t ctx); void ca_field_build_ideal_erf(ca_field_t K, ca_ctx_t ctx); void ca_field_build_ideal_gamma(ca_field_t K, ca_ctx_t ctx); +void ca_field_build_ideal_elliptic(ca_field_t K, ca_ctx_t ctx); void ca_field_cache_init(ca_field_cache_t cache, ca_ctx_t ctx); void ca_field_cache_clear(ca_field_cache_t cache, ca_ctx_t ctx); diff --git a/src/ca_field/build_ideal.c b/src/ca_field/build_ideal.c index e12ff12a32..30c708f82e 100644 --- a/src/ca_field/build_ideal.c +++ b/src/ca_field/build_ideal.c @@ -1111,6 +1111,7 @@ ca_field_build_ideal(ca_field_t K, ca_ctx_t ctx) /* ca_field_build_ideal_sin_cos(K, ctx); */ ca_field_build_ideal_erf(K, ctx); ca_field_build_ideal_gamma(K, ctx); + ca_field_build_ideal_elliptic(K, ctx); if (ctx->options[CA_OPT_USE_GROEBNER]) { diff --git a/src/ca_field/build_ideal_elliptic.c b/src/ca_field/build_ideal_elliptic.c new file mode 100644 index 0000000000..5186acaf64 --- /dev/null +++ b/src/ca_field/build_ideal_elliptic.c @@ -0,0 +1,156 @@ +/* + Copyright (C) 2020 Fredrik Johansson + + This file is part of FLINT. + + FLINT 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 3 of the License, or + (at your option) any later version. See . +*/ + +#include "gr.h" +#include "ca.h" +#include "ca_ext.h" +#include "ca_field.h" +#include "fmpz_mpoly.h" + +void _ca_field_ideal_insert_clear_mpoly(ca_field_t K, fmpz_mpoly_t poly, fmpz_mpoly_ctx_t mctx, ca_ctx_t ctx); + +/* This merely implements the Legendre relation between the generators i, j (elliptic K-functions), k, l (elliptic E-functions) and pi_index (pi), if it exists. */ +void +ca_field_build_elliptic_relation(ca_field_t K, slong i, slong j, slong k, slong l, slong pi_index, ca_ctx_t ctx) +{ + ca_ptr xi, xj, xk, xl; + ca_t t, u; + truth_t e; + + ca_init(t, ctx); + ca_init(u, ctx); + + xi = CA_EXT_FUNC_ARGS(CA_FIELD_EXT_ELEM(K, i)); + xj = CA_EXT_FUNC_ARGS(CA_FIELD_EXT_ELEM(K, j)); + xk = CA_EXT_FUNC_ARGS(CA_FIELD_EXT_ELEM(K, k)); + xl = CA_EXT_FUNC_ARGS(CA_FIELD_EXT_ELEM(K, l)); + + ca_add(t, xi, xj, ctx); + ca_add(u, xk, xl, ctx); + + /* swap indices if necessary */ + if (ca_check_equal(xi, xl, ctx) == T_TRUE){ + ca_swap(xk, xl, ctx); + slong d = k; + k = l; + l = d; + } + + e = truth_and(truth_and(ca_check_equal(xi, xk, ctx), + ca_check_is_one(t, ctx)), ca_check_is_one(u, ctx)); + + if (e == T_TRUE) + { + fmpz_mpoly_t poly; + fmpz_mpoly_init(poly, CA_FIELD_MCTX(K, ctx)); + + slong len = CA_FIELD_LENGTH(K); + ulong* exp = (ulong*) GR_TMP_ALLOC(len * sizeof(ulong)); + for (slong v = 0; v < len; v++){ + exp[v] = 0; + } + + exp[i] = 1; + exp[l] = 1; + fmpz_mpoly_set_coeff_si_ui(poly, 2, exp, CA_FIELD_MCTX(K, ctx)); + exp[i] = 0; + exp[l] = 0; + exp[j] = 1; + exp[k] = 1; + fmpz_mpoly_set_coeff_si_ui(poly, 2, exp, CA_FIELD_MCTX(K, ctx)); + exp[k] = 0; + exp[i] = 1; + fmpz_mpoly_set_coeff_si_ui(poly, -2, exp, CA_FIELD_MCTX(K, ctx)); + exp[i] = 0; + exp[j] = 0; + exp[pi_index] = 1; + fmpz_mpoly_set_coeff_si_ui(poly, -1, exp, CA_FIELD_MCTX(K, ctx)); + + _ca_field_ideal_insert_clear_mpoly(K, poly, CA_FIELD_MCTX(K, ctx), ctx); + + GR_TMP_FREE(exp, len * sizeof(ulong)); + } + + ca_clear(t, ctx); + ca_clear(u, ctx); +} + +void +ca_field_build_ideal_elliptic(ca_field_t K, ca_ctx_t ctx) +{ + calcium_func_code Fi, Fj, Fk, Fl; + slong i, j, k, l, len, num_ellK, num_ellE, pi_index; + + len = CA_FIELD_LENGTH(K); + + if (len < 5) + { + return; + } + + num_ellK = 0; + num_ellE = 0; + pi_index = -1; + for (i = 0; i < len; i++) + { + Fi = CA_EXT_HEAD(CA_FIELD_EXT_ELEM(K, i)); + + if (Fi == CA_EllipticK) + { + num_ellK++; + } + else if (Fi == CA_EllipticE) + { + num_ellE++; + } + else if (Fi == CA_Pi) + { + pi_index = i; + } + } + + if (num_ellK < 2 || num_ellE < 2 || pi_index == -1) + return; + + for (i = 0; i < len; i++) + { + Fi = CA_EXT_HEAD(CA_FIELD_EXT_ELEM(K, i)); + + if (Fi == CA_EllipticK) + { + for (j = i + 1; j < len; j++) + { + Fj = CA_EXT_HEAD(CA_FIELD_EXT_ELEM(K, j)); + + if (Fj == CA_EllipticK) + { + for (k = 0; k < len; k++) + { + Fk = CA_EXT_HEAD(CA_FIELD_EXT_ELEM(K, k)); + + if (Fk == CA_EllipticE) + { + for (l = k + 1; l < len; l++) + { + Fl = CA_EXT_HEAD(CA_FIELD_EXT_ELEM(K, l)); + + if (Fl == CA_EllipticE) + { + ca_field_build_elliptic_relation(K, i, j, k, l, pi_index, ctx); + } + } + } + } + } + } + } + } +} diff --git a/src/ca_types.h b/src/ca_types.h index 33a33d70fa..bb3fff91cc 100644 --- a/src/ca_types.h +++ b/src/ca_types.h @@ -87,6 +87,10 @@ typedef enum CA_Erfi, CA_RiemannZeta, CA_HurwitzZeta, + /* Complete elliptic integrals */ + CA_EllipticK, + CA_EllipticE, + CA_EllipticPi, CA_FUNC_CODE_LENGTH } calcium_func_code; diff --git a/src/calcium/func_name.c b/src/calcium/func_name.c index 44541f7ff3..72bf7e30d5 100644 --- a/src/calcium/func_name.c +++ b/src/calcium/func_name.c @@ -63,6 +63,10 @@ const char * calcium_func_name(calcium_func_code func) case CA_Erfi: return "Erfi"; case CA_RiemannZeta: return "RiemannZeta"; case CA_HurwitzZeta: return "HurwitzZeta"; + /* Complete elliptic integrals */ + case CA_EllipticK: return "EllipticK"; + case CA_EllipticE: return "EllipticE"; + case CA_EllipticPi: return "EllipticPi"; default: return ""; } }