View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.statistics.distribution;
18  
19  /**
20   * Utility class used by various distributions to accurately compute their
21   * respective probability mass functions. The implementation for this class is
22   * based on the Catherine Loader's
23   * <a href="http://www.herine.net/stat/software/dbinom.html">dbinom</a> routines.
24   *
25   * This class is not intended to be called directly.
26   */
27  final class SaddlePointExpansionUtils {
28      /** 2 &pi;. */
29      private static final double TWO_PI = 2 * Math.PI;
30      /** 1/10. */
31      private static final double ONE_TENTH = 0.1;
32      /** The threshold value for switching the method to compute th Stirling error. */
33      private static final int STIRLING_ERROR_THRESHOLD = 15;
34  
35      /** Exact Stirling expansion error for certain values. */
36      private static final double[] EXACT_STIRLING_ERRORS = {
37          0.0, /* 0.0 */
38          0.1534264097200273452913848, /* 0.5 */
39          0.0810614667953272582196702, /* 1.0 */
40          0.0548141210519176538961390, /* 1.5 */
41          0.0413406959554092940938221, /* 2.0 */
42          0.03316287351993628748511048, /* 2.5 */
43          0.02767792568499833914878929, /* 3.0 */
44          0.02374616365629749597132920, /* 3.5 */
45          0.02079067210376509311152277, /* 4.0 */
46          0.01848845053267318523077934, /* 4.5 */
47          0.01664469118982119216319487, /* 5.0 */
48          0.01513497322191737887351255, /* 5.5 */
49          0.01387612882307074799874573, /* 6.0 */
50          0.01281046524292022692424986, /* 6.5 */
51          0.01189670994589177009505572, /* 7.0 */
52          0.01110455975820691732662991, /* 7.5 */
53          0.010411265261972096497478567, /* 8.0 */
54          0.009799416126158803298389475, /* 8.5 */
55          0.009255462182712732917728637, /* 9.0 */
56          0.008768700134139385462952823, /* 9.5 */
57          0.008330563433362871256469318, /* 10.0 */
58          0.007934114564314020547248100, /* 10.5 */
59          0.007573675487951840794972024, /* 11.0 */
60          0.007244554301320383179543912, /* 11.5 */
61          0.006942840107209529865664152, /* 12.0 */
62          0.006665247032707682442354394, /* 12.5 */
63          0.006408994188004207068439631, /* 13.0 */
64          0.006171712263039457647532867, /* 13.5 */
65          0.005951370112758847735624416, /* 14.0 */
66          0.005746216513010115682023589, /* 14.5 */
67          0.005554733551962801371038690 /* 15.0 */
68      };
69  
70      /**
71       * Forbid construction.
72       */
73      private SaddlePointExpansionUtils() {}
74  
75      /**
76       * Compute the error of Stirling's series at the given value.
77       * <p>
78       * References:
79       * <ol>
80       * <li>Eric W. Weisstein. "Stirling's Series." From MathWorld--A Wolfram Web
81       * Resource. <a target="_blank"
82       * href="https://mathworld.wolfram.com/StirlingsSeries.html">
83       * https://mathworld.wolfram.com/StirlingsSeries.html</a></li>
84       * </ol>
85       *
86       * <p>Note: This function has been modified for integer {@code z}.</p>
87       *
88       * @param z Value at which the function is evaluated.
89       * @return the Stirling's series error.
90       */
91      static double getStirlingError(int z) {
92          if (z <= STIRLING_ERROR_THRESHOLD) {
93              return EXACT_STIRLING_ERRORS[2 * z];
94          }
95          final double z2 = (double) z * z;
96          return (0.083333333333333333333 -
97                         (0.00277777777777777777778 -
98                                 (0.00079365079365079365079365 -
99                                         (0.000595238095238095238095238 -
100                                                0.0008417508417508417508417508 /
101                                                z2) / z2) / z2) / z2) / z;
102     }
103 
104     /**
105      * A part of the deviance portion of the saddle point approximation.
106      * <p>
107      * References:
108      * <ol>
109      * <li>Catherine Loader (2000). "Fast and Accurate Computation of Binomial
110      * Probabilities.". <a target="_blank"
111      * href="http://www.herine.net/stat/papers/dbinom.pdf">
112      * http://www.herine.net/stat/papers/dbinom.pdf</a></li>
113      * </ol>
114      *
115      * <p>Note: This function has been modified for integer {@code x}.</p>
116      *
117      * @param x Value at which the function is evaluated.
118      * @param mu Average.
119      * @return a part of the deviance.
120      */
121     static double getDeviancePart(int x, double mu) {
122         if (Math.abs(x - mu) < 0.1 * (x + mu)) {
123             final double d = x - mu;
124             double v = d / (x + mu);
125             double s1 = v * d;
126             double s = Double.NaN;
127             double ej = 2.0 * x * v;
128             v *= v;
129             int j = 1;
130             while (s1 != s) {
131                 s = s1;
132                 ej *= v;
133                 s1 = s + ej / ((j * 2) + 1);
134                 ++j;
135             }
136             return s1;
137         } else if (x == 0) {
138             return mu;
139         }
140         return x * Math.log(x / mu) + mu - x;
141     }
142 
143     /**
144      * Compute the logarithm of the PMF for a binomial distribution
145      * using the saddle point expansion.
146      *
147      * @param x Value at which the probability is evaluated.
148      * @param n Number of trials.
149      * @param p Probability of success.
150      * @param q Probability of failure (1 - p).
151      * @return log(p(x)).
152      */
153     static double logBinomialProbability(int x, int n, double p, double q) {
154         if (x == 0) {
155             if (p < ONE_TENTH) {
156                 // Subtract from 0 avoids returning -0.0 for p=0.0
157                 return 0.0 - getDeviancePart(n, n * q) - n * p;
158             } else if (n == 0) {
159                 return 0;
160             }
161             return n * Math.log(q);
162         } else if (x == n) {
163             if (q < ONE_TENTH) {
164                 // Subtract from 0 avoids returning -0.0 for p=1.0
165                 return 0.0 - getDeviancePart(n, n * p) - n * q;
166             }
167             return n * Math.log(p);
168         }
169         final int nMx = n - x;
170         final double ret = getStirlingError(n) - getStirlingError(x) -
171                            getStirlingError(nMx) - getDeviancePart(x, n * p) -
172                            getDeviancePart(nMx, n * q);
173         final double f = (TWO_PI * x * nMx) / n;
174         return -0.5 * Math.log(f) + ret;
175     }
176 }