SSJ API Documentation
Stochastic Simulation in Java
Loading...
Searching...
No Matches
KolmogorovSmirnovDistQuick.java
1/*
2 * Class: KolmogorovSmirnovDistQuick
3 * Description: Kolmogorov-Smirnov 2-sided 1-sample 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 January 2010
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
40
41 /*
42 * For n <= NEXACT, we use exact algorithms: the Durbin matrix and the Pomeranz
43 * algorithms. For n > NEXACT, we use asymptotic methods except for x close to 0
44 * where we still use the method of Durbin for n <= NKOLMO. For n > NKOLMO, we
45 * use asymptotic methods only and so the precision is less for x close to 0. We
46 * could increase the limit NKOLMO to 10^6 to get better precision for x close
47 * to 0, but at the price of a slower speed.
48 */
49 private static final int NKOLMO = 100000;
50
51 private static class Function implements MathFunction {
52 protected int n;
53 protected double u;
54
55 public Function(int n, double u) {
56 this.n = n;
57 this.u = u;
58 }
59
60 public double evaluate(double x) {
61 return u - cdf(n, x);
62 }
63 }
64
69 super(n);
70 }
71
72 public double density(double x) {
73 return density(n, x);
74 }
75
76 public double cdf(double x) {
77 return cdf(n, x);
78 }
79
80 public double barF(double x) {
81 return barF(n, x);
82 }
83
84 public double inverseF(double u) {
85 return inverseF(n, u);
86 }
87
92 public static double density(int n, double x) {
93
94 double Res = densConnue(n, x);
95 if (Res != -1.0)
96 return Res;
97
98 final double EPS = 1.0 / Num.TWOEXP[6];
99 Res = (cdf(n, x + EPS) - cdf(n, x - EPS)) / (2.0 * EPS);
100 if (Res <= 0.0)
101 return 0.0;
102 return Res;
103 }
104
105 private static double Pelz(int n, double x) {
106 /*
107 * Approximating the Lower Tail-Areas of the Kolmogorov-Smirnov One-Sample
108 * Statistic, Wolfgang Pelz and I. J. Good, Journal of the Royal Statistical
109 * Society, Series B. Vol. 38, No. 2 (1976), pp. 152-156
110 */
111
112 final int JMAX = 20;
113 final double EPS = 1.0e-10;
114 final double RACN = Math.sqrt((double) n);
115 final double z = RACN * x;
116 final double z2 = z * z;
117 final double z4 = z2 * z2;
118 final double z6 = z4 * z2;
119 final double C2PI = 2.506628274631001; // sqrt(2*Pi)
120 final double DPI2 = 1.2533141373155001; // sqrt(Pi/2)
121 final double PI2 = Math.PI * Math.PI;
122 final double PI4 = PI2 * PI2;
123 final double w = PI2 / (2.0 * z * z);
124 double ti, term, tom;
125 double sum;
126 int j;
127
128 term = 1;
129 j = 0;
130 sum = 0;
131 while (j <= JMAX && term > EPS * sum) {
132 ti = j + 0.5;
133 term = Math.exp(-ti * ti * w);
134 sum += term;
135 j++;
136 }
137 sum *= C2PI / z;
138
139 term = 1;
140 tom = 0;
141 j = 0;
142 while (j <= JMAX && Math.abs(term) > EPS * Math.abs(tom)) {
143 ti = j + 0.5;
144 term = (PI2 * ti * ti - z2) * Math.exp(-ti * ti * w);
145 tom += term;
146 j++;
147 }
148 sum += tom * DPI2 / (RACN * 3.0 * z4);
149
150 term = 1;
151 tom = 0;
152 j = 0;
153 while (j <= JMAX && Math.abs(term) > EPS * Math.abs(tom)) {
154 ti = j + 0.5;
155 term = 6 * z6 + 2 * z4 + PI2 * (2 * z4 - 5 * z2) * ti * ti + PI4 * (1 - 2 * z2) * ti * ti * ti * ti;
156 term *= Math.exp(-ti * ti * w);
157 tom += term;
158 j++;
159 }
160 sum += tom * DPI2 / (n * 36.0 * z * z6);
161
162 term = 1;
163 tom = 0;
164 j = 1;
165 while (j <= JMAX && term > EPS * tom) {
166 ti = j;
167 term = PI2 * ti * ti * Math.exp(-ti * ti * w);
168 tom += term;
169 j++;
170 }
171 sum -= tom * DPI2 / (n * 18.0 * z * z2);
172
173 term = 1;
174 tom = 0;
175 j = 0;
176 while (j <= JMAX && Math.abs(term) > EPS * Math.abs(tom)) {
177 ti = j + 0.5;
178 ti = ti * ti;
179 term = -30 * z6 - 90 * z6 * z2 + PI2 * (135 * z4 - 96 * z6) * ti + PI4 * (212 * z4 - 60 * z2) * ti * ti
180 + PI2 * PI4 * ti * ti * ti * (5 - 30 * z2);
181 term *= Math.exp(-ti * w);
182 tom += term;
183 j++;
184 }
185 sum += tom * DPI2 / (RACN * n * 3240.0 * z4 * z6);
186
187 term = 1;
188 tom = 0;
189 j = 1;
190 while (j <= JMAX && Math.abs(term) > EPS * Math.abs(tom)) {
191 ti = j * j;
192 term = (3 * PI2 * ti * z2 - PI4 * ti * ti) * Math.exp(-ti * w);
193 tom += term;
194 j++;
195 }
196 sum += tom * DPI2 / (RACN * n * 108.0 * z6);
197
198 return sum;
199 }
200
201//========================================================================
202
203 private static void CalcFloorCeil(int n, // sample size
204 double t, // = nx
205 double[] A, // A_i
206 double[] Atflo, // floor (A_i - t)
207 double[] Atcei // ceiling (A_i + t)
208 ) {
209 // Precompute A_i, floors, and ceilings for limits of sums in the
210 // Pomeranz algorithm
211 int i;
212 int ell = (int) t; // floor (t)
213 double z = t - ell; // t - floor (t)
214 double w = Math.ceil(t) - t;
215
216 if (z > 0.5) {
217 for (i = 2; i <= 2 * n + 2; i += 2)
218 Atflo[i] = i / 2 - 2 - ell;
219 for (i = 1; i <= 2 * n + 2; i += 2)
220 Atflo[i] = i / 2 - 1 - ell;
221
222 for (i = 2; i <= 2 * n + 2; i += 2)
223 Atcei[i] = i / 2 + ell;
224 for (i = 1; i <= 2 * n + 2; i += 2)
225 Atcei[i] = i / 2 + 1 + ell;
226
227 } else if (z > 0.0) {
228 for (i = 1; i <= 2 * n + 2; i++)
229 Atflo[i] = i / 2 - 1 - ell;
230
231 for (i = 2; i <= 2 * n + 2; i++)
232 Atcei[i] = i / 2 + ell;
233 Atcei[1] = 1 + ell;
234
235 } else { // z == 0
236 for (i = 2; i <= 2 * n + 2; i += 2)
237 Atflo[i] = i / 2 - 1 - ell;
238 for (i = 1; i <= 2 * n + 2; i += 2)
239 Atflo[i] = i / 2 - ell;
240
241 for (i = 2; i <= 2 * n + 2; i += 2)
242 Atcei[i] = i / 2 - 1 + ell;
243 for (i = 1; i <= 2 * n + 2; i += 2)
244 Atcei[i] = i / 2 + ell;
245 }
246
247 if (w < z)
248 z = w;
249 A[0] = A[1] = 0;
250 A[2] = z;
251 A[3] = 1 - A[2];
252 for (i = 4; i <= 2 * n + 1; i++)
253 A[i] = A[i - 2] + 1;
254 A[2 * n + 2] = n;
255 }
256
257 // ========================================================================
258
259 private static double Pomeranz(int n, double x) {
260 // The Pomeranz algorithm to compute the KS distribution
261 final double EPS = 1.0e-15;
262 final int ENO = 350;
263 final double RENO = Math.scalb(1.0, ENO); // for renormalization of V
264 int coreno; // counter: how many renormalizations
265 final double t = n * x;
266 double w, sum, minsum;
267 int i, j, k, s;
268 int r1, r2; // Indices i and i-1 for V[i][]
269 int jlow, jup, klow, kup, kup0;
270 double[] A = new double[2 * n + 3];
271 double[] Atflo = new double[2 * n + 3];
272 double[] Atcei = new double[2 * n + 3];
273 double[][] V = new double[2][n + 2];
274 double[][] H = new double[4][n + 2]; // = pow(w, j) / Factorial(j)
275
276 CalcFloorCeil(n, t, A, Atflo, Atcei);
277
278 for (j = 1; j <= n + 1; j++)
279 V[0][j] = 0;
280 for (j = 2; j <= n + 1; j++)
281 V[1][j] = 0;
282 V[1][1] = RENO;
283 coreno = 1;
284
285 // Precompute H[][] = (A[j] - A[j-1]^k / k!
286 H[0][0] = 1;
287 w = 2.0 * A[2] / n;
288 for (j = 1; j <= n + 1; j++)
289 H[0][j] = w * H[0][j - 1] / j;
290
291 H[1][0] = 1;
292 w = (1.0 - 2.0 * A[2]) / n;
293 for (j = 1; j <= n + 1; j++)
294 H[1][j] = w * H[1][j - 1] / j;
295
296 H[2][0] = 1;
297 w = A[2] / n;
298 for (j = 1; j <= n + 1; j++)
299 H[2][j] = w * H[2][j - 1] / j;
300
301 H[3][0] = 1;
302 for (j = 1; j <= n + 1; j++)
303 H[3][j] = 0;
304
305 r1 = 0;
306 r2 = 1;
307 for (i = 2; i <= 2 * n + 2; i++) {
308 jlow = (int) (2 + Atflo[i]);
309 if (jlow < 1)
310 jlow = 1;
311 jup = (int) (Atcei[i]);
312 if (jup > n + 1)
313 jup = n + 1;
314
315 klow = (int) (2 + Atflo[i - 1]);
316 if (klow < 1)
317 klow = 1;
318 kup0 = (int) (Atcei[i - 1]);
319
320 // Find to which case it corresponds
321 w = (A[i] - A[i - 1]) / n;
322 s = -1;
323 for (j = 0; j < 4; j++) {
324 if (Math.abs(w - H[j][1]) <= EPS) {
325 s = j;
326 break;
327 }
328 }
329
330 minsum = RENO;
331 r1 = (r1 + 1) & 1; // i - 1
332 r2 = (r2 + 1) & 1; // i
333
334 for (j = jlow; j <= jup; j++) {
335 kup = kup0;
336 if (kup > j)
337 kup = j;
338 sum = 0;
339 for (k = kup; k >= klow; k--)
340 sum += V[r1][k] * H[s][j - k];
341 V[r2][j] = sum;
342 if (sum < minsum)
343 minsum = sum;
344 }
345
346 if (minsum < 1.0e-280) {
347 // V is too small: renormalize to avoid underflow of probabilities
348 for (j = jlow; j <= jup; j++)
349 V[r2][j] *= RENO;
350 coreno++; // keep track of log of RENO
351 }
352 }
353
354 sum = V[r2][n + 1];
355 w = Num.lnFactorial(n) - coreno * ENO * Num.LN2 + Math.log(sum);
356 if (w >= 0.)
357 return 1.;
358 return Math.exp(w);
359 }
360 // ========================================================================
361
379 public static double cdf(int n, double x) {
380 double u = cdfConnu(n, x);
381 if (u >= 0.0)
382 return u;
383
384 final double w = n * x * x;
385 if (n <= NEXACT) {
386 if (w < 0.754693)
387 return DurbinMatrix(n, x);
388 if (w < 4.0)
389 return Pomeranz(n, x);
390 return 1.0 - barF(n, x);
391 }
392
393 if ((w * x * n <= 7.0) && (n <= NKOLMO))
394 return DurbinMatrix(n, x);
395
396 return Pelz(n, x);
397 }
398
412 public static double barF(int n, double x) {
413 double v = barFConnu(n, x);
414 if (v >= 0.0)
415 return v;
416
417 final double w = n * x * x;
418 if (n <= NEXACT) {
419 if (w < 4.0)
420 return 1.0 - cdf(n, x);
421 else
422 return 2.0 * KolmogorovSmirnovPlusDist.KSPlusbarUpper(n, x);
423 }
424
425 if (w >= 2.65)
426 return 2.0 * KolmogorovSmirnovPlusDist.KSPlusbarUpper(n, x);
427
428 return 1.0 - cdf(n, x);
429 }
430
436 public static double inverseF(int n, double u) {
437 double Res = inverseConnue(n, u);
438 if (Res != -1.0)
439 return Res;
440 Function f = new Function(n, u);
441 return RootFinder.brentDekker(0.5 / n, 1.0, f, 1e-5);
442 }
443
444}
KolmogorovSmirnovDistQuick(int n)
Constructs a Kolmogorov–Smirnov distribution with parameter .
double cdf(double x)
Returns the distribution function .
static double inverseF(int n, double u)
Computes the inverse of the distribution.
double barF(double x)
Returns the complementary distribution function.
static double cdf(int n, double x)
Computes the Kolmogorov–Smirnov distribution function with parameter , using the program described ...
double density(double x)
Returns , the density evaluated at .
static double density(int n, double x)
Computes the density for the Kolmogorov–Smirnov distribution with parameter .
static double barF(int n, double x)
Computes the complementary Kolmogorov–Smirnov distribution.
double inverseF(double u)
Returns the inverse distribution function .
KolmogorovSmirnovDist(int n)
Constructs a Kolmogorov–Smirnov distribution with parameter.
Extends the class ContinuousDistribution for the Kolmogorov–Smirnov+ distribution (see tdar60a,...
This class provides various constants and methods to compute numerical quantities such as factorials,...
Definition Num.java:35
static final double TWOEXP[]
Contains the precomputed positive powers of 2.
Definition Num.java:170
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.