View Javadoc
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    *      https://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  
18  /*
19   * This is not the original file distributed by the Apache Software Foundation
20   * It has been modified by the Hipparchus project
21   */
22  package org.hipparchus.distribution.continuous;
23  
24  import org.hipparchus.exception.LocalizedCoreFormats;
25  import org.hipparchus.exception.MathIllegalArgumentException;
26  import org.hipparchus.special.Beta;
27  import org.hipparchus.special.Gamma;
28  import org.hipparchus.util.FastMath;
29  
30  /**
31   * Implements the Beta distribution.
32   *
33   * @see <a href="http://en.wikipedia.org/wiki/Beta_distribution">Beta distribution</a>
34   */
35  public class BetaDistribution extends AbstractRealDistribution {
36      /** Serializable version identifier. */
37      private static final long serialVersionUID = 20160320L;
38      /** First shape parameter. */
39      private final double alpha;
40      /** Second shape parameter. */
41      private final double beta;
42      /** Normalizing factor used in density computations. */
43      private final double z;
44  
45      /**
46       * Build a new instance.
47       *
48       * @param alpha First shape parameter (must be positive).
49       * @param beta Second shape parameter (must be positive).
50       */
51      public BetaDistribution(double alpha, double beta) {
52          this(alpha, beta, DEFAULT_SOLVER_ABSOLUTE_ACCURACY);
53      }
54  
55      /**
56       * Build a new instance.
57       *
58       * @param alpha First shape parameter (must be positive).
59       * @param beta Second shape parameter (must be positive).
60       * @param inverseCumAccuracy Maximum absolute error in inverse
61       * cumulative probability estimates (defaults to
62       * {@link #DEFAULT_SOLVER_ABSOLUTE_ACCURACY}).
63       */
64      public BetaDistribution(double alpha, double beta, double inverseCumAccuracy) {
65          super(inverseCumAccuracy);
66  
67          this.alpha = alpha;
68          this.beta  = beta;
69          this.z     = Gamma.logGamma(alpha) +
70                       Gamma.logGamma(beta)  -
71                       Gamma.logGamma(alpha + beta);
72      }
73  
74      /**
75       * Access the first shape parameter, {@code alpha}.
76       *
77       * @return the first shape parameter.
78       */
79      public double getAlpha() {
80          return alpha;
81      }
82  
83      /**
84       * Access the second shape parameter, {@code beta}.
85       *
86       * @return the second shape parameter.
87       */
88      public double getBeta() {
89          return beta;
90      }
91  
92      /** {@inheritDoc} */
93      @Override
94      public double density(double x) {
95          final double logDensity = logDensity(x);
96          return logDensity == Double.NEGATIVE_INFINITY ? 0 : FastMath.exp(logDensity);
97      }
98  
99      /** {@inheritDoc} **/
100     @Override
101     public double logDensity(double x) {
102         if (x < 0 || x > 1) {
103             return Double.NEGATIVE_INFINITY;
104         } else if (x == 0) {
105             if (alpha < 1) {
106                 throw new MathIllegalArgumentException(LocalizedCoreFormats.CANNOT_COMPUTE_BETA_DENSITY_AT_0_FOR_SOME_ALPHA,
107                                                        alpha, 1, false);
108             }
109             return Double.NEGATIVE_INFINITY;
110         } else if (x == 1) {
111             if (beta < 1) {
112                 throw new MathIllegalArgumentException(LocalizedCoreFormats.CANNOT_COMPUTE_BETA_DENSITY_AT_1_FOR_SOME_BETA,
113                                                        beta, 1, false);
114             }
115             return Double.NEGATIVE_INFINITY;
116         } else {
117             double logX = FastMath.log(x);
118             double log1mX = FastMath.log1p(-x);
119             return (alpha - 1) * logX + (beta - 1) * log1mX - z;
120         }
121     }
122 
123     /** {@inheritDoc} */
124     @Override
125     public double cumulativeProbability(double x)  {
126         if (x <= 0) {
127             return 0;
128         } else if (x >= 1) {
129             return 1;
130         } else {
131             return Beta.regularizedBeta(x, alpha, beta);
132         }
133     }
134 
135     /**
136      * {@inheritDoc}
137      *
138      * For first shape parameter {@code alpha} and second shape parameter
139      * {@code beta}, the mean is {@code alpha / (alpha + beta)}.
140      */
141     @Override
142     public double getNumericalMean() {
143         final double a = getAlpha();
144         return a / (a + getBeta());
145     }
146 
147     /**
148      * {@inheritDoc}
149      *
150      * For first shape parameter {@code alpha} and second shape parameter
151      * {@code beta}, the variance is
152      * {@code (alpha * beta) / [(alpha + beta)^2 * (alpha + beta + 1)]}.
153      */
154     @Override
155     public double getNumericalVariance() {
156         final double a = getAlpha();
157         final double b = getBeta();
158         final double alphabetasum = a + b;
159         return (a * b) / ((alphabetasum * alphabetasum) * (alphabetasum + 1));
160     }
161 
162     /**
163      * {@inheritDoc}
164      *
165      * The lower bound of the support is always 0 no matter the parameters.
166      *
167      * @return lower bound of the support (always 0)
168      */
169     @Override
170     public double getSupportLowerBound() {
171         return 0;
172     }
173 
174     /**
175      * {@inheritDoc}
176      *
177      * The upper bound of the support is always 1 no matter the parameters.
178      *
179      * @return upper bound of the support (always 1)
180      */
181     @Override
182     public double getSupportUpperBound() {
183         return 1;
184     }
185 
186     /**
187      * {@inheritDoc}
188      *
189      * The support of this distribution is connected.
190      *
191      * @return {@code true}
192      */
193     @Override
194     public boolean isSupportConnected() {
195         return true;
196     }
197 }