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.special;
018
019import org.apache.commons.math3.exception.NumberIsTooSmallException;
020import org.apache.commons.math3.exception.OutOfRangeException;
021import org.apache.commons.math3.util.ContinuedFraction;
022import org.apache.commons.math3.util.FastMath;
023
024/**
025 * <p>
026 * This is a utility class that provides computation methods related to the
027 * Beta family of functions.
028 * </p>
029 * <p>
030 * Implementation of {@link #logBeta(double, double)} is based on the
031 * algorithms described in
032 * <ul>
033 * <li><a href="http://dx.doi.org/10.1145/22721.23109">Didonato and Morris
034 *     (1986)</a>, <em>Computation of the Incomplete Gamma Function Ratios
035 *     and their Inverse</em>, TOMS 12(4), 377-393,</li>
036 * <li><a href="http://dx.doi.org/10.1145/131766.131776">Didonato and Morris
037 *     (1992)</a>, <em>Algorithm 708: Significant Digit Computation of the
038 *     Incomplete Beta Function Ratios</em>, TOMS 18(3), 360-373,</li>
039 * </ul>
040 * and implemented in the
041 * <a href="http://www.dtic.mil/docs/citations/ADA476840">NSWC Library of Mathematical Functions</a>,
042 * available
043 * <a href="http://www.ualberta.ca/CNS/RESEARCH/Software/NumericalNSWC/site.html">here</a>.
044 * This library is "approved for public release", and the
045 * <a href="http://www.dtic.mil/dtic/pdf/announcements/CopyrightGuidance.pdf">Copyright guidance</a>
046 * indicates that unless otherwise stated in the code, all FORTRAN functions in
047 * this library are license free. Since no such notice appears in the code these
048 * functions can safely be ported to Commons-Math.
049 * </p>
050 *
051 *
052 */
053public class Beta {
054    /** Maximum allowed numerical error. */
055    private static final double DEFAULT_EPSILON = 1E-14;
056
057    /** The constant value of ½log 2π. */
058    private static final double HALF_LOG_TWO_PI = .9189385332046727;
059
060    /**
061     * <p>
062     * The coefficients of the series expansion of the Δ function. This function
063     * is defined as follows
064     * </p>
065     * <center>Δ(x) = log Γ(x) - (x - 0.5) log a + a - 0.5 log 2π,</center>
066     * <p>
067     * see equation (23) in Didonato and Morris (1992). The series expansion,
068     * which applies for x ≥ 10, reads
069     * </p>
070     * <pre>
071     *                 14
072     *                ====
073     *             1  \                2 n
074     *     Δ(x) = ---  >    d  (10 / x)
075     *             x  /      n
076     *                ====
077     *                n = 0
078     * <pre>
079     */
080    private static final double[] DELTA = {
081        .833333333333333333333333333333E-01,
082        -.277777777777777777777777752282E-04,
083        .793650793650793650791732130419E-07,
084        -.595238095238095232389839236182E-09,
085        .841750841750832853294451671990E-11,
086        -.191752691751854612334149171243E-12,
087        .641025640510325475730918472625E-14,
088        -.295506514125338232839867823991E-15,
089        .179643716359402238723287696452E-16,
090        -.139228964661627791231203060395E-17,
091        .133802855014020915603275339093E-18,
092        -.154246009867966094273710216533E-19,
093        .197701992980957427278370133333E-20,
094        -.234065664793997056856992426667E-21,
095        .171348014966398575409015466667E-22
096    };
097
098    /**
099     * Default constructor.  Prohibit instantiation.
100     */
101    private Beta() {}
102
103    /**
104     * Returns the
105     * <a href="http://mathworld.wolfram.com/RegularizedBetaFunction.html">
106     * regularized beta function</a> I(x, a, b).
107     *
108     * @param x Value.
109     * @param a Parameter {@code a}.
110     * @param b Parameter {@code b}.
111     * @return the regularized beta function I(x, a, b).
112     * @throws org.apache.commons.math3.exception.MaxCountExceededException
113     * if the algorithm fails to converge.
114     */
115    public static double regularizedBeta(double x, double a, double b) {
116        return regularizedBeta(x, a, b, DEFAULT_EPSILON, Integer.MAX_VALUE);
117    }
118
119    /**
120     * Returns the
121     * <a href="http://mathworld.wolfram.com/RegularizedBetaFunction.html">
122     * regularized beta function</a> I(x, a, b).
123     *
124     * @param x Value.
125     * @param a Parameter {@code a}.
126     * @param b Parameter {@code b}.
127     * @param epsilon When the absolute value of the nth item in the
128     * series is less than epsilon the approximation ceases to calculate
129     * further elements in the series.
130     * @return the regularized beta function I(x, a, b)
131     * @throws org.apache.commons.math3.exception.MaxCountExceededException
132     * if the algorithm fails to converge.
133     */
134    public static double regularizedBeta(double x,
135                                         double a, double b,
136                                         double epsilon) {
137        return regularizedBeta(x, a, b, epsilon, Integer.MAX_VALUE);
138    }
139
140    /**
141     * Returns the regularized beta function I(x, a, b).
142     *
143     * @param x the value.
144     * @param a Parameter {@code a}.
145     * @param b Parameter {@code b}.
146     * @param maxIterations Maximum number of "iterations" to complete.
147     * @return the regularized beta function I(x, a, b)
148     * @throws org.apache.commons.math3.exception.MaxCountExceededException
149     * if the algorithm fails to converge.
150     */
151    public static double regularizedBeta(double x,
152                                         double a, double b,
153                                         int maxIterations) {
154        return regularizedBeta(x, a, b, DEFAULT_EPSILON, maxIterations);
155    }
156
157    /**
158     * Returns the regularized beta function I(x, a, b).
159     *
160     * The implementation of this method is based on:
161     * <ul>
162     * <li>
163     * <a href="http://mathworld.wolfram.com/RegularizedBetaFunction.html">
164     * Regularized Beta Function</a>.</li>
165     * <li>
166     * <a href="http://functions.wolfram.com/06.21.10.0001.01">
167     * Regularized Beta Function</a>.</li>
168     * </ul>
169     *
170     * @param x the value.
171     * @param a Parameter {@code a}.
172     * @param b Parameter {@code b}.
173     * @param epsilon When the absolute value of the nth item in the
174     * series is less than epsilon the approximation ceases to calculate
175     * further elements in the series.
176     * @param maxIterations Maximum number of "iterations" to complete.
177     * @return the regularized beta function I(x, a, b)
178     * @throws org.apache.commons.math3.exception.MaxCountExceededException
179     * if the algorithm fails to converge.
180     */
181    public static double regularizedBeta(double x,
182                                         final double a, final double b,
183                                         double epsilon, int maxIterations) {
184        double ret;
185
186        if (Double.isNaN(x) ||
187            Double.isNaN(a) ||
188            Double.isNaN(b) ||
189            x < 0 ||
190            x > 1 ||
191            a <= 0 ||
192            b <= 0) {
193            ret = Double.NaN;
194        } else if (x > (a + 1) / (2 + b + a) &&
195                   1 - x <= (b + 1) / (2 + b + a)) {
196            ret = 1 - regularizedBeta(1 - x, b, a, epsilon, maxIterations);
197        } else {
198            ContinuedFraction fraction = new ContinuedFraction() {
199
200                @Override
201                protected double getB(int n, double x) {
202                    double ret;
203                    double m;
204                    if (n % 2 == 0) { // even
205                        m = n / 2.0;
206                        ret = (m * (b - m) * x) /
207                            ((a + (2 * m) - 1) * (a + (2 * m)));
208                    } else {
209                        m = (n - 1.0) / 2.0;
210                        ret = -((a + m) * (a + b + m) * x) /
211                                ((a + (2 * m)) * (a + (2 * m) + 1.0));
212                    }
213                    return ret;
214                }
215
216                @Override
217                protected double getA(int n, double x) {
218                    return 1.0;
219                }
220            };
221            ret = FastMath.exp((a * FastMath.log(x)) + (b * FastMath.log1p(-x)) -
222                FastMath.log(a) - logBeta(a, b)) *
223                1.0 / fraction.evaluate(x, epsilon, maxIterations);
224        }
225
226        return ret;
227    }
228
229    /**
230     * Returns the natural logarithm of the beta function B(a, b).
231     *
232     * The implementation of this method is based on:
233     * <ul>
234     * <li><a href="http://mathworld.wolfram.com/BetaFunction.html">
235     * Beta Function</a>, equation (1).</li>
236     * </ul>
237     *
238     * @param a Parameter {@code a}.
239     * @param b Parameter {@code b}.
240     * @param epsilon This parameter is ignored.
241     * @param maxIterations This parameter is ignored.
242     * @return log(B(a, b)).
243     * @deprecated as of version 3.1, this method is deprecated as the
244     * computation of the beta function is no longer iterative; it will be
245     * removed in version 4.0. Current implementation of this method
246     * internally calls {@link #logBeta(double, double)}.
247     */
248    @Deprecated
249    public static double logBeta(double a, double b,
250                                 double epsilon,
251                                 int maxIterations) {
252
253        return logBeta(a, b);
254    }
255
256
257    /**
258     * Returns the value of log Γ(a + b) for 1 ≤ a, b ≤ 2. Based on the
259     * <em>NSWC Library of Mathematics Subroutines</em> double precision
260     * implementation, {@code DGSMLN}. In {@code BetaTest.testLogGammaSum()},
261     * this private method is accessed through reflection.
262     *
263     * @param a First argument.
264     * @param b Second argument.
265     * @return the value of {@code log(Gamma(a + b))}.
266     * @throws OutOfRangeException if {@code a} or {@code b} is lower than
267     * {@code 1.0} or greater than {@code 2.0}.
268     */
269    private static double logGammaSum(final double a, final double b)
270        throws OutOfRangeException {
271
272        if ((a < 1.0) || (a > 2.0)) {
273            throw new OutOfRangeException(a, 1.0, 2.0);
274        }
275        if ((b < 1.0) || (b > 2.0)) {
276            throw new OutOfRangeException(b, 1.0, 2.0);
277        }
278
279        final double x = (a - 1.0) + (b - 1.0);
280        if (x <= 0.5) {
281            return Gamma.logGamma1p(1.0 + x);
282        } else if (x <= 1.5) {
283            return Gamma.logGamma1p(x) + FastMath.log1p(x);
284        } else {
285            return Gamma.logGamma1p(x - 1.0) + FastMath.log(x * (1.0 + x));
286        }
287    }
288
289    /**
290     * Returns the value of log[Γ(b) / Γ(a + b)] for a ≥ 0 and b ≥ 10. Based on
291     * the <em>NSWC Library of Mathematics Subroutines</em> double precision
292     * implementation, {@code DLGDIV}. In
293     * {@code BetaTest.testLogGammaMinusLogGammaSum()}, this private method is
294     * accessed through reflection.
295     *
296     * @param a First argument.
297     * @param b Second argument.
298     * @return the value of {@code log(Gamma(b) / Gamma(a + b))}.
299     * @throws NumberIsTooSmallException if {@code a < 0.0} or {@code b < 10.0}.
300     */
301    private static double logGammaMinusLogGammaSum(final double a,
302                                                   final double b)
303        throws NumberIsTooSmallException {
304
305        if (a < 0.0) {
306            throw new NumberIsTooSmallException(a, 0.0, true);
307        }
308        if (b < 10.0) {
309            throw new NumberIsTooSmallException(b, 10.0, true);
310        }
311
312        /*
313         * d = a + b - 0.5
314         */
315        final double d;
316        final double w;
317        if (a <= b) {
318            d = b + (a - 0.5);
319            w = deltaMinusDeltaSum(a, b);
320        } else {
321            d = a + (b - 0.5);
322            w = deltaMinusDeltaSum(b, a);
323        }
324
325        final double u = d * FastMath.log1p(a / b);
326        final double v = a * (FastMath.log(b) - 1.0);
327
328        return u <= v ? (w - u) - v : (w - v) - u;
329    }
330
331    /**
332     * Returns the value of Δ(b) - Δ(a + b), with 0 ≤ a ≤ b and b ≥ 10. Based
333     * on equations (26), (27) and (28) in Didonato and Morris (1992).
334     *
335     * @param a First argument.
336     * @param b Second argument.
337     * @return the value of {@code Delta(b) - Delta(a + b)}
338     * @throws OutOfRangeException if {@code a < 0} or {@code a > b}
339     * @throws NumberIsTooSmallException if {@code b < 10}
340     */
341    private static double deltaMinusDeltaSum(final double a,
342                                             final double b)
343        throws OutOfRangeException, NumberIsTooSmallException {
344
345        if ((a < 0) || (a > b)) {
346            throw new OutOfRangeException(a, 0, b);
347        }
348        if (b < 10) {
349            throw new NumberIsTooSmallException(b, 10, true);
350        }
351
352        final double h = a / b;
353        final double p = h / (1.0 + h);
354        final double q = 1.0 / (1.0 + h);
355        final double q2 = q * q;
356        /*
357         * s[i] = 1 + q + ... - q**(2 * i)
358         */
359        final double[] s = new double[DELTA.length];
360        s[0] = 1.0;
361        for (int i = 1; i < s.length; i++) {
362            s[i] = 1.0 + (q + q2 * s[i - 1]);
363        }
364        /*
365         * w = Delta(b) - Delta(a + b)
366         */
367        final double sqrtT = 10.0 / b;
368        final double t = sqrtT * sqrtT;
369        double w = DELTA[DELTA.length - 1] * s[s.length - 1];
370        for (int i = DELTA.length - 2; i >= 0; i--) {
371            w = t * w + DELTA[i] * s[i];
372        }
373        return w * p / b;
374    }
375
376    /**
377     * Returns the value of Δ(p) + Δ(q) - Δ(p + q), with p, q ≥ 10. Based on
378     * the <em>NSWC Library of Mathematics Subroutines</em> double precision
379     * implementation, {@code DBCORR}. In
380     * {@code BetaTest.testSumDeltaMinusDeltaSum()}, this private method is
381     * accessed through reflection.
382     *
383     * @param p First argument.
384     * @param q Second argument.
385     * @return the value of {@code Delta(p) + Delta(q) - Delta(p + q)}.
386     * @throws NumberIsTooSmallException if {@code p < 10.0} or {@code q < 10.0}.
387     */
388    private static double sumDeltaMinusDeltaSum(final double p,
389                                                final double q) {
390
391        if (p < 10.0) {
392            throw new NumberIsTooSmallException(p, 10.0, true);
393        }
394        if (q < 10.0) {
395            throw new NumberIsTooSmallException(q, 10.0, true);
396        }
397
398        final double a = FastMath.min(p, q);
399        final double b = FastMath.max(p, q);
400        final double sqrtT = 10.0 / a;
401        final double t = sqrtT * sqrtT;
402        double z = DELTA[DELTA.length - 1];
403        for (int i = DELTA.length - 2; i >= 0; i--) {
404            z = t * z + DELTA[i];
405        }
406        return z / a + deltaMinusDeltaSum(a, b);
407    }
408
409    /**
410     * Returns the value of log B(p, q) for 0 ≤ x ≤ 1 and p, q > 0. Based on the
411     * <em>NSWC Library of Mathematics Subroutines</em> implementation,
412     * {@code DBETLN}.
413     *
414     * @param p First argument.
415     * @param q Second argument.
416     * @return the value of {@code log(Beta(p, q))}, {@code NaN} if
417     * {@code p <= 0} or {@code q <= 0}.
418     */
419    public static double logBeta(final double p, final double q) {
420        if (Double.isNaN(p) || Double.isNaN(q) || (p <= 0.0) || (q <= 0.0)) {
421            return Double.NaN;
422        }
423
424        final double a = FastMath.min(p, q);
425        final double b = FastMath.max(p, q);
426        if (a >= 10.0) {
427            final double w = sumDeltaMinusDeltaSum(a, b);
428            final double h = a / b;
429            final double c = h / (1.0 + h);
430            final double u = -(a - 0.5) * FastMath.log(c);
431            final double v = b * FastMath.log1p(h);
432            if (u <= v) {
433                return (((-0.5 * FastMath.log(b) + HALF_LOG_TWO_PI) + w) - u) - v;
434            } else {
435                return (((-0.5 * FastMath.log(b) + HALF_LOG_TWO_PI) + w) - v) - u;
436            }
437        } else if (a > 2.0) {
438            if (b > 1000.0) {
439                final int n = (int) FastMath.floor(a - 1.0);
440                double prod = 1.0;
441                double ared = a;
442                for (int i = 0; i < n; i++) {
443                    ared -= 1.0;
444                    prod *= ared / (1.0 + ared / b);
445                }
446                return (FastMath.log(prod) - n * FastMath.log(b)) +
447                        (Gamma.logGamma(ared) +
448                         logGammaMinusLogGammaSum(ared, b));
449            } else {
450                double prod1 = 1.0;
451                double ared = a;
452                while (ared > 2.0) {
453                    ared -= 1.0;
454                    final double h = ared / b;
455                    prod1 *= h / (1.0 + h);
456                }
457                if (b < 10.0) {
458                    double prod2 = 1.0;
459                    double bred = b;
460                    while (bred > 2.0) {
461                        bred -= 1.0;
462                        prod2 *= bred / (ared + bred);
463                    }
464                    return FastMath.log(prod1) +
465                           FastMath.log(prod2) +
466                           (Gamma.logGamma(ared) +
467                           (Gamma.logGamma(bred) -
468                            logGammaSum(ared, bred)));
469                } else {
470                    return FastMath.log(prod1) +
471                           Gamma.logGamma(ared) +
472                           logGammaMinusLogGammaSum(ared, b);
473                }
474            }
475        } else if (a >= 1.0) {
476            if (b > 2.0) {
477                if (b < 10.0) {
478                    double prod = 1.0;
479                    double bred = b;
480                    while (bred > 2.0) {
481                        bred -= 1.0;
482                        prod *= bred / (a + bred);
483                    }
484                    return FastMath.log(prod) +
485                           (Gamma.logGamma(a) +
486                            (Gamma.logGamma(bred) -
487                             logGammaSum(a, bred)));
488                } else {
489                    return Gamma.logGamma(a) +
490                           logGammaMinusLogGammaSum(a, b);
491                }
492            } else {
493                return Gamma.logGamma(a) +
494                       Gamma.logGamma(b) -
495                       logGammaSum(a, b);
496            }
497        } else {
498            if (b >= 10.0) {
499                return Gamma.logGamma(a) +
500                       logGammaMinusLogGammaSum(a, b);
501            } else {
502                // The following command is the original NSWC implementation.
503                // return Gamma.logGamma(a) +
504                // (Gamma.logGamma(b) - Gamma.logGamma(a + b));
505                // The following command turns out to be more accurate.
506                return FastMath.log(Gamma.gamma(a) * Gamma.gamma(b) /
507                                    Gamma.gamma(a + b));
508            }
509        }
510    }
511}