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