001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package org.apache.commons.math3.distribution; 018 019 import java.util.ArrayList; 020 import java.util.List; 021 import org.apache.commons.math3.exception.DimensionMismatchException; 022 import org.apache.commons.math3.exception.MathArithmeticException; 023 import org.apache.commons.math3.exception.NotANumberException; 024 import org.apache.commons.math3.exception.NotFiniteNumberException; 025 import org.apache.commons.math3.exception.NotPositiveException; 026 import org.apache.commons.math3.random.RandomGenerator; 027 import org.apache.commons.math3.random.Well19937c; 028 import org.apache.commons.math3.util.Pair; 029 030 /** 031 * <p>Implementation of a real-valued {@link EnumeratedDistribution}. 032 * 033 * <p>Values with zero-probability are allowed but they do not extend the 034 * support.<br/> 035 * Duplicate values are allowed. Probabilities of duplicate values are combined 036 * when computing cumulative probabilities and statistics.</p> 037 * 038 * @version $Id: EnumeratedRealDistribution.html 860130 2013-04-27 21:11:39Z luc $ 039 * @since 3.2 040 */ 041 public class EnumeratedRealDistribution extends AbstractRealDistribution { 042 043 /** Serializable UID. */ 044 private static final long serialVersionUID = 20130308L; 045 046 /** 047 * {@link EnumeratedDistribution} (using the {@link Double} wrapper) 048 * used to generate the pmf. 049 */ 050 protected final EnumeratedDistribution<Double> innerDistribution; 051 052 /** 053 * Create a discrete distribution using the given probability mass function 054 * enumeration. 055 * 056 * @param singletons array of random variable values. 057 * @param probabilities array of probabilities. 058 * @throws DimensionMismatchException if 059 * {@code singletons.length != probabilities.length} 060 * @throws NotPositiveException if any of the probabilities are negative. 061 * @throws NotFiniteNumberException if any of the probabilities are infinite. 062 * @throws NotANumberException if any of the probabilities are NaN. 063 * @throws MathArithmeticException all of the probabilities are 0. 064 */ 065 public EnumeratedRealDistribution(final double[] singletons, final double[] probabilities) 066 throws DimensionMismatchException, NotPositiveException, MathArithmeticException, 067 NotFiniteNumberException, NotANumberException { 068 this(new Well19937c(), singletons, probabilities); 069 } 070 071 /** 072 * Create a discrete distribution using the given random number generator 073 * and probability mass function enumeration. 074 * 075 * @param rng random number generator. 076 * @param singletons array of random variable values. 077 * @param probabilities array of probabilities. 078 * @throws DimensionMismatchException if 079 * {@code singletons.length != probabilities.length} 080 * @throws NotPositiveException if any of the probabilities are negative. 081 * @throws NotFiniteNumberException if any of the probabilities are infinite. 082 * @throws NotANumberException if any of the probabilities are NaN. 083 * @throws MathArithmeticException all of the probabilities are 0. 084 */ 085 public EnumeratedRealDistribution(final RandomGenerator rng, 086 final double[] singletons, final double[] probabilities) 087 throws DimensionMismatchException, NotPositiveException, MathArithmeticException, 088 NotFiniteNumberException, NotANumberException { 089 super(rng); 090 if (singletons.length != probabilities.length) { 091 throw new DimensionMismatchException(probabilities.length, singletons.length); 092 } 093 094 List<Pair<Double, Double>> samples = new ArrayList<Pair<Double, Double>>(singletons.length); 095 096 for (int i = 0; i < singletons.length; i++) { 097 samples.add(new Pair<Double, Double>(singletons[i], probabilities[i])); 098 } 099 100 innerDistribution = new EnumeratedDistribution<Double>(rng, samples); 101 } 102 103 /** 104 * {@inheritDoc} 105 */ 106 @Override 107 public double probability(final double x) { 108 return innerDistribution.probability(x); 109 } 110 111 /** 112 * For a random variable {@code X} whose values are distributed according to 113 * this distribution, this method returns {@code P(X = x)}. In other words, 114 * this method represents the probability mass function (PMF) for the 115 * distribution. 116 * 117 * @param x the point at which the PMF is evaluated 118 * @return the value of the probability mass function at point {@code x} 119 */ 120 public double density(final double x) { 121 return probability(x); 122 } 123 124 /** 125 * {@inheritDoc} 126 */ 127 public double cumulativeProbability(final double x) { 128 double probability = 0; 129 130 for (final Pair<Double, Double> sample : innerDistribution.getPmf()) { 131 if (sample.getKey() <= x) { 132 probability += sample.getValue(); 133 } 134 } 135 136 return probability; 137 } 138 139 /** 140 * {@inheritDoc} 141 * 142 * @return {@code sum(singletons[i] * probabilities[i])} 143 */ 144 public double getNumericalMean() { 145 double mean = 0; 146 147 for (final Pair<Double, Double> sample : innerDistribution.getPmf()) { 148 mean += sample.getValue() * sample.getKey(); 149 } 150 151 return mean; 152 } 153 154 /** 155 * {@inheritDoc} 156 * 157 * @return {@code sum((singletons[i] - mean) ^ 2 * probabilities[i])} 158 */ 159 public double getNumericalVariance() { 160 double mean = 0; 161 double meanOfSquares = 0; 162 163 for (final Pair<Double, Double> sample : innerDistribution.getPmf()) { 164 mean += sample.getValue() * sample.getKey(); 165 meanOfSquares += sample.getValue() * sample.getKey() * sample.getKey(); 166 } 167 168 return meanOfSquares - mean * mean; 169 } 170 171 /** 172 * {@inheritDoc} 173 * 174 * Returns the lowest value with non-zero probability. 175 * 176 * @return the lowest value with non-zero probability. 177 */ 178 public double getSupportLowerBound() { 179 double min = Double.POSITIVE_INFINITY; 180 for (final Pair<Double, Double> sample : innerDistribution.getPmf()) { 181 if (sample.getKey() < min && sample.getValue() > 0) { 182 min = sample.getKey(); 183 } 184 } 185 186 return min; 187 } 188 189 /** 190 * {@inheritDoc} 191 * 192 * Returns the highest value with non-zero probability. 193 * 194 * @return the highest value with non-zero probability. 195 */ 196 public double getSupportUpperBound() { 197 double max = Double.NEGATIVE_INFINITY; 198 for (final Pair<Double, Double> sample : innerDistribution.getPmf()) { 199 if (sample.getKey() > max && sample.getValue() > 0) { 200 max = sample.getKey(); 201 } 202 } 203 204 return max; 205 } 206 207 /** 208 * {@inheritDoc} 209 * 210 * The support of this distribution includes the lower bound. 211 * 212 * @return {@code true} 213 */ 214 public boolean isSupportLowerBoundInclusive() { 215 return true; 216 } 217 218 /** 219 * {@inheritDoc} 220 * 221 * The support of this distribution includes the upper bound. 222 * 223 * @return {@code true} 224 */ 225 public boolean isSupportUpperBoundInclusive() { 226 return true; 227 } 228 229 /** 230 * {@inheritDoc} 231 * 232 * The support of this distribution is connected. 233 * 234 * @return {@code true} 235 */ 236 public boolean isSupportConnected() { 237 return true; 238 } 239 240 /** 241 * {@inheritDoc} 242 */ 243 @Override 244 public double sample() { 245 return innerDistribution.sample(); 246 } 247 }