Home | History | Annotate | Line # | Download | only in tests
      1      1.1  mrg /* Chi-squared test for mpfr_nrandom
      2      1.1  mrg 
      3  1.1.1.3  mrg Copyright 2011-2023 Free Software Foundation, Inc.
      4      1.1  mrg Contributed by Charles Karney <charles (at) karney.com>, SRI International.
      5      1.1  mrg 
      6      1.1  mrg This file is part of the GNU MPFR Library.
      7      1.1  mrg 
      8      1.1  mrg The GNU MPFR Library is free software; you can redistribute it and/or modify
      9      1.1  mrg it under the terms of the GNU Lesser General Public License as published by
     10      1.1  mrg the Free Software Foundation; either version 3 of the License, or (at your
     11      1.1  mrg option) any later version.
     12      1.1  mrg 
     13      1.1  mrg The GNU MPFR Library is distributed in the hope that it will be useful, but
     14      1.1  mrg WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
     15      1.1  mrg or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
     16      1.1  mrg License for more details.
     17      1.1  mrg 
     18      1.1  mrg You should have received a copy of the GNU Lesser General Public License
     19      1.1  mrg along with the GNU MPFR Library; see the file COPYING.LESSER.  If not, see
     20  1.1.1.2  mrg https://www.gnu.org/licenses/ or write to the Free Software Foundation, Inc.,
     21      1.1  mrg 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */
     22      1.1  mrg 
     23      1.1  mrg #include "mpfr-test.h"
     24      1.1  mrg 
     25      1.1  mrg /* Return Phi(x) = erf(x / sqrt(2)) / 2, the cumulative probability function
     26      1.1  mrg  * for the normal distribution.  We only take differences of this function so
     27      1.1  mrg  * the offset doesn't matter; here Phi(0) = 0. */
     28      1.1  mrg static void
     29  1.1.1.3  mrg normal_cumulative (mpfr_ptr z, mpfr_ptr x, mpfr_rnd_t rnd)
     30      1.1  mrg {
     31      1.1  mrg   mpfr_sqrt_ui (z, 2, rnd);
     32      1.1  mrg   mpfr_div (z, x, z, rnd);
     33      1.1  mrg   mpfr_erf (z, z, rnd);
     34      1.1  mrg   mpfr_div_ui (z, z, 2, rnd);
     35      1.1  mrg }
     36      1.1  mrg 
     37      1.1  mrg /* Given nu and chisqp, compute probability that chisq > chisqp.  This uses,
     38      1.1  mrg  * A&S 26.4.16,
     39      1.1  mrg  *
     40      1.1  mrg  * Q(nu,chisqp) =
     41      1.1  mrg  *     erfc( (3/2)*sqrt(nu) * ( cbrt(chisqp/nu) - 1 + 2/(9*nu) ) ) / 2
     42      1.1  mrg  *
     43      1.1  mrg  * which is valid for nu > 30.  This is the basis for the formula in Knuth,
     44      1.1  mrg  * TAOCP, Vol 2, 3.3.1, Table 1.  It more accurate than the similar formula,
     45      1.1  mrg  * DLMF 8.11.10. */
     46      1.1  mrg static void
     47  1.1.1.3  mrg chisq_prob (mpfr_ptr q, long nu, mpfr_ptr chisqp)
     48      1.1  mrg {
     49      1.1  mrg   mpfr_t t;
     50      1.1  mrg   mpfr_rnd_t rnd;
     51      1.1  mrg 
     52      1.1  mrg   rnd = MPFR_RNDN;  /* This uses an approx formula.  Might as well use RNDN. */
     53      1.1  mrg   mpfr_init2 (t, mpfr_get_prec (q));
     54      1.1  mrg 
     55      1.1  mrg   mpfr_div_si (q, chisqp, nu, rnd); /* chisqp/nu */
     56      1.1  mrg   mpfr_cbrt (q, q, rnd);            /* (chisqp/nu)^(1/3) */
     57      1.1  mrg   mpfr_sub_ui (q, q, 1, rnd);       /* (chisqp/nu)^(1/3) - 1 */
     58      1.1  mrg   mpfr_set_ui (t, 2, rnd);
     59      1.1  mrg   mpfr_div_si (t, t, 9*nu, rnd); /* 2/(9*nu) */
     60      1.1  mrg   mpfr_add (q, q, t, rnd);       /* (chisqp/nu)^(1/3) - 1 + 2/(9*nu) */
     61      1.1  mrg   mpfr_sqrt_ui (t, nu, rnd);     /* sqrt(nu) */
     62      1.1  mrg   mpfr_mul_d (t, t, 1.5, rnd);   /* (3/2)*sqrt(nu) */
     63      1.1  mrg   mpfr_mul (q, q, t, rnd);       /* arg to erfc */
     64      1.1  mrg   mpfr_erfc (q, q, rnd);         /* erfc(...) */
     65      1.1  mrg   mpfr_div_ui (q, q, 2, rnd);    /* erfc(...)/2 */
     66      1.1  mrg 
     67      1.1  mrg   mpfr_clear (t);
     68      1.1  mrg }
     69      1.1  mrg 
     70      1.1  mrg /* The continuous chi-squared test on with a set of bins of equal width.
     71      1.1  mrg  *
     72      1.1  mrg  * A single precision is picked for sampling and the chi-squared calculation.
     73      1.1  mrg  * This should picked high enough so that binning in test doesn't need to be
     74      1.1  mrg  * accurately aligned with possible values of the deviates.  Also we need the
     75      1.1  mrg  * precision big enough that chi-squared calculation itself is reliable.
     76      1.1  mrg  *
     77      1.1  mrg  * There's no particular benefit is testing with at very higher precisions;
     78      1.1  mrg  * because of the way tnrandom samples, this just adds additional barely
     79      1.1  mrg  * significant random bits to the deviates.  So this chi-squared test with
     80      1.1  mrg  * continuous equal width bins isn't a good tool for finding problems here.
     81      1.1  mrg  *
     82      1.1  mrg  * The testing of low precision normal deviates is done by
     83      1.1  mrg  * test_nrandom_chisq_disc. */
     84      1.1  mrg static double
     85      1.1  mrg test_nrandom_chisq_cont (long num, mpfr_prec_t prec, int nu,
     86      1.1  mrg                          double xmin, double xmax, int verbose)
     87      1.1  mrg {
     88      1.1  mrg   mpfr_t x, a, b, dx, z, pa, pb, ps, t;
     89      1.1  mrg   long *counts;
     90      1.1  mrg   int i, inexact;
     91      1.1  mrg   long k;
     92      1.1  mrg   mpfr_rnd_t rnd, rndd;
     93      1.1  mrg   double Q, chisq;
     94      1.1  mrg 
     95      1.1  mrg   rnd = MPFR_RNDN;              /* For chi-squared calculation */
     96      1.1  mrg   rndd = MPFR_RNDD;             /* For sampling and figuring the bins */
     97      1.1  mrg   mpfr_inits2 (prec, x, a, b, dx, z, pa, pb, ps, t, (mpfr_ptr) 0);
     98      1.1  mrg 
     99      1.1  mrg   counts = (long *) tests_allocate ((nu + 1) * sizeof (long));
    100      1.1  mrg   for (i = 0; i <= nu; i++)
    101      1.1  mrg     counts[i] = 0;
    102      1.1  mrg 
    103      1.1  mrg   /* a and b are bounds of nu equally spaced bins.  Set dx = (b-a)/nu */
    104      1.1  mrg   mpfr_set_d (a, xmin, rnd);
    105      1.1  mrg   mpfr_set_d (b, xmax, rnd);
    106      1.1  mrg 
    107      1.1  mrg   mpfr_sub (dx, b, a, rnd);
    108      1.1  mrg   mpfr_div_si (dx, dx, nu, rnd);
    109      1.1  mrg 
    110      1.1  mrg   for (k = 0; k < num; ++k)
    111      1.1  mrg     {
    112      1.1  mrg       inexact = mpfr_nrandom (x, RANDS, rndd);
    113      1.1  mrg       if (inexact == 0)
    114      1.1  mrg         {
    115      1.1  mrg           /* one call in the loop pretended to return an exact number! */
    116      1.1  mrg           printf ("Error: mpfr_nrandom() returns a zero ternary value.\n");
    117      1.1  mrg           exit (1);
    118      1.1  mrg         }
    119      1.1  mrg       mpfr_sub (x, x, a, rndd);
    120      1.1  mrg       mpfr_div (x, x, dx, rndd);
    121      1.1  mrg       i = mpfr_get_si (x, rndd);
    122      1.1  mrg       ++counts[i >= 0 && i < nu ? i : nu];
    123      1.1  mrg     }
    124      1.1  mrg 
    125      1.1  mrg   mpfr_set (x, a, rnd);
    126      1.1  mrg   normal_cumulative (pa, x, rnd);
    127      1.1  mrg   mpfr_add_ui (ps, pa, 1, rnd);
    128      1.1  mrg   mpfr_set_zero (t, 1);
    129      1.1  mrg   for (i = 0; i <= nu; ++i)
    130      1.1  mrg     {
    131      1.1  mrg       if (i < nu)
    132      1.1  mrg         {
    133      1.1  mrg           mpfr_add (x, x, dx, rnd);
    134      1.1  mrg           normal_cumulative (pb, x, rnd);
    135      1.1  mrg           mpfr_sub (pa, pb, pa, rnd); /* prob for this bin */
    136      1.1  mrg         }
    137      1.1  mrg       else
    138      1.1  mrg         mpfr_sub (pa, ps, pa, rnd); /* prob for last bin, i = nu */
    139      1.1  mrg 
    140      1.1  mrg       /* Compute z = counts[i] - num * p; t += z * z / (num * p) */
    141      1.1  mrg       mpfr_mul_ui (pa, pa, num, rnd);
    142      1.1  mrg       mpfr_ui_sub (z, counts[i], pa, rnd);
    143      1.1  mrg       mpfr_sqr (z, z, rnd);
    144      1.1  mrg       mpfr_div (z, z, pa, rnd);
    145      1.1  mrg       mpfr_add (t, t, z, rnd);
    146      1.1  mrg       mpfr_swap (pa, pb);       /* i.e., pa = pb */
    147      1.1  mrg     }
    148      1.1  mrg 
    149      1.1  mrg   chisq = mpfr_get_d (t, rnd);
    150      1.1  mrg   chisq_prob (t, nu, t);
    151      1.1  mrg   Q = mpfr_get_d (t, rnd);
    152      1.1  mrg   if (verbose)
    153      1.1  mrg     {
    154      1.1  mrg       printf ("num = %ld, equal bins in [%.2f, %.2f], nu = %d: chisq = %.2f\n",
    155      1.1  mrg               num, xmin, xmax, nu, chisq);
    156      1.1  mrg       if (Q < 0.05)
    157      1.1  mrg         printf ("    WARNING: probability (less than 5%%) = %.2e\n", Q);
    158      1.1  mrg     }
    159      1.1  mrg 
    160      1.1  mrg   tests_free (counts, (nu + 1) * sizeof (long));
    161      1.1  mrg   mpfr_clears (x, a, b, dx, z, pa, pb, ps, t, (mpfr_ptr) 0);
    162      1.1  mrg   return Q;
    163      1.1  mrg }
    164      1.1  mrg 
    165      1.1  mrg /* Return a sequential number for a positive low-precision x.  x is altered by
    166  1.1.1.2  mrg  * this function.  low precision means prec = 2, 3, or 4.  High values of
    167      1.1  mrg  * precision will result in integer overflow. */
    168      1.1  mrg static long
    169  1.1.1.3  mrg sequential (mpfr_ptr x)
    170      1.1  mrg {
    171      1.1  mrg   long expt, prec;
    172      1.1  mrg 
    173      1.1  mrg   prec = mpfr_get_prec (x);
    174      1.1  mrg   expt =  mpfr_get_exp (x);
    175      1.1  mrg   mpfr_mul_2si (x, x, prec - expt, MPFR_RNDN);
    176      1.1  mrg 
    177      1.1  mrg   return expt * (1 << (prec - 1)) + mpfr_get_si (x, MPFR_RNDN);
    178      1.1  mrg }
    179      1.1  mrg 
    180      1.1  mrg /* The chi-squared test on low precision normal deviates.  wprec is the working
    181      1.1  mrg  * precision for the chi-squared calculation.  prec is the precision for the
    182      1.1  mrg  * sampling; choose this in [2,5].  The bins consist of all the possible
    183      1.1  mrg  * deviate values in the range [xmin, xmax] coupled with the value of inexact.
    184      1.1  mrg  * Thus with prec = 2, the bins are
    185      1.1  mrg  *   ...
    186      1.1  mrg  *   (7/16, 1/2)  x = 1/2, inexact = +1
    187      1.1  mrg  *   (1/2 , 5/8)  x = 1/2, inexact = -1
    188      1.1  mrg  *   (5/8 , 3/4)  x = 3/4, inexact = +1
    189      1.1  mrg  *   (3/4 , 7/8)  x = 3/4, inexact = -1
    190      1.1  mrg  *   (7/8 , 1  )  x = 1  , inexact = +1
    191      1.1  mrg  *   (1   , 5/4)  x = 1  , inexact = -1
    192      1.1  mrg  *   (5/4 , 3/2)  x = 3/2, inexact = +1
    193      1.1  mrg  *   (3/2 , 7/4)  x = 3/2, inexact = -1
    194      1.1  mrg  *   ...
    195      1.1  mrg  * In addition, two bins are allocated for [0,xmin) and (xmax,inf).
    196      1.1  mrg  *
    197      1.1  mrg  * This test is applied to the absolute values of the deviates.  The sign is
    198      1.1  mrg  * tested by test_nrandom_chisq_cont.  In any case, the way the sign is
    199      1.1  mrg  * assigned in mpfr_nrandom is trivial.  In addition, the sampling is with
    200      1.1  mrg  * MPFR_RNDN.  This is the rounding mode which elicits the most information.
    201      1.1  mrg  * trandom_deviate includes checks on the consistency of the results extracted
    202      1.1  mrg  * from a random_deviate with other rounding modes.  */
    203      1.1  mrg static double
    204      1.1  mrg test_nrandom_chisq_disc (long num, mpfr_prec_t wprec, int prec,
    205      1.1  mrg                          double xmin, double xmax, int verbose)
    206      1.1  mrg {
    207      1.1  mrg   mpfr_t x, v, pa, pb, z, t;
    208      1.1  mrg   mpfr_rnd_t rnd;
    209      1.1  mrg   int i, inexact, nu;
    210      1.1  mrg   long *counts;
    211      1.1  mrg   long k, seqmin, seqmax, seq;
    212      1.1  mrg   double Q, chisq;
    213      1.1  mrg 
    214      1.1  mrg   rnd = MPFR_RNDN;
    215      1.1  mrg   mpfr_init2 (x, prec);
    216      1.1  mrg   mpfr_init2 (v, prec+1);
    217      1.1  mrg   mpfr_inits2 (wprec, pa, pb, z, t, (mpfr_ptr) 0);
    218      1.1  mrg 
    219      1.1  mrg   mpfr_set_d (x, xmin, rnd);
    220      1.1  mrg   xmin = mpfr_get_d (x, rnd);
    221      1.1  mrg   mpfr_set (v, x, rnd);
    222      1.1  mrg   seqmin = sequential (x);
    223      1.1  mrg   mpfr_set_d (x, xmax, rnd);
    224      1.1  mrg   xmax = mpfr_get_d (x, rnd);
    225      1.1  mrg   seqmax = sequential (x);
    226      1.1  mrg 
    227      1.1  mrg   /* Two bins for each sequential number (for inexact = +/- 1), plus 1 for u <
    228      1.1  mrg    * umin and 1 for u > umax, minus 1 for degrees of freedom */
    229      1.1  mrg   nu = 2 * (seqmax - seqmin + 1) + 2 - 1;
    230      1.1  mrg   counts = (long *) tests_allocate ((nu + 1) * sizeof (long));
    231      1.1  mrg   for (i = 0; i <= nu; i++)
    232      1.1  mrg     counts[i] = 0;
    233      1.1  mrg 
    234      1.1  mrg   for (k = 0; k < num; ++k)
    235      1.1  mrg     {
    236      1.1  mrg       inexact = mpfr_nrandom (x, RANDS, rnd);
    237      1.1  mrg       if (mpfr_signbit (x))
    238      1.1  mrg         {
    239      1.1  mrg           inexact = -inexact;
    240      1.1  mrg           mpfr_setsign (x, x, 0, rnd);
    241      1.1  mrg         }
    242      1.1  mrg       /* Don't call sequential with small args to avoid undefined behavior with
    243      1.1  mrg        * zero and possibility of overflow. */
    244      1.1  mrg       seq = mpfr_greaterequal_p (x, v) ? sequential (x) : seqmin - 1;
    245      1.1  mrg       ++counts[seq < seqmin ? 0 :
    246      1.1  mrg                seq <= seqmax ? 2 * (seq - seqmin) + 1 + (inexact > 0 ? 0 : 1) :
    247      1.1  mrg                nu];
    248      1.1  mrg     }
    249      1.1  mrg 
    250      1.1  mrg   mpfr_set_zero (v, 1);
    251      1.1  mrg   normal_cumulative (pa, v, rnd);
    252      1.1  mrg   /* Cycle through all the bin boundaries using mpfr_nextabove at precision
    253      1.1  mrg    * prec + 1 starting at mpfr_nextbelow (xmin) */
    254      1.1  mrg   mpfr_set_d (x, xmin, rnd);
    255      1.1  mrg   mpfr_set (v, x, rnd);
    256      1.1  mrg   mpfr_nextbelow (v);
    257      1.1  mrg   mpfr_nextbelow (v);
    258      1.1  mrg   mpfr_set_zero (t, 1);
    259      1.1  mrg   for (i = 0; i <= nu; ++i)
    260      1.1  mrg     {
    261      1.1  mrg       if (i < nu)
    262      1.1  mrg         mpfr_nextabove (v);
    263      1.1  mrg       else
    264      1.1  mrg         mpfr_set_inf (v, 1);
    265      1.1  mrg       normal_cumulative (pb, v, rnd);
    266      1.1  mrg       mpfr_sub (pa, pb, pa, rnd);
    267      1.1  mrg 
    268      1.1  mrg       /* Compute z = counts[i] - num * p; t += z * z / (num * p).  2*num to
    269      1.1  mrg        * account for the fact the p needs to be doubled since we are
    270      1.1  mrg        * considering only the absolute value of the deviates. */
    271      1.1  mrg       mpfr_mul_ui (pa, pa, 2*num, rnd);
    272      1.1  mrg       mpfr_ui_sub (z, counts[i], pa, rnd);
    273      1.1  mrg       mpfr_sqr (z, z, rnd);
    274      1.1  mrg       mpfr_div (z, z, pa, rnd);
    275      1.1  mrg       mpfr_add (t, t, z, rnd);
    276      1.1  mrg       mpfr_swap (pa, pb);       /* i.e., pa = pb */
    277      1.1  mrg     }
    278      1.1  mrg 
    279      1.1  mrg   chisq = mpfr_get_d (t, rnd);
    280      1.1  mrg   chisq_prob (t, nu, t);
    281      1.1  mrg   Q = mpfr_get_d (t, rnd);
    282      1.1  mrg   if (verbose)
    283      1.1  mrg     {
    284      1.1  mrg       printf ("num = %ld, discrete (prec = %d) bins in [%.6f, %.2f], "
    285      1.1  mrg               "nu = %d: chisq = %.2f\n", num, prec, xmin, xmax, nu, chisq);
    286      1.1  mrg       if (Q < 0.05)
    287      1.1  mrg         printf ("    WARNING: probability (less than 5%%) = %.2e\n", Q);
    288      1.1  mrg     }
    289      1.1  mrg 
    290      1.1  mrg   tests_free (counts, (nu + 1) * sizeof (long));
    291      1.1  mrg   mpfr_clears (x, v, pa, pb, z, t, (mpfr_ptr) 0);
    292      1.1  mrg   return Q;
    293      1.1  mrg }
    294      1.1  mrg 
    295      1.1  mrg static void
    296      1.1  mrg run_chisq (double (*f)(long, mpfr_prec_t, int, double, double, int),
    297      1.1  mrg            long num, mpfr_prec_t prec, int bin,
    298      1.1  mrg            double xmin, double xmax, int verbose)
    299      1.1  mrg {
    300      1.1  mrg   double Q, Qcum, Qbad, Qthresh;
    301      1.1  mrg   int i;
    302      1.1  mrg 
    303      1.1  mrg   Qcum = 1;
    304      1.1  mrg   Qbad = 1.e-9;
    305      1.1  mrg   Qthresh = 0.01;
    306      1.1  mrg   for (i = 0; i < 3; ++i)
    307      1.1  mrg     {
    308      1.1  mrg       Q = (*f)(num, prec, bin, xmin, xmax, verbose);
    309      1.1  mrg       Qcum *= Q;
    310      1.1  mrg       if (Q > Qthresh)
    311      1.1  mrg         return;
    312      1.1  mrg       else if (Q < Qbad)
    313      1.1  mrg         {
    314      1.1  mrg           printf ("Error: mpfr_nrandom chi-squared failure "
    315      1.1  mrg                   "(prob = %.2e)\n", Q);
    316      1.1  mrg           exit (1);
    317      1.1  mrg         }
    318      1.1  mrg       num *= 10;
    319      1.1  mrg       Qthresh /= 10;
    320      1.1  mrg     }
    321      1.1  mrg   if (Qcum < Qbad)              /* Presumably this is true */
    322      1.1  mrg     {
    323      1.1  mrg       printf ("Error: mpfr_nrandom combined chi-squared failure "
    324      1.1  mrg               "(prob = %.2e)\n", Qcum);
    325      1.1  mrg       exit (1);
    326      1.1  mrg     }
    327      1.1  mrg }
    328      1.1  mrg 
    329      1.1  mrg int
    330      1.1  mrg main (int argc, char *argv[])
    331      1.1  mrg {
    332      1.1  mrg   long nbtests;
    333      1.1  mrg   int verbose;
    334      1.1  mrg 
    335      1.1  mrg   tests_start_mpfr ();
    336      1.1  mrg 
    337      1.1  mrg   verbose = 0;
    338      1.1  mrg   nbtests = 100000;
    339      1.1  mrg   if (argc > 1)
    340      1.1  mrg     {
    341      1.1  mrg       long a = atol (argv[1]);
    342      1.1  mrg       verbose = 1;
    343      1.1  mrg       if (a != 0)
    344      1.1  mrg         nbtests = a;
    345      1.1  mrg     }
    346      1.1  mrg 
    347      1.1  mrg   run_chisq (test_nrandom_chisq_cont, nbtests, 64, 60, -4, 4, verbose);
    348      1.1  mrg   run_chisq (test_nrandom_chisq_disc, nbtests, 64, 2, 0.0005, 3, verbose);
    349      1.1  mrg   run_chisq (test_nrandom_chisq_disc, nbtests, 64, 3, 0.002, 4, verbose);
    350      1.1  mrg   run_chisq (test_nrandom_chisq_disc, nbtests, 64, 4, 0.004, 4, verbose);
    351      1.1  mrg 
    352      1.1  mrg   tests_end_mpfr ();
    353      1.1  mrg   return 0;
    354      1.1  mrg }
    355