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 java.io.Serializable;
     20 
     21 import org.apache.commons.math.MathException;
     22 import org.apache.commons.math.MathRuntimeException;
     23 import org.apache.commons.math.exception.util.LocalizedFormats;
     24 import org.apache.commons.math.special.Gamma;
     25 import org.apache.commons.math.util.MathUtils;
     26 import org.apache.commons.math.util.FastMath;
     27 
     28 /**
     29  * Implementation for the {@link PoissonDistribution}.
     30  *
     31  * @version $Revision: 1054524 $ $Date: 2011-01-03 05:59:18 +0100 (lun. 03 janv. 2011) $
     32  */
     33 public class PoissonDistributionImpl extends AbstractIntegerDistribution
     34         implements PoissonDistribution, Serializable {
     35 
     36     /**
     37      * Default maximum number of iterations for cumulative probability calculations.
     38      * @since 2.1
     39      */
     40     public static final int DEFAULT_MAX_ITERATIONS = 10000000;
     41 
     42     /**
     43      * Default convergence criterion.
     44      * @since 2.1
     45      */
     46     public static final double DEFAULT_EPSILON = 1E-12;
     47 
     48     /** Serializable version identifier */
     49     private static final long serialVersionUID = -3349935121172596109L;
     50 
     51     /** Distribution used to compute normal approximation. */
     52     private NormalDistribution normal;
     53 
     54     /**
     55      * Holds the Poisson mean for the distribution.
     56      */
     57     private double mean;
     58 
     59     /**
     60      * Maximum number of iterations for cumulative probability.
     61      *
     62      * Cumulative probabilities are estimated using either Lanczos series approximation of
     63      * Gamma#regularizedGammaP or continued fraction approximation of Gamma#regularizedGammaQ.
     64      */
     65     private int maxIterations = DEFAULT_MAX_ITERATIONS;
     66 
     67     /**
     68      * Convergence criterion for cumulative probability.
     69      */
     70     private double epsilon = DEFAULT_EPSILON;
     71 
     72     /**
     73      * Create a new Poisson distribution with the given the mean. The mean value
     74      * must be positive; otherwise an <code>IllegalArgument</code> is thrown.
     75      *
     76      * @param p the Poisson mean
     77      * @throws IllegalArgumentException if p &le; 0
     78      */
     79     public PoissonDistributionImpl(double p) {
     80         this(p, new NormalDistributionImpl());
     81     }
     82 
     83     /**
     84      * Create a new Poisson distribution with the given mean, convergence criterion
     85      * and maximum number of iterations.
     86      *
     87      * @param p the Poisson mean
     88      * @param epsilon the convergence criteria for cumulative probabilites
     89      * @param maxIterations the maximum number of iterations for cumulative probabilites
     90      * @since 2.1
     91      */
     92     public PoissonDistributionImpl(double p, double epsilon, int maxIterations) {
     93         setMean(p);
     94         this.epsilon = epsilon;
     95         this.maxIterations = maxIterations;
     96     }
     97 
     98     /**
     99      * Create a new Poisson distribution with the given mean and convergence criterion.
    100      *
    101      * @param p the Poisson mean
    102      * @param epsilon the convergence criteria for cumulative probabilites
    103      * @since 2.1
    104      */
    105     public PoissonDistributionImpl(double p, double epsilon) {
    106         setMean(p);
    107         this.epsilon = epsilon;
    108     }
    109 
    110     /**
    111      * Create a new Poisson distribution with the given mean and maximum number of iterations.
    112      *
    113      * @param p the Poisson mean
    114      * @param maxIterations the maximum number of iterations for cumulative probabilites
    115      * @since 2.1
    116      */
    117     public PoissonDistributionImpl(double p, int maxIterations) {
    118         setMean(p);
    119         this.maxIterations = maxIterations;
    120     }
    121 
    122 
    123     /**
    124      * Create a new Poisson distribution with the given the mean. The mean value
    125      * must be positive; otherwise an <code>IllegalArgument</code> is thrown.
    126      *
    127      * @param p the Poisson mean
    128      * @param z a normal distribution used to compute normal approximations.
    129      * @throws IllegalArgumentException if p &le; 0
    130      * @since 1.2
    131      * @deprecated as of 2.1 (to avoid possibly inconsistent state, the
    132      * "NormalDistribution" will be instantiated internally)
    133      */
    134     @Deprecated
    135     public PoissonDistributionImpl(double p, NormalDistribution z) {
    136         super();
    137         setNormalAndMeanInternal(z, p);
    138     }
    139 
    140     /**
    141      * Get the Poisson mean for the distribution.
    142      *
    143      * @return the Poisson mean for the distribution.
    144      */
    145     public double getMean() {
    146         return mean;
    147     }
    148 
    149     /**
    150      * Set the Poisson mean for the distribution. The mean value must be
    151      * positive; otherwise an <code>IllegalArgument</code> is thrown.
    152      *
    153      * @param p the Poisson mean value
    154      * @throws IllegalArgumentException if p &le; 0
    155      * @deprecated as of 2.1 (class will become immutable in 3.0)
    156      */
    157     @Deprecated
    158     public void setMean(double p) {
    159         setNormalAndMeanInternal(normal, p);
    160     }
    161     /**
    162      * Set the Poisson mean for the distribution. The mean value must be
    163      * positive; otherwise an <code>IllegalArgument</code> is thrown.
    164      *
    165      * @param z the new distribution
    166      * @param p the Poisson mean value
    167      * @throws IllegalArgumentException if p &le; 0
    168      */
    169     private void setNormalAndMeanInternal(NormalDistribution z,
    170                                           double p) {
    171         if (p <= 0) {
    172             throw MathRuntimeException.createIllegalArgumentException(
    173                     LocalizedFormats.NOT_POSITIVE_POISSON_MEAN, p);
    174         }
    175         mean = p;
    176         normal = z;
    177         normal.setMean(p);
    178         normal.setStandardDeviation(FastMath.sqrt(p));
    179     }
    180 
    181     /**
    182      * The probability mass function P(X = x) for a Poisson distribution.
    183      *
    184      * @param x the value at which the probability density function is
    185      *            evaluated.
    186      * @return the value of the probability mass function at x
    187      */
    188     public double probability(int x) {
    189         double ret;
    190         if (x < 0 || x == Integer.MAX_VALUE) {
    191             ret = 0.0;
    192         } else if (x == 0) {
    193             ret = FastMath.exp(-mean);
    194         } else {
    195             ret = FastMath.exp(-SaddlePointExpansion.getStirlingError(x) -
    196                   SaddlePointExpansion.getDeviancePart(x, mean)) /
    197                   FastMath.sqrt(MathUtils.TWO_PI * x);
    198         }
    199         return ret;
    200     }
    201 
    202     /**
    203      * The probability distribution function P(X <= x) for a Poisson
    204      * distribution.
    205      *
    206      * @param x the value at which the PDF is evaluated.
    207      * @return Poisson distribution function evaluated at x
    208      * @throws MathException if the cumulative probability can not be computed
    209      *             due to convergence or other numerical errors.
    210      */
    211     @Override
    212     public double cumulativeProbability(int x) throws MathException {
    213         if (x < 0) {
    214             return 0;
    215         }
    216         if (x == Integer.MAX_VALUE) {
    217             return 1;
    218         }
    219         return Gamma.regularizedGammaQ((double) x + 1, mean, epsilon, maxIterations);
    220     }
    221 
    222     /**
    223      * Calculates the Poisson distribution function using a normal
    224      * approximation. The <code>N(mean, sqrt(mean))</code> distribution is used
    225      * to approximate the Poisson distribution.
    226      * <p>
    227      * The computation uses "half-correction" -- evaluating the normal
    228      * distribution function at <code>x + 0.5</code>
    229      * </p>
    230      *
    231      * @param x the upper bound, inclusive
    232      * @return the distribution function value calculated using a normal
    233      *         approximation
    234      * @throws MathException if an error occurs computing the normal
    235      *             approximation
    236      */
    237     public double normalApproximateProbability(int x) throws MathException {
    238         // calculate the probability using half-correction
    239         return normal.cumulativeProbability(x + 0.5);
    240     }
    241 
    242     /**
    243      * Generates a random value sampled from this distribution.
    244      *
    245      * <p><strong>Algorithm Description</strong>:
    246      * <ul><li> For small means, uses simulation of a Poisson process
    247      * using Uniform deviates, as described
    248      * <a href="http://irmi.epfl.ch/cmos/Pmmi/interactive/rng7.htm"> here.</a>
    249      * The Poisson process (and hence value returned) is bounded by 1000 * mean.</li><
    250      *
    251      * <li> For large means, uses the rejection algorithm described in <br/>
    252      * Devroye, Luc. (1981).<i>The Computer Generation of Poisson Random Variables</i>
    253      * <strong>Computing</strong> vol. 26 pp. 197-207.</li></ul></p>
    254      *
    255      * @return random value
    256      * @since 2.2
    257      * @throws MathException if an error occurs generating the random value
    258      */
    259     @Override
    260     public int sample() throws MathException {
    261         return (int) FastMath.min(randomData.nextPoisson(mean), Integer.MAX_VALUE);
    262     }
    263 
    264     /**
    265      * Access the domain value lower bound, based on <code>p</code>, used to
    266      * bracket a CDF root. This method is used by
    267      * {@link #inverseCumulativeProbability(double)} to find critical values.
    268      *
    269      * @param p the desired probability for the critical value
    270      * @return domain lower bound
    271      */
    272     @Override
    273     protected int getDomainLowerBound(double p) {
    274         return 0;
    275     }
    276 
    277     /**
    278      * Access the domain value upper bound, based on <code>p</code>, used to
    279      * bracket a CDF root. This method is used by
    280      * {@link #inverseCumulativeProbability(double)} to find critical values.
    281      *
    282      * @param p the desired probability for the critical value
    283      * @return domain upper bound
    284      */
    285     @Override
    286     protected int getDomainUpperBound(double p) {
    287         return Integer.MAX_VALUE;
    288     }
    289 
    290     /**
    291      * Modify the normal distribution used to compute normal approximations. The
    292      * caller is responsible for insuring the normal distribution has the proper
    293      * parameter settings.
    294      *
    295      * @param value the new distribution
    296      * @since 1.2
    297      * @deprecated as of 2.1 (class will become immutable in 3.0)
    298      */
    299     @Deprecated
    300     public void setNormal(NormalDistribution value) {
    301         setNormalAndMeanInternal(value, mean);
    302     }
    303 
    304     /**
    305      * Returns the lower bound of the support for the distribution.
    306      *
    307      * The lower bound of the support is always 0 no matter the mean parameter.
    308      *
    309      * @return lower bound of the support (always 0)
    310      * @since 2.2
    311      */
    312     public int getSupportLowerBound() {
    313         return 0;
    314     }
    315 
    316     /**
    317      * Returns the upper bound of the support for the distribution.
    318      *
    319      * The upper bound of the support is positive infinity,
    320      * regardless of the parameter values. There is no integer infinity,
    321      * so this method returns <code>Integer.MAX_VALUE</code> and
    322      * {@link #isSupportUpperBoundInclusive()} returns <code>true</code>.
    323      *
    324      * @return upper bound of the support (always <code>Integer.MAX_VALUE</code> for positive infinity)
    325      * @since 2.2
    326      */
    327     public int getSupportUpperBound() {
    328         return Integer.MAX_VALUE;
    329     }
    330 
    331     /**
    332      * Returns the variance of the distribution.
    333      *
    334      * For mean parameter <code>p</code>, the variance is <code>p</code>
    335      *
    336      * @return the variance
    337      * @since 2.2
    338      */
    339     public double getNumericalVariance() {
    340         return getMean();
    341     }
    342 
    343 }
    344