FDistribution.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 org.apache.commons.math3.exception.NotStrictlyPositiveException;
  19. import org.apache.commons.math3.exception.util.LocalizedFormats;
  20. import org.apache.commons.math3.random.RandomGenerator;
  21. import org.apache.commons.math3.random.Well19937c;
  22. import org.apache.commons.math3.special.Beta;
  23. import org.apache.commons.math3.util.FastMath;

  24. /**
  25.  * Implementation of the F-distribution.
  26.  *
  27.  * @see <a href="http://en.wikipedia.org/wiki/F-distribution">F-distribution (Wikipedia)</a>
  28.  * @see <a href="http://mathworld.wolfram.com/F-Distribution.html">F-distribution (MathWorld)</a>
  29.  */
  30. public class FDistribution extends AbstractRealDistribution {
  31.     /**
  32.      * Default inverse cumulative probability accuracy.
  33.      * @since 2.1
  34.      */
  35.     public static final double DEFAULT_INVERSE_ABSOLUTE_ACCURACY = 1e-9;
  36.     /** Serializable version identifier. */
  37.     private static final long serialVersionUID = -8516354193418641566L;
  38.     /** The numerator degrees of freedom. */
  39.     private final double numeratorDegreesOfFreedom;
  40.     /** The numerator degrees of freedom. */
  41.     private final double denominatorDegreesOfFreedom;
  42.     /** Inverse cumulative probability accuracy. */
  43.     private final double solverAbsoluteAccuracy;
  44.     /** Cached numerical variance */
  45.     private double numericalVariance = Double.NaN;
  46.     /** Whether or not the numerical variance has been calculated */
  47.     private boolean numericalVarianceIsCalculated = false;

  48.     /**
  49.      * Creates an F distribution using the given degrees of freedom.
  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 numeratorDegreesOfFreedom Numerator degrees of freedom.
  59.      * @param denominatorDegreesOfFreedom Denominator degrees of freedom.
  60.      * @throws NotStrictlyPositiveException if
  61.      * {@code numeratorDegreesOfFreedom <= 0} or
  62.      * {@code denominatorDegreesOfFreedom <= 0}.
  63.      */
  64.     public FDistribution(double numeratorDegreesOfFreedom,
  65.                          double denominatorDegreesOfFreedom)
  66.         throws NotStrictlyPositiveException {
  67.         this(numeratorDegreesOfFreedom, denominatorDegreesOfFreedom,
  68.              DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
  69.     }

  70.     /**
  71.      * Creates an F distribution using the given degrees of freedom
  72.      * and inverse cumulative probability accuracy.
  73.      * <p>
  74.      * <b>Note:</b> this constructor will implicitly create an instance of
  75.      * {@link Well19937c} as random generator to be used for sampling only (see
  76.      * {@link #sample()} and {@link #sample(int)}). In case no sampling is
  77.      * needed for the created distribution, it is advised to pass {@code null}
  78.      * as random generator via the appropriate constructors to avoid the
  79.      * additional initialisation overhead.
  80.      *
  81.      * @param numeratorDegreesOfFreedom Numerator degrees of freedom.
  82.      * @param denominatorDegreesOfFreedom Denominator degrees of freedom.
  83.      * @param inverseCumAccuracy the maximum absolute error in inverse
  84.      * cumulative probability estimates.
  85.      * @throws NotStrictlyPositiveException if
  86.      * {@code numeratorDegreesOfFreedom <= 0} or
  87.      * {@code denominatorDegreesOfFreedom <= 0}.
  88.      * @since 2.1
  89.      */
  90.     public FDistribution(double numeratorDegreesOfFreedom,
  91.                          double denominatorDegreesOfFreedom,
  92.                          double inverseCumAccuracy)
  93.         throws NotStrictlyPositiveException {
  94.         this(new Well19937c(), numeratorDegreesOfFreedom,
  95.              denominatorDegreesOfFreedom, inverseCumAccuracy);
  96.     }

  97.     /**
  98.      * Creates an F distribution.
  99.      *
  100.      * @param rng Random number generator.
  101.      * @param numeratorDegreesOfFreedom Numerator degrees of freedom.
  102.      * @param denominatorDegreesOfFreedom Denominator degrees of freedom.
  103.      * @throws NotStrictlyPositiveException if {@code numeratorDegreesOfFreedom <= 0} or
  104.      * {@code denominatorDegreesOfFreedom <= 0}.
  105.      * @since 3.3
  106.      */
  107.     public FDistribution(RandomGenerator rng,
  108.                          double numeratorDegreesOfFreedom,
  109.                          double denominatorDegreesOfFreedom)
  110.         throws NotStrictlyPositiveException {
  111.         this(rng, numeratorDegreesOfFreedom, denominatorDegreesOfFreedom, DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
  112.     }

  113.     /**
  114.      * Creates an F distribution.
  115.      *
  116.      * @param rng Random number generator.
  117.      * @param numeratorDegreesOfFreedom Numerator degrees of freedom.
  118.      * @param denominatorDegreesOfFreedom Denominator degrees of freedom.
  119.      * @param inverseCumAccuracy the maximum absolute error in inverse
  120.      * cumulative probability estimates.
  121.      * @throws NotStrictlyPositiveException if {@code numeratorDegreesOfFreedom <= 0} or
  122.      * {@code denominatorDegreesOfFreedom <= 0}.
  123.      * @since 3.1
  124.      */
  125.     public FDistribution(RandomGenerator rng,
  126.                          double numeratorDegreesOfFreedom,
  127.                          double denominatorDegreesOfFreedom,
  128.                          double inverseCumAccuracy)
  129.         throws NotStrictlyPositiveException {
  130.         super(rng);

  131.         if (numeratorDegreesOfFreedom <= 0) {
  132.             throw new NotStrictlyPositiveException(LocalizedFormats.DEGREES_OF_FREEDOM,
  133.                                                    numeratorDegreesOfFreedom);
  134.         }
  135.         if (denominatorDegreesOfFreedom <= 0) {
  136.             throw new NotStrictlyPositiveException(LocalizedFormats.DEGREES_OF_FREEDOM,
  137.                                                    denominatorDegreesOfFreedom);
  138.         }
  139.         this.numeratorDegreesOfFreedom = numeratorDegreesOfFreedom;
  140.         this.denominatorDegreesOfFreedom = denominatorDegreesOfFreedom;
  141.         solverAbsoluteAccuracy = inverseCumAccuracy;
  142.     }

  143.     /**
  144.      * {@inheritDoc}
  145.      *
  146.      * @since 2.1
  147.      */
  148.     public double density(double x) {
  149.         return FastMath.exp(logDensity(x));
  150.     }

  151.     /** {@inheritDoc} **/
  152.     @Override
  153.     public double logDensity(double x) {
  154.         final double nhalf = numeratorDegreesOfFreedom / 2;
  155.         final double mhalf = denominatorDegreesOfFreedom / 2;
  156.         final double logx = FastMath.log(x);
  157.         final double logn = FastMath.log(numeratorDegreesOfFreedom);
  158.         final double logm = FastMath.log(denominatorDegreesOfFreedom);
  159.         final double lognxm = FastMath.log(numeratorDegreesOfFreedom * x +
  160.                 denominatorDegreesOfFreedom);
  161.         return nhalf * logn + nhalf * logx - logx +
  162.                mhalf * logm - nhalf * lognxm - mhalf * lognxm -
  163.                Beta.logBeta(nhalf, mhalf);
  164.     }

  165.     /**
  166.      * {@inheritDoc}
  167.      *
  168.      * The implementation of this method is based on
  169.      * <ul>
  170.      *  <li>
  171.      *   <a href="http://mathworld.wolfram.com/F-Distribution.html">
  172.      *   F-Distribution</a>, equation (4).
  173.      *  </li>
  174.      * </ul>
  175.      */
  176.     public double cumulativeProbability(double x)  {
  177.         double ret;
  178.         if (x <= 0) {
  179.             ret = 0;
  180.         } else {
  181.             double n = numeratorDegreesOfFreedom;
  182.             double m = denominatorDegreesOfFreedom;

  183.             ret = Beta.regularizedBeta((n * x) / (m + n * x),
  184.                 0.5 * n,
  185.                 0.5 * m);
  186.         }
  187.         return ret;
  188.     }

  189.     /**
  190.      * Access the numerator degrees of freedom.
  191.      *
  192.      * @return the numerator degrees of freedom.
  193.      */
  194.     public double getNumeratorDegreesOfFreedom() {
  195.         return numeratorDegreesOfFreedom;
  196.     }

  197.     /**
  198.      * Access the denominator degrees of freedom.
  199.      *
  200.      * @return the denominator degrees of freedom.
  201.      */
  202.     public double getDenominatorDegreesOfFreedom() {
  203.         return denominatorDegreesOfFreedom;
  204.     }

  205.     /** {@inheritDoc} */
  206.     @Override
  207.     protected double getSolverAbsoluteAccuracy() {
  208.         return solverAbsoluteAccuracy;
  209.     }

  210.     /**
  211.      * {@inheritDoc}
  212.      *
  213.      * For denominator degrees of freedom parameter {@code b}, the mean is
  214.      * <ul>
  215.      *  <li>if {@code b > 2} then {@code b / (b - 2)},</li>
  216.      *  <li>else undefined ({@code Double.NaN}).
  217.      * </ul>
  218.      */
  219.     public double getNumericalMean() {
  220.         final double denominatorDF = getDenominatorDegreesOfFreedom();

  221.         if (denominatorDF > 2) {
  222.             return denominatorDF / (denominatorDF - 2);
  223.         }

  224.         return Double.NaN;
  225.     }

  226.     /**
  227.      * {@inheritDoc}
  228.      *
  229.      * For numerator degrees of freedom parameter {@code a} and denominator
  230.      * degrees of freedom parameter {@code b}, the variance is
  231.      * <ul>
  232.      *  <li>
  233.      *    if {@code b > 4} then
  234.      *    {@code [2 * b^2 * (a + b - 2)] / [a * (b - 2)^2 * (b - 4)]},
  235.      *  </li>
  236.      *  <li>else undefined ({@code Double.NaN}).
  237.      * </ul>
  238.      */
  239.     public double getNumericalVariance() {
  240.         if (!numericalVarianceIsCalculated) {
  241.             numericalVariance = calculateNumericalVariance();
  242.             numericalVarianceIsCalculated = true;
  243.         }
  244.         return numericalVariance;
  245.     }

  246.     /**
  247.      * used by {@link #getNumericalVariance()}
  248.      *
  249.      * @return the variance of this distribution
  250.      */
  251.     protected double calculateNumericalVariance() {
  252.         final double denominatorDF = getDenominatorDegreesOfFreedom();

  253.         if (denominatorDF > 4) {
  254.             final double numeratorDF = getNumeratorDegreesOfFreedom();
  255.             final double denomDFMinusTwo = denominatorDF - 2;

  256.             return ( 2 * (denominatorDF * denominatorDF) * (numeratorDF + denominatorDF - 2) ) /
  257.                    ( (numeratorDF * (denomDFMinusTwo * denomDFMinusTwo) * (denominatorDF - 4)) );
  258.         }

  259.         return Double.NaN;
  260.     }

  261.     /**
  262.      * {@inheritDoc}
  263.      *
  264.      * The lower bound of the support is always 0 no matter the parameters.
  265.      *
  266.      * @return lower bound of the support (always 0)
  267.      */
  268.     public double getSupportLowerBound() {
  269.         return 0;
  270.     }

  271.     /**
  272.      * {@inheritDoc}
  273.      *
  274.      * The upper bound of the support is always positive infinity
  275.      * no matter the parameters.
  276.      *
  277.      * @return upper bound of the support (always Double.POSITIVE_INFINITY)
  278.      */
  279.     public double getSupportUpperBound() {
  280.         return Double.POSITIVE_INFINITY;
  281.     }

  282.     /** {@inheritDoc} */
  283.     public boolean isSupportLowerBoundInclusive() {
  284.         return false;
  285.     }

  286.     /** {@inheritDoc} */
  287.     public boolean isSupportUpperBoundInclusive() {
  288.         return false;
  289.     }

  290.     /**
  291.      * {@inheritDoc}
  292.      *
  293.      * The support of this distribution is connected.
  294.      *
  295.      * @return {@code true}
  296.      */
  297.     public boolean isSupportConnected() {
  298.         return true;
  299.     }
  300. }