arb-based cosine minpoly calculation

This commit is contained in:
Fredrik Johansson 2013-05-31 17:33:12 +02:00
parent 39fd03828f
commit 9c6e1d71f3
6 changed files with 472 additions and 3 deletions

View file

@ -84,3 +84,30 @@ The exponential function
This implementation guarantees consistent rounding but will overflow
for too large *x*.
Trigonometric functions
--------------------------------------------------------------------------------
.. function:: void _elefun_cos_minpoly_roots(fmprb_struct * alpha, long d, ulong n, long prec)
Sets the vector *alpha* to the the *d* roots of `\Phi_n(x)`, computed
using a working precision of *prec* bits.
.. function:: void _elefun_cos_minpoly(fmpz * coeffs, long d, ulong n)
.. function:: void elefun_cos_minpoly(fmpz_poly_t poly, ulong n)
Computes `\Phi_n(x)`, the minimal polynomial of `\cos(2\pi/n)`,
which for `n > 2` has degree `d = \varphi(n) / 2`.
For small `n`, the coefficients of the polynomial are looked up
from a table. For large `n`, we compute numerical approximations of
the roots using :func:`_elefun_cos_minpoly_roots`, then multiply
the linear factors together using a balanced product tree, and convert
the numerical coefficients to exact integers.
Since `\Phi_n(x) = 2^r \prod_{i=1}^d (x - \cos(\alpha_i))` for some
`\alpha_i`, where `r = d - 1` if `n` is a power of two and `r = d`
otherwise, we can use the binomial theorem to estimate the required
working precision as `d + \log_2 {d \choose d / 2}`, plus a few
guard bits. This estimate is not optimal, but it is acceptably tight
for large `n`.

View file

@ -27,6 +27,7 @@
#define ELEFUN_H
#include "fmprb.h"
#include "fmprb_poly.h"
#include "fmpz_extras.h"
static __inline__ void
@ -57,5 +58,12 @@ int elefun_exp_precomp(fmprb_t z, const fmprb_t x, long prec, int minus_one);
void elefun_exp_via_mpfr(fmprb_t z, const fmprb_t x, long prec);
void _elefun_cos_minpoly_roots(fmprb_struct * alpha, long d, ulong n, long prec);
void _elefun_cos_minpoly(fmpz * coeffs, long d, ulong n);
void elefun_cos_minpoly(fmpz_poly_t poly, ulong n);
#endif

188
elefun/cos_minpoly.c Normal file
View file

