Blame cdf/gammainv.c

Packit 67cb25
/* cdf/gammainv.c
Packit 67cb25
 * 
Packit 67cb25
 * Copyright (C) 2003, 2007 Brian Gough
Packit 67cb25
 * 
Packit 67cb25
 * This program is free software; you can redistribute it and/or modify
Packit 67cb25
 * it under the terms of the GNU General Public License as published by
Packit 67cb25
 * the Free Software Foundation; either version 3 of the License, or (at
Packit 67cb25
 * your option) any later version.
Packit 67cb25
 * 
Packit 67cb25
 * This program is distributed in the hope that it will be useful, but
Packit 67cb25
 * WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 67cb25
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 67cb25
 * General Public License for more details.
Packit 67cb25
 * 
Packit 67cb25
 * You should have received a copy of the GNU General Public License
Packit 67cb25
 * along with this program; if not, write to the Free Software Foundation,
Packit 67cb25
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Packit 67cb25
 */
Packit 67cb25
Packit 67cb25
#include <config.h>
Packit 67cb25
#include <math.h>
Packit 67cb25
#include <gsl/gsl_cdf.h>
Packit 67cb25
#include <gsl/gsl_math.h>
Packit 67cb25
#include <gsl/gsl_randist.h>
Packit 67cb25
#include <gsl/gsl_sf_gamma.h>
Packit 67cb25
Packit 67cb25
#include <stdio.h>
Packit 67cb25
Packit 67cb25
double
Packit 67cb25
gsl_cdf_gamma_Pinv (const double P, const double a, const double b)
Packit 67cb25
{
Packit 67cb25
  double x;
Packit 67cb25
Packit 67cb25
  if (P == 1.0)
Packit 67cb25
    {
Packit 67cb25
      return GSL_POSINF;
Packit 67cb25
    }
Packit 67cb25
  else if (P == 0.0)
Packit 67cb25
    {
Packit 67cb25
      return 0.0;
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  /* Consider, small, large and intermediate cases separately.  The
Packit 67cb25
     boundaries at 0.05 and 0.95 have not been optimised, but seem ok
Packit 67cb25
     for an initial approximation.
Packit 67cb25
Packit 67cb25
     BJG: These approximations aren't really valid, the relevant
Packit 67cb25
     criterion is P*gamma(a+1) < 1. Need to rework these routines and
Packit 67cb25
     use a single bisection style solver for all the inverse
Packit 67cb25
     functions.
Packit 67cb25
  */
Packit 67cb25
Packit 67cb25
  if (P < 0.05)
Packit 67cb25
    {
Packit 67cb25
      double x0 = exp ((gsl_sf_lngamma (a) + log (P)) / a);
Packit 67cb25
      x = x0;
Packit 67cb25
    }
Packit 67cb25
  else if (P > 0.95)
Packit 67cb25
    {
Packit 67cb25
      double x0 = -log1p (-P) + gsl_sf_lngamma (a);
Packit 67cb25
      x = x0;
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      double xg = gsl_cdf_ugaussian_Pinv (P);
Packit 67cb25
      double x0 = (xg < -0.5*sqrt (a)) ? a : sqrt (a) * xg + a;
Packit 67cb25
      x = x0;
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  /* Use Lagrange's interpolation for E(x)/phi(x0) to work backwards
Packit 67cb25
     to an improved value of x (Abramowitz & Stegun, 3.6.6) 
Packit 67cb25
Packit 67cb25
     where E(x)=P-integ(phi(u),u,x0,x) and phi(u) is the pdf.
Packit 67cb25
   */
Packit 67cb25
Packit 67cb25
  {
Packit 67cb25
    double lambda, dP, phi;
Packit 67cb25
    unsigned int n = 0;
Packit 67cb25
Packit 67cb25
  start:
Packit 67cb25
    dP = P - gsl_cdf_gamma_P (x, a, 1.0);
Packit 67cb25
    phi = gsl_ran_gamma_pdf (x, a, 1.0);
Packit 67cb25
Packit 67cb25
    if (dP == 0.0 || n++ > 32)
Packit 67cb25
      goto end;
Packit 67cb25
Packit 67cb25
    lambda = dP / GSL_MAX (2 * fabs (dP / x), phi);
Packit 67cb25
Packit 67cb25
    {
Packit 67cb25
      double step0 = lambda;
Packit 67cb25
      double step1 = -((a - 1) / x - 1) * lambda * lambda / 4.0;
Packit 67cb25
Packit 67cb25
      double step = step0;
Packit 67cb25
      if (fabs (step1) < 0.5 * fabs (step0))
Packit 67cb25
        step += step1;
Packit 67cb25
Packit 67cb25
      if (x + step > 0)
Packit 67cb25
        x += step;
Packit 67cb25
      else
Packit 67cb25
        {
Packit 67cb25
          x /= 2.0;
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      if (fabs (step0) > 1e-10 * x || fabs(step0 * phi) > 1e-10 * P)
Packit 67cb25
        goto start;
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  end:
Packit 67cb25
    if (fabs(dP) > GSL_SQRT_DBL_EPSILON * P)
Packit 67cb25
      {
Packit 67cb25
        GSL_ERROR_VAL("inverse failed to converge", GSL_EFAILED, GSL_NAN);
Packit 67cb25
      }
Packit 67cb25
    
Packit 67cb25
    return b * x;
Packit 67cb25
  }
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
double
Packit 67cb25
gsl_cdf_gamma_Qinv (const double Q, const double a, const double b)
Packit 67cb25
{
Packit 67cb25
  double x;
Packit 67cb25
Packit 67cb25
  if (Q == 1.0)
Packit 67cb25
    {
Packit 67cb25
      return 0.0;
Packit 67cb25
    }
Packit 67cb25
  else if (Q == 0.0)
Packit 67cb25
    {
Packit 67cb25
      return GSL_POSINF;
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  /* Consider, small, large and intermediate cases separately.  The
Packit 67cb25
     boundaries at 0.05 and 0.95 have not been optimised, but seem ok
Packit 67cb25
     for an initial approximation. */
Packit 67cb25
Packit 67cb25
  if (Q < 0.05)
Packit 67cb25
    {
Packit 67cb25
      double x0 = -log (Q) + gsl_sf_lngamma (a);
Packit 67cb25
      x = x0;
Packit 67cb25
    }
Packit 67cb25
  else if (Q > 0.95)
Packit 67cb25
    {
Packit 67cb25
      double x0 = exp ((gsl_sf_lngamma (a) + log1p (-Q)) / a);
Packit 67cb25
      x = x0;
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      double xg = gsl_cdf_ugaussian_Qinv (Q);
Packit 67cb25
      double x0 = (xg < -0.5*sqrt (a)) ? a : sqrt (a) * xg + a;
Packit 67cb25
      x = x0;
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  /* Use Lagrange's interpolation for E(x)/phi(x0) to work backwards
Packit 67cb25
     to an improved value of x (Abramowitz & Stegun, 3.6.6) 
Packit 67cb25
Packit 67cb25
     where E(x)=P-integ(phi(u),u,x0,x) and phi(u) is the pdf.
Packit 67cb25
   */
Packit 67cb25
Packit 67cb25
  {
Packit 67cb25
    double lambda, dQ, phi;
Packit 67cb25
    unsigned int n = 0;
Packit 67cb25
Packit 67cb25
  start:
Packit 67cb25
    dQ = Q - gsl_cdf_gamma_Q (x, a, 1.0);
Packit 67cb25
    phi = gsl_ran_gamma_pdf (x, a, 1.0);
Packit 67cb25
Packit 67cb25
    if (dQ == 0.0 || n++ > 32)
Packit 67cb25
      goto end;
Packit 67cb25
Packit 67cb25
    lambda = -dQ / GSL_MAX (2 * fabs (dQ / x), phi);
Packit 67cb25
Packit 67cb25
    {
Packit 67cb25
      double step0 = lambda;
Packit 67cb25
      double step1 = -((a - 1) / x - 1) * lambda * lambda / 4.0;
Packit 67cb25
Packit 67cb25
      double step = step0;
Packit 67cb25
      if (fabs (step1) < 0.5 * fabs (step0))
Packit 67cb25
        step += step1;
Packit 67cb25
Packit 67cb25
      if (x + step > 0)
Packit 67cb25
        x += step;
Packit 67cb25
      else
Packit 67cb25
        {
Packit 67cb25
          x /= 2.0;
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      if (fabs (step0) > 1e-10 * x)
Packit 67cb25
        goto start;
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  }
Packit 67cb25
Packit 67cb25
end:
Packit 67cb25
  return b * x;
Packit 67cb25
}