SSJ API Documentation
Stochastic Simulation in Java
Loading...
Searching...
No Matches
ChiSquareNoncentralDist.java
1/*
2 * Class: ChiSquareNoncentralDist
3 * Description: Non-central chi-square distribution
4 * Environment: Java
5 * Software: SSJ
6 * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
7 * Organization: DIRO, Universite de Montreal
8 * @author Richard Simard
9 * @since March 2008
10 *
11 *
12 * Licensed under the Apache License, Version 2.0 (the "License");
13 * you may not use this file except in compliance with the License.
14 * You may obtain a copy of the License at
15 *
16 * http://www.apache.org/licenses/LICENSE-2.0
17 *
18 * Unless required by applicable law or agreed to in writing, software
19 * distributed under the License is distributed on an "AS IS" BASIS,
20 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21 * See the License for the specific language governing permissions and
22 * limitations under the License.
23 *
24 */
25package umontreal.ssj.probdist;
26
27import umontreal.ssj.util.*;
28import umontreal.ssj.functions.MathFunction;
29
62
63 protected static final int PREC = 15; // num. decimal digits of precision
64 protected static final double EPS = Num.TEN_NEG_POW[PREC];
65
66 // To get better precision for very small probabilities
67 private static final double PROB_MIN = Double.MIN_VALUE / EPS;
68 private static final boolean DETAIL = false; // For debugging
69 private static final double PARLIM = 1000.0; // Parameters limit
70
71 private double nu;
72 private double lambda;
73 private PoissonDist pois;
74 private int jmin = -1;
75 private int jmax = -1;
76
77 private static class Function implements MathFunction {
78 protected double nu;
79 protected double lambda;
80 protected double u;
81
82 public Function(double nu, double lambda, double u) {
83 this.nu = nu;
84 this.lambda = lambda;
85 this.u = u;
86 }
87
88 public double evaluate(double x) {
89 return u - cdf(nu, lambda, x);
90 }
91 }
92
93 private static double getXLIM(double nu, double lambda) {
94 // for x >= XLIM, density(x) = 0, and cdf(x) = 1
95 return (1600.0 + 20.0 * (nu + lambda));
96 }
97
98 private static int calcJmin(PoissonDist pois) {
99 /*
100 * Find first lower j such that pois.cdf(j) < EPS. If EPS is chosen larger, must
101 * change the 7*sig below to find jmin.
102 */
103 double lam = pois.getLambda();
104 double sig = Math.sqrt(lam);
105 int j = (int) (lam - 7.0 * sig);
106 if (j < 0)
107 return 0;
108 while (j > 0 && pois.cdf(j) > EPS)
109 j--;
110 return j + 1;
111 }
112
113 private static int calcJmax(PoissonDist pois) {
114 /*
115 * Find first upper j such that pois.barF(j) < EPS. If EPS is chosen larger,
116 * must change the 7*sig below to find jmax.
117 */
118 double lam = pois.getLambda();
119 double sig = Math.sqrt(lam);
120 int j = (int) (lam + 7.0 * sig);
121 while (pois.barF(j) > EPS)
122 j++;
123 return j - 1;
124 }
125
130 public ChiSquareNoncentralDist(double nu, double lambda) {
131 setParams(nu, lambda);
132 }
133
134 public double density(double x) {
135 return density(nu, lambda, x);
136 }
137
138 public double cdf(double x) {
139 if (x <= 0.0)
140 return 0.0;
141 if (x >= getXLIM(nu, lambda))
142 return 1.0;
143
144 if (nu >= PARLIM || lambda >= PARLIM)
145 return cdfPenev(false, nu, lambda, x);
146
147 return cdfExact(pois, jmax, nu, lambda, x);
148 }
149
150 public double barF(double x) {
151 if (x <= 0.0)
152 return 1.0;
153 if (x >= getXLIM(nu, lambda))
154 return 0.0;
155
156 if (nu >= PARLIM || lambda >= PARLIM)
157 return cdfPenev(true, nu, lambda, x);
158
159 return barFExact(pois, jmin, nu, lambda, x);
160 }
161
162 public double inverseF(double u) {
163 return inverseF(nu, lambda, u);
164 }
165
166 public double getMean() {
167 return ChiSquareNoncentralDist.getMean(nu, lambda);
168 }
169
170 public double getVariance() {
171 return ChiSquareNoncentralDist.getVariance(nu, lambda);
172 }
173
174 public double getStandardDeviation() {
175 return ChiSquareNoncentralDist.getStandardDeviation(nu, lambda);
176 }
177
184 public static double density(double nu, double lambda, double x) {
185 if (nu <= 0.0)
186 throw new IllegalArgumentException("nu <= 0");
187 if (lambda < 0.0)
188 throw new IllegalArgumentException("lambda < 0");
189 if (x <= 0.0)
190 return 0.0;
191 if (lambda == 0.0)
192 return GammaDist.density(nu / 2.0, 0.5, x);
193 if (x >= getXLIM(nu, lambda))
194 return 0.0;
195
196 final double a = 0.5 * nu - 1.0; // a = order of Bessel I_a(x)
197 double z = lambda * x;
198
199 // The series term is maximal for j = JMAX
200 final long JMAX = (long) (0.5 * z / (a + Math.sqrt(a * a + z)));
201
202 // calculer le terme pour j = JMAX --> termMax
203 double termMax = -0.5 * (x + lambda) + 0.25 * (nu - 2) * Math.log(x / lambda)
204 + 0.5 * (a + 2 * JMAX) * Math.log(0.25 * x * lambda) - Num.lnFactorial(JMAX) - Num.lnGamma(a + JMAX + 1)
205 - Num.LN2;
206
207 termMax = Math.exp(termMax);
208
209 // Sum non negligible terms on each side of the max term
210 double sum = termMax;
211 double term = termMax;
212 long j = JMAX;
213 double y = 4.0 / z;
214
215 while (j > 0 && term > sum * EPS) {
216 term *= y * j * (j + a);
217 sum += term;
218 j--;
219 }
220
221 term = termMax;
222 j = JMAX + 1;
223 y = z / 4.0;
224 while (term > sum * EPS) {
225 term *= y / (j * (j + a));
226 sum += term;
227 j++;
228 }
229 return sum;
230 }
231
248 public static double cdf(double nu, double lambda, double x) {
249 if (nu <= 0.0)
250 throw new IllegalArgumentException("nu <= 0");
251 if (lambda < 0.0)
252 throw new IllegalArgumentException("lambda < 0");
253 if (x <= 0.0)
254 return 0.0;
255 if (lambda == 0.0)
256 return GammaDist.cdf(nu / 2.0, 0.5, PREC, x);
257 if (x >= getXLIM(nu, lambda))
258 return 1.0;
259
260 if (nu >= PARLIM || lambda >= PARLIM)
261 return cdfPenev(false, nu, lambda, x);
262
263 PoissonDist pois = new PoissonDist(lambda / 2.0);
264 int j = calcJmax(pois);
265 return cdfExact(pois, j, nu, lambda, x);
266 }
267
268 private static double cdfExact(PoissonDist pois, int jmax, double nu, double lambda, double x) {
269 final int JMED = (int) (lambda / 2.0);
270
271 int j = jmax;
272 double chicdf = GammaDist.cdf(j + 0.5 * nu, 0.5, PREC, x);
273 if (chicdf >= 1.0)
274 return 1.0;
275
276 double chiterm = x / (j + 0.5 * nu) * GammaDist.density(j + 0.5 * nu, 0.5, x);
277 double prob = pois.prob(j);
278 double sum = prob * chicdf;
279
280 if (DETAIL) {
281 System.out.println(PrintfFormat.NEWLINE + "----------------------------------- cdf efficace");
282 System.out.print(" j chi term chi CDF");
283 System.out.println(" Poisson p somme" + PrintfFormat.NEWLINE);
284 System.out.println(
285 PrintfFormat.d(5, j) + " " + PrintfFormat.g(20, 10, chiterm) + " " + PrintfFormat.g(22, 15, chicdf)
286 + " " + PrintfFormat.g(20, 10, prob) + " " + PrintfFormat.g(22, 15, sum));
287 }
288
289 --j;
290 while (j >= 0 && (prob > sum * EPS || j >= JMED)) {
291 if (chicdf <= PROB_MIN) {
292 chicdf = GammaDist.cdf(j + nu / 2.0, 0.5, PREC, x);
293 chiterm = x / (j + 0.5 * nu) * GammaDist.density(j + nu / 2.0, 0.5, x);
294 } else {
295 chiterm *= (2 + 2 * j + nu) / x;
296 chicdf += chiterm;
297 }
298 if (chicdf >= 1.0 - EPS)
299 return sum + pois.cdf(j);
300 prob = pois.prob(j);
301 sum += prob * chicdf;
302 if (DETAIL) {
303 System.out.println(PrintfFormat.d(5, j) + " " + PrintfFormat.g(20, 10, chiterm) + " "
304 + PrintfFormat.g(22, 15, chicdf) + " " + PrintfFormat.g(20, 10, prob) + " "
305 + PrintfFormat.g(22, 15, sum));
306 }
307 --j;
308 }
309 return sum;
310 }
311
312 /*******************
313 * public static double cdf4 (double nu, double lambda, double x) { // Version
314 * naive et lente if (nu <= 0.0) throw new IllegalArgumentException ("nu <= 0");
315 * if (lambda < 0.0) throw new IllegalArgumentException ("lambda < 0"); if (x <=
316 * 0.0) return 0.0; if (lambda == 0.0) return GammaDist.cdf (nu/2.0, 0.5, PREC,
317 * x); PoissonDist pois = new PoissonDist (lambda / 2.0); int jmed = (int)
318 * (lambda / 2.0); int j = 0; double sum = 0.0; double prob = 1.0e100;
319 *
320 * if (DETAIL) { System.out.println (PrintfFormat.NEWLINE +
321 * "-------------------------------------- cdf naif"); System.out.print (" j
322 * Poisson p chi CDF"); System.out.println (" somme" + PrintfFormat.NEWLINE); }
323 *
324 * while (j <= jmed || prob > sum*EPS) { double chi2cdf = GammaDist.cdf (j +
325 * nu/2.0, 0.5, PREC, x); prob = pois.prob(j); sum += prob * chi2cdf; if
326 * (DETAIL) System.out.println (PrintfFormat.d (5, j) + " " + PrintfFormat.g
327 * (20, 10, prob) + " " + PrintfFormat.g (20, 10, chi2cdf) + " " +
328 * PrintfFormat.g (20, 10, sum)); ++j; } if (DETAIL) System.out.println ("");
329 * return sum; }
330 ********************/
331
332 private static double cdfPearson(boolean bar, double nu, double lambda, double x) {
333 // Pearson's central chi square approximation from @cite tPEA59a
334 // If bar is true, then returns u = barF; else returns u = cdf
335 double t2 = (nu + 2.0 * lambda);
336 double t3 = (nu + 3.0 * lambda);
337 double lib = t2 * t2 * t2 / (t3 * t3);
338 double z = x + lambda * lambda / t3;
339 if (bar)
340 return GammaDist.barF(lib / 2.0, 0.5, PREC, t2 * z / t3);
341 else
342 return GammaDist.cdf(lib / 2.0, 0.5, PREC, t2 * z / t3);
343 }
344
345 private static double penevH(double y) {
346 // The h function in Penev-Raykov paper
347 if (0.0 == y)
348 return 0.0;
349 if (1.0 == y)
350 return 0.5;
351 return ((1.0 - y) * Math.log1p(-y) + y - 0.5 * y * y) / (y * y);
352 }
353
354 private static double penevB(double mu, double s, double h1) {
355 // The B function in Penev-Raykov paper
356 final double f = 1.0 + 2.0 * mu * s;
357 final double g = 1.0 + 3.0 * mu * s;
358 final double c = (f - 2.0 * h1 - s * f) / (f - 2.0 * h1);
359 double B = -1.5 * (1.0 + 4.0 * mu * s) / (f * f);
360 B += 5.0 * g * g / (3.0 * f * f * f) + 2.0 * g / ((s - 1.0) * f * f);
361 B += 3.0 * c / ((s - 1.0) * (s - 1.0) * f);
362 B -= c * c * (1.0 + 2.0 * penevH(c)) / (2.0 * (s - 1.0) * (s - 1.0) * f);
363 return B;
364 }
365
366 private static double getPenevLim(double nu, double lam) {
367 if (lam >= 100000.0)
368 return 5.0;
369 if (nu >= 20000.0)
370 return 3.0;
371 return 2.0;
372 }
373
374 private static double cdfPenev(boolean bar, double nu, double lambda, double x) {
375 /*
376 * Penev-Raykov normal approximation from @cite tPEN00a If bar is true, then
377 * returns u = barF; else returns u = cdf. This approximation is very good
378 * except very near the mean where it is bad or give a NaN. Thus, near the mean,
379 * we use the Pearson approx.
380 */
381 double lim = getPenevLim(nu, lambda);
382 double mean = nu + lambda;
383 if (x >= mean - lim && x <= mean + lim)
384 return cdfPearson(bar, nu, lambda, x);
385
386 final double mu = lambda / nu;
387 final double s = (Math.sqrt(1.0 + 4.0 * x * mu / nu) - 1.0) / (2.0 * mu);
388 if (s == 1.0)
389 return 0.5;
390 final double h1 = penevH(1.0 - s);
391 final double B = penevB(mu, s, h1);
392 final double f = 1.0 + 2.0 * mu * s;
393 double z = nu * (s - 1.0) * (s - 1.0) * (0.5 / s + mu - h1 / s) - Math.log(1.0 / s - 2.0 * h1 / (s * f))
394 + 2.0 * B / nu;
395 // If z is a NaN, then z != z
396 if (z < 0.0 || z != z)
397 return cdfPearson(bar, nu, lambda, x);
398
399 z = Math.sqrt(z);
400 if (s < 1)
401 z = -z;
402 if (bar)
403 return NormalDist.barF01(z);
404 else
405 return NormalDist.cdf01(z);
406 }
407
414 public static double barF(double nu, double lambda, double x) {
415 if (nu <= 0.0)
416 throw new IllegalArgumentException("nu <= 0");
417 if (lambda < 0.0)
418 throw new IllegalArgumentException("lambda < 0");
419 if (x <= 0.0)
420 return 1.0;
421 if (lambda == 0.0)
422 return GammaDist.barF(nu / 2.0, 0.5, PREC, x);
423 if (x >= getXLIM(nu, lambda))
424 return 0.0;
425
426 if (nu >= PARLIM || lambda >= PARLIM)
427 return cdfPenev(true, nu, lambda, x);
428
429 PoissonDist pois = new PoissonDist(lambda / 2.0);
430 int j = calcJmin(pois);
431 return barFExact(pois, j, nu, lambda, x);
432 }
433
434 /***********************
435 * public static double bar4 (double nu, double lambda, double x) { // Version
436 * naive et lente if (nu <= 0.0) throw new IllegalArgumentException ("nu <= 0");
437 * if (lambda < 0.0) throw new IllegalArgumentException ("lambda < 0"); if (x <=
438 * 0.0) return 1.0; if (lambda == 0.0) return GammaDist.barF (nu/2.0, 0.5, PREC,
439 * x); PoissonDist pois = new PoissonDist (lambda / 2.0); int jmed = (int)
440 * (lambda / 2.0); int j = 0; double sum = 0.0; double pdfterm = 0.0; double
441 * prob = 1.0e100;
442 *
443 * if (DETAIL) { System.out.println (PrintfFormat.NEWLINE +
444 * "-------------------------------------- barF naif"); System.out.print (" j
445 * Poisson p chi term"); System.out.println (" chi barF somme" +
446 * PrintfFormat.NEWLINE); }
447 *
448 * while (j <= jmed || prob > sum*EPS) { double chi2cdf = GammaDist.barF (j +
449 * nu/2.0, 0.5, PREC, x); prob = pois.prob(j); sum += prob * chi2cdf; if
450 * (DETAIL) System.out.println (PrintfFormat.d (5, j) + " " + PrintfFormat.g
451 * (20, 12, prob) + " " + PrintfFormat.g (20, 12, chi2cdf - pdfterm) + " " +
452 * PrintfFormat.g (20, 12, chi2cdf) + " " + PrintfFormat.g (20, 12, sum)); ++j;
453 * pdfterm = chi2cdf; } if (DETAIL) System.out.println (""); return sum; }
454 ********************/
455
456 private static double barFExact(PoissonDist pois, int jmin, double nu, double lambda, double x) {
457 final int JMED = (int) (lambda / 2.0);
458
459 int j = jmin;
460 double chibar = GammaDist.barF(j + 0.5 * nu, 0.5, PREC, x);
461 if (chibar >= 1.0)
462 return 1.0;
463
464 double prob = pois.prob(j);
465 double sum = prob * chibar;
466 double chiterm = 2.0 * GammaDist.density(j + 0.5 * nu, 0.5, x);
467
468 if (DETAIL) {
469 System.out.println(PrintfFormat.NEWLINE + "--------------------------------- barF efficace");
470 System.out.print(" j Poisson p chi term");
471 System.out.println(" chi barF somme" + PrintfFormat.NEWLINE);
472 System.out.println(
473 PrintfFormat.d(5, j) + " " + PrintfFormat.g(20, 12, prob) + " " + PrintfFormat.g(20, 12, chiterm)
474 + " " + PrintfFormat.g(22, 12, chibar) + " " + PrintfFormat.g(22, 12, sum));
475 }
476
477 ++j;
478 while (prob > sum * EPS || j <= JMED) {
479 if (chibar <= PROB_MIN) {
480 chibar = GammaDist.barF(j + nu / 2.0, 0.5, PREC, x);
481 chiterm = 2.0 * GammaDist.density(j + 0.5 * nu, 0.5, x);
482 } else {
483 chiterm *= x / (2 * j - 2 + nu);
484 chibar += chiterm;
485 }
486 if (chibar >= 1.0)
487 return sum + pois.barF(j);
488 prob = pois.prob(j);
489 sum += prob * chibar;
490
491 if (DETAIL) {
492 System.out.println(
493 PrintfFormat.d(5, j) + " " + PrintfFormat.g(20, 12, prob) + " " + PrintfFormat.g(20, 12, chiterm)
494 + " " + PrintfFormat.g(22, 12, chibar) + " " + PrintfFormat.g(22, 12, sum));
495 }
496 ++j;
497 }
498 return sum;
499 }
500
506 public static double inverseF(double nu, double lambda, double u) {
507 if (nu <= 0.0)
508 throw new IllegalArgumentException("nu <= 0");
509 if (lambda < 0.0)
510 throw new IllegalArgumentException("lambda < 0");
511 if (u < 0.0 || u > 1.0)
512 throw new IllegalArgumentException("u not in [0,1]");
513 if (u >= 1.0)
514 return Double.POSITIVE_INFINITY;
515 if (u <= 0.0)
516 return 0.0;
517 if (lambda == 0.0)
518 return GammaDist.inverseF(nu / 2.0, 0.5, PREC, u);
519
520 double x = inverse9(nu, lambda, u);
521 double v = cdf(nu, lambda, x);
522
523 Function f = new Function(nu, lambda, u);
524 if (v >= u)
525 x = RootFinder.brentDekker(0.0, x, f, 1.0e-10);
526 else {
527 v = getXLIM(nu, lambda);
528 x = RootFinder.brentDekker(x, v, f, 1.0e-10);
529 }
530 return x;
531 }
532
533 private static double inverse9(double nu, double lambda, double u) {
534 // Une approximation normale @cite tKRI06a
535 double z = NormalDist.inverseF01(u);
536 double a = nu + lambda;
537 double b = (nu + 2.0 * lambda) / (a * a);
538 double t = z * Math.sqrt(2.0 * b / 9.0) - 2.0 * b / 9.0 + 1.0;
539 return a * t * t * t;
540 }
541
549 public static double getMean(double nu, double lambda) {
550 if (nu <= 0.0)
551 throw new IllegalArgumentException("nu <= 0");
552 if (lambda <= 0.0)
553 throw new IllegalArgumentException("lambda <= 0");
554 return nu + lambda;
555 }
556
564 public static double getVariance(double nu, double lambda) {
565 if (nu <= 0.)
566 throw new IllegalArgumentException("nu <= 0");
567 if (lambda <= 0.0)
568 throw new IllegalArgumentException("lambda <= 0");
569 return 2.0 * (nu + 2.0 * lambda);
570 }
571
579 public static double getStandardDeviation(double nu, double lambda) {
580 return Math.sqrt(getVariance(nu, lambda));
581 }
582
586 public double getNu() {
587 return nu;
588 }
589
593 public double getLambda() {
594 return lambda;
595 }
596
601 public void setParams(double nu, double lambda) {
602 if (nu <= 0.0)
603 throw new IllegalArgumentException("nu <= 0");
604 if (lambda < 0.0)
605 throw new IllegalArgumentException("lambda < 0");
606 if (lambda == 0.0)
607 throw new IllegalArgumentException("lambda = 0");
608 this.nu = nu;
609 this.lambda = lambda;
610 supportA = 0.0;
611 if (nu >= PARLIM || lambda >= PARLIM)
612 return;
613 pois = new PoissonDist(lambda / 2.0);
614 jmax = calcJmax(pois);
615 jmin = calcJmin(pois);
616 }
617
621 public double[] getParams() {
622 double[] retour = { nu, lambda };
623 return retour;
624 }
625
629 public String toString() {
630 return getClass().getSimpleName() + ": nu = " + nu + ", lambda = " + lambda;
631 }
632
633}
double getNu()
Returns the parameter of this object.
double density(double x)
Returns , the density evaluated at .
double[] getParams()
Returns a table containing the parameters of the current distribution.
static double density(double nu, double lambda, double x)
Computes the density function ( nc-fchi2 ) for a noncentral chi-square distribution with nu degrees ...
double inverseF(double u)
Returns the inverse distribution function .
static double getStandardDeviation(double nu, double lambda)
Computes and returns the standard deviation of the noncentral chi-square distribution with parameters...
String toString()
Returns a String containing information about the current distribution.
double cdf(double x)
Returns the distribution function .
static double inverseF(double nu, double lambda, double u)
Computes the inverse of the noncentral chi-square distribution with.
void setParams(double nu, double lambda)
Sets the parameters nu and lambda of this object.
static double getMean(double nu, double lambda)
Computes and returns the mean of the noncentral chi-square distribution with parameters nu and lam...
ChiSquareNoncentralDist(double nu, double lambda)
Constructs a noncentral chi-square distribution with nu degrees of freedom and noncentrality paramet...
static double getVariance(double nu, double lambda)
Computes and returns the variance of the noncentral chi-square distribution with parameters nu and ...
static double barF(double nu, double lambda, double x)
Computes the complementary noncentral chi-square distribution function with nu degrees of freedom an...
static double cdf(double nu, double lambda, double x)
Computes the noncentral chi-square distribution function ( nc-cdfchi2 ) with nu degrees of freedom a...
double getLambda()
Returns the parameter of this object.
double getStandardDeviation()
Returns the standard deviation.
double barF(double x)
Returns the complementary distribution function.
Classes implementing continuous distributions should inherit from this base class.
Extends the class ContinuousDistribution for the gamma distribution tjoh95a  (page 337) with shape pa...
double inverseF(double u)
Returns the inverse distribution function .
double cdf(double x)
Returns the distribution function .
double density(double x)
Returns , the density evaluated at .
double barF(double x)
Returns the complementary distribution function.
Extends the class ContinuousDistribution for the normal distribution (e.g., tjoh95a  (page 80)).
static double inverseF01(double u)
Same as inverseF(0, 1, u).
Extends the class DiscreteDistributionInt for the Poisson distribution slaw00a  (page 325) with mean.
double cdf(int x)
Returns the distribution function evaluated at (see ( FDistDisc )).
double barF(int x)
Returns , the complementary distribution function.
double prob(int x)
Returns , the probability of .
This class provides various constants and methods to compute numerical quantities such as factorials,...
Definition Num.java:35
static double lnGamma(double x)
Returns the natural logarithm of the gamma function evaluated at x.
Definition Num.java:417
static double lnFactorial(int n)
Returns the value of , the natural logarithm of factorial .
Definition Num.java:313
static final double TEN_NEG_POW[]
Contains the precomputed negative powers of 10.
Definition Num.java:186
static final double LN2
The values of .
Definition Num.java:148
This class acts like a StringBuffer which defines new types of append methods.
static String d(long x)
Same as d(0, 1, x).
static final String NEWLINE
End-of-line symbol or line separator.
static String g(double x)
Same as g(0, 6, x).
This class provides numerical methods to solve non-linear equations.
static double brentDekker(double a, double b, MathFunction f, double tol)
Computes a root of the function in f using the Brent-Dekker method.
This interface should be implemented by classes which represent univariate mathematical functions.