@ -0,0 +1,188 @@
/*=============================================================================
This file is part of ARB.
ARB is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
ARB is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with ARB; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
=============================================================================*/
/******************************************************************************
Copyright (C) 2013 Fredrik Johansson
******************************************************************************/
#include "elefun.h"
#include "ulong_extras.h"
#define ELEFUN_COS_MINPOLY_MAX_LOOKUP 58
#define MAX_LENGTH 28
static const int lookup_table[ELEFUN_COS_MINPOLY_MAX_LOOKUP][MAX_LENGTH] =
{
{-1, 1},
{1, 1},
{1, 2},
{0, 1},
{-1, 2, 4},
{-1, 2},
{-1, -4, 4, 8},
{-1, 0, 2},
{1, -6, 0, 8},
{-1, -2, 4},
{1, 6, -12, -32, 16, 32},
{-3, 0, 4},
{-1, 6, 24, -32, -80, 32, 64},
{1, -4, -4, 8},
{1, 8, -16, -8, 16},
{1, 0, -8, 0, 8},
{1, -8, -40, 80, 240, -192, -448, 128, 256},
{-1, -6, 0, 8},
{1, 10, -40, -160, 240, 672, -448, -1024, 256, 512},
{5, 0, -20, 0, 16},
{1, -16, 32, 48, -96, -32, 64},
{-1, 6, 12, -32, -16, 32},
{-1, -12, 60, 280, -560, -1792, 1792, 4608, -2304, -5120, 1024, 2048},
{1, 0, -16, 0, 16},
{-1, 10, 100, -40, -800, 32, 2240, 0, -2560, 0, 1024},
{-1, -6, 24, 32, -80, -32, 64},
{1, 18, 0, -240, 0, 864, 0, -1152, 0, 512},
{-7, 0, 56, 0, -112, 0, 64},
{-1, 14, 112, -448, -2016, 4032, 13440, -15360, -42240, 28160, 67584,
-24576, -53248, 8192, 16384},
{1, -8, -16, 8, 16},
{-1, -16, 112, 672, -2016, -8064, 13440, 42240, -42240, -112640, 67584,
159744, -53248, -114688, 16384, 32768},
{1, 0, -32, 0, 160, 0, -256, 0, 128},
{1, -24, 48, 344, -688, -1088, 2176, 1280, -2560, -512, 1024},
{1, 8, -40, -80, 240, 192, -448, -128, 256},
{1, 16, -160, -368, 1760, 2272, -7232, -5504, 13824, 5632, -12288,
-2048, 4096},
{-3, 0, 36, 0, -96, 0, 64},
{-1, 18, 180, -960, -5280, 14784, 59136, -101376, -329472, 366080,
1025024, -745472, -1863680, 860160, 1966080, -524288, -1114112, 131072,
262144},
{-1, 10, 40, -160, -240, 672, 448, -1024, -256, 512},
{1, 24, -48, -632, 1264, 3296, -6592, -6784, 13568, 6144, -12288, -2048,
4096},
{1, 0, -48, 0, 304, 0, -512, 0, 256},
{1, -20, -220, 1320, 7920, -25344, -109824, 219648, 768768, -1025024,
-3075072, 2795520, 7454720, -4587520, -11141120, 4456448, 10027008,
-2359296, -4980736, 524288, 1048576},
{1, 16, 32, -48, -96, 32, 64},
{1, 22, -220, -1760, 7920, 41184, -109824, -439296, 768768, 2562560,
-3075072, -8945664, 7454720, 19496960, -11141120, -26738688, 10027008,
22413312, -4980736, -10485760, 1048576, 2097152},
{-11, 0, 220, 0, -1232, 0, 2816, 0, -2816, 0, 1024},
{1, -24, -144, 248, 1680, -864, -7168, 1152, 13824, -512, -12288, 0, 4096},
{1, -12, -60, 280, 560, -1792, -1792, 4608, 2304, -5120, -1024, 2048},
{-1, -24, 264, 2288, -11440, -64064, 192192, 823680, -1647360,
-5857280, 8200192, 25346048, -25346048, -70189056, 50135040, 127008768,
-63504384, -149422080, 49807360, 110100480, -22020096, -46137344,
4194304, 8388608},
{1, 0, -64, 0, 320, 0, -512, 0, 256},
{-1, 28, 196, -2968, -3136, 66304, 18816, -658816, -53760, 3587584, 78848,
-11741184, -57344, 24084480, 16384, -31195136, 0, 24772608, 0, -11010048,
0, 2097152},
{-1, -10, 100, 40, -800, -32, 2240, 0, -2560, 0, 1024},
{1, 32, -64, -1504, 3008, 16832, -33664, -76288, 152576, 173568, -347136,
-210944, 421888, 131072, -262144, -32768, 65536},
{13, 0, -364, 0, 2912, 0, -9984, 0, 16640, 0, -13312, 0, 4096},
{-1, 26, 364, -2912, -21840, 96096, 512512, -1464320, -6223360, 12446720,
44808192, -65175552, -206389248, 222265344, 635043840, -508035072,
-1333592064, 784465920, 1917583360, -807403520, -1857028096, 530579456,
1157627904, -201326592, -419430400, 33554432, 67108864},
{-1, 18, 0, -240, 0, 864, 0, -1152, 0, 512},
{1, 24, -432, -1208, 15216, 28064, -185024, -263424, 1149184, 1250304,
-4177920, -3356672, 9375744, 5324800, -13123584, -4947968, 11141120,
2490368, -5242880, -524288, 1048576},
{1, 0, -96, 0, 1376, 0, -6656, 0, 13568, 0, -12288, 0, 4096},
{1, -40, 80, 2120, -4240, -31648, 63296, 194432, -388864, -613376,
1226752, 1087488, -2174976, -1097728, 2195456, 589824, -1179648,
-131072, 262144}, {-1, -14, 112, 448, -2016, -4032, 13440, 15360,
-42240, -28160, 67584, 24576, -53248, -8192, 16384}
};
/* The coefficients in 2^d * \prod_{i=1}^d (x - cos(a_i)) are
easily bounded using the binomial theorem. */
static long
magnitude_bound(long d)
{
long res;
fmpz_t t;
fmpz_init(t);
fmpz_bin_uiui(t, d, d / 2);
res = fmpz_bits(t);
fmpz_clear(t);
return FLINT_ABS(res) + d;
}
void
_elefun_cos_minpoly(fmpz * coeffs, long d, ulong n)
{
fmprb_struct * alpha, * fcoeffs;
fmpz_t t;
len_t i, prec;
if (n <= ELEFUN_COS_MINPOLY_MAX_LOOKUP)
{
for (i = 0; i <= d; i++)
fmpz_set_si(coeffs + i, lookup_table[n - 1][i]);
}
else
{
fmpz_init(t);
alpha = _fmprb_vec_init(d);
fcoeffs = _fmprb_vec_init(d + 1);
prec = magnitude_bound(d) + 10 + FLINT_BIT_COUNT(d);
_elefun_cos_minpoly_roots(alpha, d, n, prec);
_fmprb_poly_product_roots(fcoeffs, alpha, d, prec);
for (i = 0; i <= d; i++)
{
fmprb_mul_2exp_si(fcoeffs + i, fcoeffs + i,
(n & (n - 1)) == 0 ? d - 1 : d);
if (!fmprb_get_unique_fmpz(coeffs + i, fcoeffs + i))
{
printf("insufficient precision computing cos minpolynomial!\n");
abort();
}
}
_fmprb_vec_clear(alpha, d);
_fmprb_vec_clear(fcoeffs, d + 1);
fmpz_clear(t);
}
}
void
elefun_cos_minpoly(fmpz_poly_t poly, ulong n)
{
if (n == 0)
{
fmpz_poly_set_si(poly, 1);
}
else
{
long d = (n <= 2) ? 1 : n_euler_phi(n) / 2;
fmpz_poly_fit_length(poly, d + 1);
_elefun_cos_minpoly(poly->coeffs, d, n);
_fmpz_poly_set_length(poly, d + 1);
}
}

