Home | History | Annotate | Download | only in distribution
      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.math.distribution;
     18 
     19 import org.apache.commons.math.special.Gamma;
     20 import org.apache.commons.math.util.FastMath;
     21 import org.apache.commons.math.util.MathUtils;
     22 
     23 /**
     24  * <p>
     25  * Utility class used by various distributions to accurately compute their
     26  * respective probability mass functions. The implementation for this class is
     27  * based on the Catherine Loader's <a target="_blank"
     28  * href="http://www.herine.net/stat/software/dbinom.html">dbinom</a> routines.
     29  * </p>
     30  * <p>
     31  * This class is not intended to be called directly.
     32  * </p>
     33  * <p>
     34  * References:
     35  * <ol>
     36  * <li>Catherine Loader (2000). "Fast and Accurate Computation of Binomial
     37  * Probabilities.". <a target="_blank"
     38  * href="http://www.herine.net/stat/papers/dbinom.pdf">
     39  * http://www.herine.net/stat/papers/dbinom.pdf</a></li>
     40  * </ol>
     41  * </p>
     42  *
     43  * @since 2.1
     44  * @version $Revision$ $Date$
     45  */
     46 final class SaddlePointExpansion {
     47 
     48     /** 1/2 * log(2 &#960;). */
     49     private static final double HALF_LOG_2_PI = 0.5 * FastMath.log(MathUtils.TWO_PI);
     50 
     51     /** exact Stirling expansion error for certain values. */
     52     private static final double[] EXACT_STIRLING_ERRORS = { 0.0, /* 0.0 */
     53     0.1534264097200273452913848, /* 0.5 */
     54     0.0810614667953272582196702, /* 1.0 */
     55     0.0548141210519176538961390, /* 1.5 */
     56     0.0413406959554092940938221, /* 2.0 */
     57     0.03316287351993628748511048, /* 2.5 */
     58     0.02767792568499833914878929, /* 3.0 */
     59     0.02374616365629749597132920, /* 3.5 */
     60     0.02079067210376509311152277, /* 4.0 */
     61     0.01848845053267318523077934, /* 4.5 */
     62     0.01664469118982119216319487, /* 5.0 */
     63     0.01513497322191737887351255, /* 5.5 */
     64     0.01387612882307074799874573, /* 6.0 */
     65     0.01281046524292022692424986, /* 6.5 */
     66     0.01189670994589177009505572, /* 7.0 */
     67     0.01110455975820691732662991, /* 7.5 */
     68     0.010411265261972096497478567, /* 8.0 */
     69     0.009799416126158803298389475, /* 8.5 */
     70     0.009255462182712732917728637, /* 9.0 */
     71     0.008768700134139385462952823, /* 9.5 */
     72     0.008330563433362871256469318, /* 10.0 */
     73     0.007934114564314020547248100, /* 10.5 */
     74     0.007573675487951840794972024, /* 11.0 */
     75     0.007244554301320383179543912, /* 11.5 */
     76     0.006942840107209529865664152, /* 12.0 */
     77     0.006665247032707682442354394, /* 12.5 */
     78     0.006408994188004207068439631, /* 13.0 */
     79     0.006171712263039457647532867, /* 13.5 */
     80     0.005951370112758847735624416, /* 14.0 */
     81     0.005746216513010115682023589, /* 14.5 */
     82     0.005554733551962801371038690 /* 15.0 */
     83     };
     84 
     85     /**
     86      * Default constructor.
     87      */
     88     private SaddlePointExpansion() {
     89         super();
     90     }
     91 
     92     /**
     93      * Compute the error of Stirling's series at the given value.
     94      * <p>
     95      * References:
     96      * <ol>
     97      * <li>Eric W. Weisstein. "Stirling's Series." From MathWorld--A Wolfram Web
     98      * Resource. <a target="_blank"
     99      * href="http://mathworld.wolfram.com/StirlingsSeries.html">
    100      * http://mathworld.wolfram.com/StirlingsSeries.html</a></li>
    101      * </ol>
    102      * </p>
    103      *
    104      * @param z the value.
    105      * @return the Striling's series error.
    106      */
    107     static double getStirlingError(double z) {
    108         double ret;
    109         if (z < 15.0) {
    110             double z2 = 2.0 * z;
    111             if (FastMath.floor(z2) == z2) {
    112                 ret = EXACT_STIRLING_ERRORS[(int) z2];
    113             } else {
    114                 ret = Gamma.logGamma(z + 1.0) - (z + 0.5) * FastMath.log(z) +
    115                       z - HALF_LOG_2_PI;
    116             }
    117         } else {
    118             double z2 = z * z;
    119             ret = (0.083333333333333333333 -
    120                     (0.00277777777777777777778 -
    121                             (0.00079365079365079365079365 -
    122                                     (0.000595238095238095238095238 -
    123                                             0.0008417508417508417508417508 /
    124                                             z2) / z2) / z2) / z2) / z;
    125         }
    126         return ret;
    127     }
    128 
    129     /**
    130      * A part of the deviance portion of the saddle point approximation.
    131      * <p>
    132      * References:
    133      * <ol>
    134      * <li>Catherine Loader (2000). "Fast and Accurate Computation of Binomial
    135      * Probabilities.". <a target="_blank"
    136      * href="http://www.herine.net/stat/papers/dbinom.pdf">
    137      * http://www.herine.net/stat/papers/dbinom.pdf</a></li>
    138      * </ol>
    139      * </p>
    140      *
    141      * @param x the x value.
    142      * @param mu the average.
    143      * @return a part of the deviance.
    144      */
    145     static double getDeviancePart(double x, double mu) {
    146         double ret;
    147         if (FastMath.abs(x - mu) < 0.1 * (x + mu)) {
    148             double d = x - mu;
    149             double v = d / (x + mu);
    150             double s1 = v * d;
    151             double s = Double.NaN;
    152             double ej = 2.0 * x * v;
    153             v = v * v;
    154             int j = 1;
    155             while (s1 != s) {
    156                 s = s1;
    157                 ej *= v;
    158                 s1 = s + ej / ((j * 2) + 1);
    159                 ++j;
    160             }
    161             ret = s1;
    162         } else {
    163             ret = x * FastMath.log(x / mu) + mu - x;
    164         }
    165         return ret;
    166     }
    167 
    168     /**
    169      * Compute the PMF for a binomial distribution using the saddle point
    170      * expansion.
    171      *
    172      * @param x the value at which the probability is evaluated.
    173      * @param n the number of trials.
    174      * @param p the probability of success.
    175      * @param q the probability of failure (1 - p).
    176      * @return log(p(x)).
    177      */
    178     static double logBinomialProbability(int x, int n, double p, double q) {
    179         double ret;
    180         if (x == 0) {
    181             if (p < 0.1) {
    182                 ret = -getDeviancePart(n, n * q) - n * p;
    183             } else {
    184                 ret = n * FastMath.log(q);
    185             }
    186         } else if (x == n) {
    187             if (q < 0.1) {
    188                 ret = -getDeviancePart(n, n * p) - n * q;
    189             } else {
    190                 ret = n * FastMath.log(p);
    191             }
    192         } else {
    193             ret = getStirlingError(n) - getStirlingError(x) -
    194                   getStirlingError(n - x) - getDeviancePart(x, n * p) -
    195                   getDeviancePart(n - x, n * q);
    196             double f = (MathUtils.TWO_PI * x * (n - x)) / n;
    197             ret = -0.5 * FastMath.log(f) + ret;
    198         }
    199         return ret;
    200     }
    201 }
    202