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 */ 017package org.apache.commons.math3.distribution; 018 019import org.apache.commons.math3.exception.NumberIsTooSmallException; 020import org.apache.commons.math3.exception.util.LocalizedFormats; 021import org.apache.commons.math3.random.RandomGenerator; 022import org.apache.commons.math3.random.Well19937c; 023import org.apache.commons.math3.special.Beta; 024import org.apache.commons.math3.special.Gamma; 025import org.apache.commons.math3.util.FastMath; 026 027/** 028 * Implements the Beta distribution. 029 * 030 * @see <a href="http://en.wikipedia.org/wiki/Beta_distribution">Beta distribution</a> 031 * @since 2.0 (changed to concrete class in 3.0) 032 */ 033public class BetaDistribution extends AbstractRealDistribution { 034 /** 035 * Default inverse cumulative probability accuracy. 036 * @since 2.1 037 */ 038 public static final double DEFAULT_INVERSE_ABSOLUTE_ACCURACY = 1e-9; 039 /** Serializable version identifier. */ 040 private static final long serialVersionUID = -1221965979403477668L; 041 /** First shape parameter. */ 042 private final double alpha; 043 /** Second shape parameter. */ 044 private final double beta; 045 /** Normalizing factor used in density computations. 046 * updated whenever alpha or beta are changed. 047 */ 048 private double z; 049 /** Inverse cumulative probability accuracy. */ 050 private final double solverAbsoluteAccuracy; 051 052 /** 053 * Build a new instance. 054 * <p> 055 * <b>Note:</b> this constructor will implicitly create an instance of 056 * {@link Well19937c} as random generator to be used for sampling only (see 057 * {@link #sample()} and {@link #sample(int)}). In case no sampling is 058 * needed for the created distribution, it is advised to pass {@code null} 059 * as random generator via the appropriate constructors to avoid the 060 * additional initialisation overhead. 061 * 062 * @param alpha First shape parameter (must be positive). 063 * @param beta Second shape parameter (must be positive). 064 */ 065 public BetaDistribution(double alpha, double beta) { 066 this(alpha, beta, DEFAULT_INVERSE_ABSOLUTE_ACCURACY); 067 } 068 069 /** 070 * Build a new instance. 071 * <p> 072 * <b>Note:</b> this constructor will implicitly create an instance of 073 * {@link Well19937c} as random generator to be used for sampling only (see 074 * {@link #sample()} and {@link #sample(int)}). In case no sampling is 075 * needed for the created distribution, it is advised to pass {@code null} 076 * as random generator via the appropriate constructors to avoid the 077 * additional initialisation overhead. 078 * 079 * @param alpha First shape parameter (must be positive). 080 * @param beta Second shape parameter (must be positive). 081 * @param inverseCumAccuracy Maximum absolute error in inverse 082 * cumulative probability estimates (defaults to 083 * {@link #DEFAULT_INVERSE_ABSOLUTE_ACCURACY}). 084 * @since 2.1 085 */ 086 public BetaDistribution(double alpha, double beta, double inverseCumAccuracy) { 087 this(new Well19937c(), alpha, beta, inverseCumAccuracy); 088 } 089 090 /** 091 * Creates a β distribution. 092 * 093 * @param rng Random number generator. 094 * @param alpha First shape parameter (must be positive). 095 * @param beta Second shape parameter (must be positive). 096 * @since 3.3 097 */ 098 public BetaDistribution(RandomGenerator rng, double alpha, double beta) { 099 this(rng, alpha, beta, DEFAULT_INVERSE_ABSOLUTE_ACCURACY); 100 } 101 102 /** 103 * Creates a β distribution. 104 * 105 * @param rng Random number generator. 106 * @param alpha First shape parameter (must be positive). 107 * @param beta Second shape parameter (must be positive). 108 * @param inverseCumAccuracy Maximum absolute error in inverse 109 * cumulative probability estimates (defaults to 110 * {@link #DEFAULT_INVERSE_ABSOLUTE_ACCURACY}). 111 * @since 3.1 112 */ 113 public BetaDistribution(RandomGenerator rng, 114 double alpha, 115 double beta, 116 double inverseCumAccuracy) { 117 super(rng); 118 119 this.alpha = alpha; 120 this.beta = beta; 121 z = Double.NaN; 122 solverAbsoluteAccuracy = inverseCumAccuracy; 123 } 124 125 /** 126 * Access the first shape parameter, {@code alpha}. 127 * 128 * @return the first shape parameter. 129 */ 130 public double getAlpha() { 131 return alpha; 132 } 133 134 /** 135 * Access the second shape parameter, {@code beta}. 136 * 137 * @return the second shape parameter. 138 */ 139 public double getBeta() { 140 return beta; 141 } 142 143 /** Recompute the normalization factor. */ 144 private void recomputeZ() { 145 if (Double.isNaN(z)) { 146 z = Gamma.logGamma(alpha) + Gamma.logGamma(beta) - Gamma.logGamma(alpha + beta); 147 } 148 } 149 150 /** {@inheritDoc} */ 151 public double density(double x) { 152 final double logDensity = logDensity(x); 153 return logDensity == Double.NEGATIVE_INFINITY ? 0 : FastMath.exp(logDensity); 154 } 155 156 /** {@inheritDoc} **/ 157 @Override 158 public double logDensity(double x) { 159 recomputeZ(); 160 if (x < 0 || x > 1) { 161 return Double.NEGATIVE_INFINITY; 162 } else if (x == 0) { 163 if (alpha < 1) { 164 throw new NumberIsTooSmallException(LocalizedFormats.CANNOT_COMPUTE_BETA_DENSITY_AT_0_FOR_SOME_ALPHA, alpha, 1, false); 165 } 166 return Double.NEGATIVE_INFINITY; 167 } else if (x == 1) { 168 if (beta < 1) { 169 throw new NumberIsTooSmallException(LocalizedFormats.CANNOT_COMPUTE_BETA_DENSITY_AT_1_FOR_SOME_BETA, beta, 1, false); 170 } 171 return Double.NEGATIVE_INFINITY; 172 } else { 173 double logX = FastMath.log(x); 174 double log1mX = FastMath.log1p(-x); 175 return (alpha - 1) * logX + (beta - 1) * log1mX - z; 176 } 177 } 178 179 /** {@inheritDoc} */ 180 public double cumulativeProbability(double x) { 181 if (x <= 0) { 182 return 0; 183 } else if (x >= 1) { 184 return 1; 185 } else { 186 return Beta.regularizedBeta(x, alpha, beta); 187 } 188 } 189 190 /** 191 * Return the absolute accuracy setting of the solver used to estimate 192 * inverse cumulative probabilities. 193 * 194 * @return the solver absolute accuracy. 195 * @since 2.1 196 */ 197 @Override 198 protected double getSolverAbsoluteAccuracy() { 199 return solverAbsoluteAccuracy; 200 } 201 202 /** 203 * {@inheritDoc} 204 * 205 * For first shape parameter {@code alpha} and second shape parameter 206 * {@code beta}, the mean is {@code alpha / (alpha + beta)}. 207 */ 208 public double getNumericalMean() { 209 final double a = getAlpha(); 210 return a / (a + getBeta()); 211 } 212 213 /** 214 * {@inheritDoc} 215 * 216 * For first shape parameter {@code alpha} and second shape parameter 217 * {@code beta}, the variance is 218 * {@code (alpha * beta) / [(alpha + beta)^2 * (alpha + beta + 1)]}. 219 */ 220 public double getNumericalVariance() { 221 final double a = getAlpha(); 222 final double b = getBeta(); 223 final double alphabetasum = a + b; 224 return (a * b) / ((alphabetasum * alphabetasum) * (alphabetasum + 1)); 225 } 226 227 /** 228 * {@inheritDoc} 229 * 230 * The lower bound of the support is always 0 no matter the parameters. 231 * 232 * @return lower bound of the support (always 0) 233 */ 234 public double getSupportLowerBound() { 235 return 0; 236 } 237 238 /** 239 * {@inheritDoc} 240 * 241 * The upper bound of the support is always 1 no matter the parameters. 242 * 243 * @return upper bound of the support (always 1) 244 */ 245 public double getSupportUpperBound() { 246 return 1; 247 } 248 249 /** {@inheritDoc} */ 250 public boolean isSupportLowerBoundInclusive() { 251 return false; 252 } 253 254 /** {@inheritDoc} */ 255 public boolean isSupportUpperBoundInclusive() { 256 return false; 257 } 258 259 /** 260 * {@inheritDoc} 261 * 262 * The support of this distribution is connected. 263 * 264 * @return {@code true} 265 */ 266 public boolean isSupportConnected() { 267 return true; 268 } 269}