View file

@ -0,0 +1,77 @@
/*=============================================================================
This file is part of ARB.
ARB is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
ARB is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with ARB; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
=============================================================================*/
/******************************************************************************
Copyright (C) 2013 Fredrik Johansson
******************************************************************************/
#include "elefun.h"
#include "ulong_extras.h"
/* computes all the d roots alpha_j of Phi_n(x) */
void
_elefun_cos_minpoly_roots(fmprb_struct * alpha, long d, ulong n, long prec)
{
fmprb_t t, u, v, s1, s2, c1, c2;
long i, j;
fmprb_init(t);
fmprb_init(u);
fmprb_init(v);
fmprb_init(s1);
fmprb_init(s2);
fmprb_init(c1);
fmprb_init(c2);
fmprb_const_pi(s1, prec);
fmprb_mul_ui(s1, s1, 2, prec);
fmprb_div_ui(s1, s1, n, prec);
fmprb_sin_cos(s1, c1, s1, prec);
fmprb_one(c2);
fmprb_zero(s2);
for (i = j = 0; j < d; i++)
{
if (n_gcd(n, i) == 1)
{
fmprb_set(alpha + j, c2);
j++;
}
fmprb_mul(t, c1, c2, prec);
fmprb_mul(u, s1, s2, prec);
fmprb_sub(v, t, u, prec);
fmprb_mul(t, c1, s2, prec);
fmprb_mul(u, s1, c2, prec);
fmprb_add(s2, t, u, prec);
fmprb_set(c2, v);
}
fmprb_clear(t);
fmprb_clear(u);
fmprb_clear(v);
fmprb_clear(s1);
fmprb_clear(s2);
fmprb_clear(c1);
fmprb_clear(c2);
}

169
elefun/test/t-cos_minpoly.c Normal file
View file

