EnumeratedRealDistribution.java

  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.math3.distribution;

  18. import java.util.ArrayList;
  19. import java.util.List;

  20. import org.apache.commons.math3.exception.DimensionMismatchException;
  21. import org.apache.commons.math3.exception.MathArithmeticException;
  22. import org.apache.commons.math3.exception.NotANumberException;
  23. import org.apache.commons.math3.exception.NotFiniteNumberException;
  24. import org.apache.commons.math3.exception.NotPositiveException;
  25. import org.apache.commons.math3.exception.OutOfRangeException;
  26. import org.apache.commons.math3.random.RandomGenerator;
  27. import org.apache.commons.math3.random.Well19937c;
  28. import org.apache.commons.math3.util.Pair;

  29. /**
  30.  * <p>Implementation of a real-valued {@link EnumeratedDistribution}.
  31.  *
  32.  * <p>Values with zero-probability are allowed but they do not extend the
  33.  * support.<br/>
  34.  * Duplicate values are allowed. Probabilities of duplicate values are combined
  35.  * when computing cumulative probabilities and statistics.</p>
  36.  *
  37.  * @since 3.2
  38.  */
  39. public class EnumeratedRealDistribution extends AbstractRealDistribution {

  40.     /** Serializable UID. */
  41.     private static final long serialVersionUID = 20130308L;

  42.     /**
  43.      * {@link EnumeratedDistribution} (using the {@link Double} wrapper)
  44.      * used to generate the pmf.
  45.      */
  46.     protected final EnumeratedDistribution<Double> innerDistribution;

  47.     /**
  48.      * Create a discrete distribution using the given probability mass function
  49.      * enumeration.
  50.      * <p>
  51.      * <b>Note:</b> this constructor will implicitly create an instance of
  52.      * {@link Well19937c} as random generator to be used for sampling only (see
  53.      * {@link #sample()} and {@link #sample(int)}). In case no sampling is
  54.      * needed for the created distribution, it is advised to pass {@code null}
  55.      * as random generator via the appropriate constructors to avoid the
  56.      * additional initialisation overhead.
  57.      *
  58.      * @param singletons array of random variable values.
  59.      * @param probabilities array of probabilities.
  60.      * @throws DimensionMismatchException if
  61.      * {@code singletons.length != probabilities.length}
  62.      * @throws NotPositiveException if any of the probabilities are negative.
  63.      * @throws NotFiniteNumberException if any of the probabilities are infinite.
  64.      * @throws NotANumberException if any of the probabilities are NaN.
  65.      * @throws MathArithmeticException all of the probabilities are 0.
  66.      */
  67.     public EnumeratedRealDistribution(final double[] singletons, final double[] probabilities)
  68.     throws DimensionMismatchException, NotPositiveException, MathArithmeticException,
  69.            NotFiniteNumberException, NotANumberException {
  70.         this(new Well19937c(), singletons, probabilities);
  71.     }

  72.     /**
  73.      * Create a discrete distribution using the given random number generator
  74.      * and probability mass function enumeration.
  75.      *
  76.      * @param rng random number generator.
  77.      * @param singletons array of random variable values.
  78.      * @param probabilities array of probabilities.
  79.      * @throws DimensionMismatchException if
  80.      * {@code singletons.length != probabilities.length}
  81.      * @throws NotPositiveException if any of the probabilities are negative.
  82.      * @throws NotFiniteNumberException if any of the probabilities are infinite.
  83.      * @throws NotANumberException if any of the probabilities are NaN.
  84.      * @throws MathArithmeticException all of the probabilities are 0.
  85.      */
  86.     public EnumeratedRealDistribution(final RandomGenerator rng,
  87.                                     final double[] singletons, final double[] probabilities)
  88.         throws DimensionMismatchException, NotPositiveException, MathArithmeticException,
  89.                NotFiniteNumberException, NotANumberException {
  90.         super(rng);
  91.         if (singletons.length != probabilities.length) {
  92.             throw new DimensionMismatchException(probabilities.length, singletons.length);
  93.         }

  94.         List<Pair<Double, Double>> samples = new ArrayList<Pair<Double, Double>>(singletons.length);

  95.         for (int i = 0; i < singletons.length; i++) {
  96.             samples.add(new Pair<Double, Double>(singletons[i], probabilities[i]));
  97.         }

  98.         innerDistribution = new EnumeratedDistribution<Double>(rng, samples);
  99.     }

  100.     /**
  101.      * {@inheritDoc}
  102.      */
  103.     @Override
  104.     public double probability(final double x) {
  105.         return innerDistribution.probability(x);
  106.     }

  107.     /**
  108.      * For a random variable {@code X} whose values are distributed according to
  109.      * this distribution, this method returns {@code P(X = x)}. In other words,
  110.      * this method represents the probability mass function (PMF) for the
  111.      * distribution.
  112.      *
  113.      * @param x the point at which the PMF is evaluated
  114.      * @return the value of the probability mass function at point {@code x}
  115.      */
  116.     public double density(final double x) {
  117.         return probability(x);
  118.     }

  119.     /**
  120.      * {@inheritDoc}
  121.      */
  122.     public double cumulativeProbability(final double x) {
  123.         double probability = 0;

  124.         for (final Pair<Double, Double> sample : innerDistribution.getPmf()) {
  125.             if (sample.getKey() <= x) {
  126.                 probability += sample.getValue();
  127.             }
  128.         }

  129.         return probability;
  130.     }

  131.     /**
  132.      * {@inheritDoc}
  133.      */
  134.     @Override
  135.     public double inverseCumulativeProbability(final double p) throws OutOfRangeException {
  136.         if (p < 0.0 || p > 1.0) {
  137.             throw new OutOfRangeException(p, 0, 1);
  138.         }

  139.         double probability = 0;
  140.         double x = getSupportLowerBound();
  141.         for (final Pair<Double, Double> sample : innerDistribution.getPmf()) {
  142.             if (sample.getValue() == 0.0) {
  143.                 continue;
  144.             }

  145.             probability += sample.getValue();
  146.             x = sample.getKey();

  147.             if (probability >= p) {
  148.                 break;
  149.             }
  150.         }

  151.         return x;
  152.     }

  153.     /**
  154.      * {@inheritDoc}
  155.      *
  156.      * @return {@code sum(singletons[i] * probabilities[i])}
  157.      */
  158.     public double getNumericalMean() {
  159.         double mean = 0;

  160.         for (final Pair<Double, Double> sample : innerDistribution.getPmf()) {
  161.             mean += sample.getValue() * sample.getKey();
  162.         }

  163.         return mean;
  164.     }

  165.     /**
  166.      * {@inheritDoc}
  167.      *
  168.      * @return {@code sum((singletons[i] - mean) ^ 2 * probabilities[i])}
  169.      */
  170.     public double getNumericalVariance() {
  171.         double mean = 0;
  172.         double meanOfSquares = 0;

  173.         for (final Pair<Double, Double> sample : innerDistribution.getPmf()) {
  174.             mean += sample.getValue() * sample.getKey();
  175.             meanOfSquares += sample.getValue() * sample.getKey() * sample.getKey();
  176.         }

  177.         return meanOfSquares - mean * mean;
  178.     }

  179.     /**
  180.      * {@inheritDoc}
  181.      *
  182.      * Returns the lowest value with non-zero probability.
  183.      *
  184.      * @return the lowest value with non-zero probability.
  185.      */
  186.     public double getSupportLowerBound() {
  187.         double min = Double.POSITIVE_INFINITY;
  188.         for (final Pair<Double, Double> sample : innerDistribution.getPmf()) {
  189.             if (sample.getKey() < min && sample.getValue() > 0) {
  190.                 min = sample.getKey();
  191.             }
  192.         }

  193.         return min;
  194.     }

  195.     /**
  196.      * {@inheritDoc}
  197.      *
  198.      * Returns the highest value with non-zero probability.
  199.      *
  200.      * @return the highest value with non-zero probability.
  201.      */
  202.     public double getSupportUpperBound() {
  203.         double max = Double.NEGATIVE_INFINITY;
  204.         for (final Pair<Double, Double> sample : innerDistribution.getPmf()) {
  205.             if (sample.getKey() > max && sample.getValue() > 0) {
  206.                 max = sample.getKey();
  207.             }
  208.         }

  209.         return max;
  210.     }

  211.     /**
  212.      * {@inheritDoc}
  213.      *
  214.      * The support of this distribution includes the lower bound.
  215.      *
  216.      * @return {@code true}
  217.      */
  218.     public boolean isSupportLowerBoundInclusive() {
  219.         return true;
  220.     }

  221.     /**
  222.      * {@inheritDoc}
  223.      *
  224.      * The support of this distribution includes the upper bound.
  225.      *
  226.      * @return {@code true}
  227.      */
  228.     public boolean isSupportUpperBoundInclusive() {
  229.         return true;
  230.     }

  231.     /**
  232.      * {@inheritDoc}
  233.      *
  234.      * The support of this distribution is connected.
  235.      *
  236.      * @return {@code true}
  237.      */
  238.     public boolean isSupportConnected() {
  239.         return true;
  240.     }

  241.     /**
  242.      * {@inheritDoc}
  243.      */
  244.     @Override
  245.     public double sample() {
  246.         return innerDistribution.sample();
  247.     }
  248. }