@ -0,0 +1,169 @@
/*=============================================================================
This file is part of FLINT.
FLINT is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
FLINT is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with FLINT; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
=============================================================================*/
/******************************************************************************
Copyright (C) 2011, 013 Fredrik Johansson
******************************************************************************/
#include "elefun.h"
/*
Generated with Mathematica:
Table[Mod[MinimalPolynomial[Cos[2 Pi/n]][1337], 31337], {n,1,500}]
*/
static const short testdata[] = {
1,
1336, 1338, 2675, 1337, 8113, 2673, 6283, 2719, 29508, 2765, 6949,
5437, 2788, 26742, 25554, 26194, 29376, 29506, 30945, 15614, 8957,
16643, 9263, 21050, 30556, 10533, 1570, 11562, 3988, 16546, 26642, 4041,
3581, 109, 9839, 27175, 11691, 1460, 28287, 18369, 16503, 3184, 13336,
23083, 12495, 3246, 14160, 8081, 5301, 8652, 28989, 24149, 17733, 1568,
4800, 28863, 29280, 13741, 30919, 29819, 28584, 8913, 550, 6207, 13930,
23373, 12644, 15265, 27975, 30386, 1603, 15894, 22276, 3138, 11610,
2208, 515, 30817, 23050, 4333, 25031, 13615, 5116, 18609, 25490, 14555,
22663, 8425, 21751, 19293, 3, 10688, 26829, 14467, 1426, 12413, 5305,
25377, 27164, 3711,
9613, 22340, 7457, 3704, 1795, 22877, 31060, 17472, 11317, 22274,
11036, 7796, 27242, 22174, 3663, 10507, 16599, 18192, 15208, 7257, 7022,
10810, 27891, 18495, 7032, 11383, 20768, 27351, 31089, 27723, 10486,
2075, 25298, 20531, 28548, 25342, 6510, 20657, 15608, 5534, 22145,
30150, 25222, 12128, 389, 21860, 9631, 4536, 4704, 3677, 27282, 26668,
20784, 15684, 12847, 1307, 10586, 24355, 27553, 10952, 8886, 25029,
29278, 29964, 17943, 1006, 5895, 11466, 16679, 17500, 5414, 3420, 17644,
5165, 6255, 2807, 30577, 26277, 14032, 2425, 13945, 27988, 17437, 28204,
11853, 12265, 8097, 24919, 10703, 18081, 19121, 23364, 14035, 2382,
1722, 21617, 11863, 27682, 8538, 26401,
1487, 14570, 14213, 18315, 30244, 14611, 25421, 13954, 29802,
29118, 5788, 7547, 9710, 21645, 17858, 20672, 2295, 21286, 7217, 30405,
5090, 22674, 5747, 5809, 13789, 16385, 23732, 12258, 10944, 14669, 2043,
1453, 13510, 12422, 24073, 3025, 28094, 2770, 9198, 27411, 24736, 28958,
23508, 27897, 17838, 10690, 5375, 29469, 22458, 9466, 28541, 16308,
20491, 10320, 9836, 673, 26630, 20819, 25687, 19263, 16620, 28683,
30268, 1113, 26632, 18450, 17555, 20121, 18083, 12796, 26659, 9788,
10448, 2828, 29753, 26653, 13636, 6270, 10398, 16224, 1481, 1153, 26387,
17835, 19289, 2683, 1937, 16760, 14372, 12632, 15716, 12423, 24202,
14543, 10763, 27059, 437, 18647, 17133, 27774,
2039, 3931, 7737, 20470, 11068, 26238, 28463, 22610, 28349, 23819,
22780, 4101, 13218, 12878, 25048, 25163, 11032, 10129, 2571, 9319,
11708, 6704, 19105, 11593, 24863, 26090, 15235, 18038, 22056, 19624,
12066, 9798, 16508, 22376, 15776, 10595, 28391, 18898, 11645, 16655,
19391, 11364, 28198, 4348, 6653, 11962, 22652, 18750, 22125, 21504,
23718, 25662, 6768, 24234, 29605, 8280, 5246, 23064, 1360, 21538, 4374,
8186, 7540, 24091, 3017, 23007, 12000, 11289, 8698, 22118, 5505, 18535,
29647, 15878, 4416, 8598, 13062, 8878, 9674, 5066, 17770, 24888, 20643,
1345, 22570, 1363, 3710, 18429, 11731, 14885, 12983, 18600, 26334,
27101, 17858, 22221, 2471, 911, 12033, 2824,
6354, 984, 28507, 3521, 17963, 6558, 11166, 24004, 24367, 8572,
19198, 6937, 15220, 13122, 3540, 589, 17503, 14073, 14954, 26020, 12974,
20684, 19844, 17852, 1097, 10831, 23848, 7013, 15683, 15954, 22290,
30257, 15807, 22775, 13607, 9428, 30055, 11607, 30426, 2579, 340, 29747,
25213, 28551, 5705, 15704, 10625, 16932, 3215, 16716, 6698, 21470,
29839, 511, 23506, 4338, 30506, 18038, 20430, 20586, 18225, 7721, 15812,
3140, 22149, 4949, 8125, 9897, 6323, 20612, 2012, 23744, 9414, 16497,
5557, 5225, 8518, 30549, 21805, 5692, 25222, 16326, 22995, 27432, 16385,
23506, 9911, 23131, 3880, 30647, 13222, 10416, 5619, 2078, 9411, 12398,
22772, 7328, 17932, 19965,
-1
};
int main()
{
long iter;
flint_rand_t state;
flint_randinit(state);
printf("cos_minpoly....");
fflush(stdout);
/* compare with table */
{
long n;
fmpz_poly_t p;
fmpz_poly_init(p);
for (n = 0; testdata[n] != -1; n++)
{
mp_limb_t y;
elefun_cos_minpoly(p, n);
y = fmpz_poly_evaluate_mod(p, 1337, 31337);
if (y != testdata[n])
{
printf("FAIL: n = %ld\n", n);
printf("y = %lu\n", y);
printf("\n");
abort();
}
}
fmpz_poly_clear(p);
}
/* check that Phi_n(cos(2pi/n)) = 0 */
for (iter = 0; iter < 100; iter++)
{
long n, prec;
fmpz_poly_t phi;
fmprb_poly_t fphi;
fmprb_t x, y;
n = 1 + n_randint(state, 2000);
prec = 2 + n_randint(state, 1000);
fmpz_poly_init(phi);
fmprb_poly_init(fphi);
fmprb_init(x);
fmprb_init(y);
elefun_cos_minpoly(phi, n);
fmprb_poly_set_fmpz_poly(fphi, phi, FMPR_PREC_EXACT);
fmprb_const_pi(x, prec);
fmprb_mul_ui(x, x, 2, prec);
fmprb_div_ui(x, x, n, prec);
fmprb_cos(x, x, prec);
fmprb_poly_evaluate(y, fphi, x, prec);
if (!fmprb_contains_zero(y))
{
printf("FAIL!\n");
printf("n = %ld, prec = %ld\n", n, prec);
printf("phi = "); fmpz_poly_print(phi); printf("\n\n");
printf("x = "); fmprb_printd(x, prec / 3.33); printf("\n\n");
printf("y = "); fmprb_printd(y, prec / 3.33); printf("\n\n");
abort();
}
fmpz_poly_clear(phi);
fmprb_poly_clear(fphi);
fmprb_clear(x);
fmprb_clear(y);
}
_fmpz_cleanup();
printf("PASS\n");
return 0;
}

View file

@ -25,7 +25,7 @@
#include "fmprb.h"
#include "fmprb_poly.h"
#include "arith.h"
#include "elefun.h"
void
_fmprb_cos_pi_fmpq_algebraic(fmprb_t c, ulong p, ulong q, long prec)
@ -98,9 +98,9 @@ _fmprb_cos_pi_fmpq_algebraic(fmprb_t c, ulong p, ulong q, long prec)
fmprb_poly_init(fpoly);
if (p % 2 == 0)
arith_cos_minpoly(poly, q);
elefun_cos_minpoly(poly, q);
else
arith_cos_minpoly(poly, 2 * q);
elefun_cos_minpoly(poly, 2 * q);
eval_extra_prec = fmpz_poly_max_bits(poly);
eval_extra_prec = FLINT_ABS(eval_extra_prec);