diff PsiCLASS-1.0.2/stats.cpp @ 0:903fc43d6227 draft default tip

Uploaded
author lsong10
date Fri, 26 Mar 2021 16:52:45 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/PsiCLASS-1.0.2/stats.cpp	Fri Mar 26 16:52:45 2021 +0000
@@ -0,0 +1,580 @@
+#include "stats.hpp"
+
+/** The digamma function in long double precision.
+* @param x the real value of the argument
+* @return the value of the digamma (psi) function at that point
+* @author Richard J. Mathar
+* @since 2005-11-24
+*/
+long double digammal(long double x) 
+{
+	/* force into the interval 1..3 */
+	if( x < 0.0L )
+		return digammal(1.0L-x)+M_PIl/tanl(M_PIl*(1.0L-x)) ;	/* reflection formula */
+	else if( x < 1.0L )
+		return digammal(1.0L+x)-1.0L/x ;
+	else if ( x == 1.0L)
+		return -M_GAMMAl ;
+	else if ( x == 2.0L)
+		return 1.0L-M_GAMMAl ;
+	else if ( x == 3.0L)
+		return 1.5L-M_GAMMAl ;
+	else if ( x > 3.0L)
+		/* duplication formula */
+		return 0.5L*(digammal(x/2.0L)+digammal((x+1.0L)/2.0L))+M_LN2l ;
+	else
+	{
+		/* Just for your information, the following lines contain
+		* the Maple source code to re-generate the table that is
+		* eventually becoming the Kncoe[] array below
+		* interface(prettyprint=0) :
+		* Digits := 63 :
+		* r := 0 :
+		* 
+		* for l from 1 to 60 do
+		* 	d := binomial(-1/2,l) :
+		* 	r := r+d*(-1)^l*(Zeta(2*l+1) -1) ;
+		* 	evalf(r) ;
+		* 	print(%,evalf(1+Psi(1)-r)) ;
+		*o d :
+		* 
+		* for N from 1 to 28 do
+		* 	r := 0 :
+		* 	n := N-1 :
+		*
+ 		*	for l from iquo(n+3,2) to 70 do
+		*		d := 0 :
+ 		*		for s from 0 to n+1 do
+ 		*		 d := d+(-1)^s*binomial(n+1,s)*binomial((s-1)/2,l) :
+ 		*		od :
+ 		*		if 2*l-n > 1 then
+ 		*		r := r+d*(-1)^l*(Zeta(2*l-n) -1) :
+ 		*		fi :
+ 		*	od :
+ 		*	print(evalf((-1)^n*2*r)) ;
+ 		*od :
+ 		*quit :
+		*/
+		static long double Kncoe[] = { .30459198558715155634315638246624251L,
+		.72037977439182833573548891941219706L, -.12454959243861367729528855995001087L,
+		.27769457331927827002810119567456810e-1L, -.67762371439822456447373550186163070e-2L,
+		.17238755142247705209823876688592170e-2L, -.44817699064252933515310345718960928e-3L,
+		.11793660000155572716272710617753373e-3L, -.31253894280980134452125172274246963e-4L,
+		.83173997012173283398932708991137488e-5L, -.22191427643780045431149221890172210e-5L,
+		.59302266729329346291029599913617915e-6L, -.15863051191470655433559920279603632e-6L,
+		.42459203983193603241777510648681429e-7L, -.11369129616951114238848106591780146e-7L,
+		.304502217295931698401459168423403510e-8L, -.81568455080753152802915013641723686e-9L,
+		.21852324749975455125936715817306383e-9L, -.58546491441689515680751900276454407e-10L,
+		.15686348450871204869813586459513648e-10L, -.42029496273143231373796179302482033e-11L,
+		.11261435719264907097227520956710754e-11L, -.30174353636860279765375177200637590e-12L,
+		.80850955256389526647406571868193768e-13L, -.21663779809421233144009565199997351e-13L,
+		.58047634271339391495076374966835526e-14L, -.15553767189204733561108869588173845e-14L,
+		.41676108598040807753707828039353330e-15L, -.11167065064221317094734023242188463e-15L } ;
+
+		register long double Tn_1 = 1.0L ;	/* T_{n-1}(x), started at n=1 */
+		register long double Tn = x-2.0L ;	/* T_{n}(x) , started at n=1 */
+		register long double resul = Kncoe[0] + Kncoe[1]*Tn ;
+
+		x -= 2.0L ;
+		int n ;
+
+		for( n = 2 ; n < int( sizeof(Kncoe)/sizeof(long double) ) ; n++)
+		{
+			const long double Tn1 = 2.0L * x * Tn - Tn_1 ;	/* Chebyshev recursion, Eq. 22.7.4 Abramowitz-Stegun */
+			resul += Kncoe[n]*Tn1 ;
+			Tn_1 = Tn ;
+			Tn = Tn1 ;
+		}
+		return resul ;
+	}
+}
+
+
+
+double trigamma ( double x, int *ifault )
+
+//****************************************************************************
+//  purpose:
+//
+//    trigamma calculates trigamma(x) = d**2 log(gamma(x)) / dx**2
+//
+//  licensing:
+//
+//    this code is distributed under the gnu lgpl license. 
+//
+//  modified:
+//
+//    19 january 2008
+//
+//  author:
+//
+//    original fortran77 version by be schneider.
+//    c++ version by john burkardt.
+//
+//  reference:
+//
+//    be schneider,
+//    algorithm as 121:
+//    trigamma function,
+//    applied statistics,
+//    volume 27, number 1, pages 97-99, 1978.
+//
+//  parameters:
+//
+//    input, double x, the argument of the trigamma function.
+//    0 < x.
+//
+//    output, int *ifault, error flag.
+//    0, no error.
+//    1, x <= 0.
+//
+//    output, double trigamma, the value of the trigamma function at x.
+//
+{
+	double a = 0.0001;
+	double b = 5.0;
+	double b2 =  0.1666666667;
+	double b4 = -0.03333333333;
+	double b6 =  0.02380952381;
+	double b8 = -0.03333333333;
+	double value;
+	double y;
+	double z;
+	//
+	//  check the input.
+	//
+	if ( x <= 0.0 )
+	{
+		*ifault = 1;
+		value = 0.0;
+		return value;
+	}
+
+	*ifault = 0;
+	z = x;
+	//
+	//  use small value approximation if x <= a.
+	//
+	if ( x <= a )
+	{
+		value = 1.0 / x / x;
+		return value;
+	}
+	//
+	//  increase argument to ( x + i ) >= b.
+	//
+	value = 0.0;
+
+	while ( z < b )
+	{
+		value = value + 1.0 / z / z;
+		z = z + 1.0;
+	}
+	//
+	//  apply asymptotic formula if argument is b or greater.
+	//
+	y = 1.0 / z / z;
+
+	value = value + 0.5 *
+		y + ( 1.0 + y * ( b2+ y * ( b4 + y * ( b6+ y * b8 )))) / z;
+
+	return value;
+}
+
+
+double LogGammaDensity( double x, double k, double theta )
+{
+	return -k * log( theta ) + ( k - 1 ) * log( x ) - x / theta - lgamma( k ) ;
+}
+
+double MixtureGammaAssignment( double x, double pi, double* k, double *theta )
+{
+	if ( pi == 1 )
+		return 1 ;
+	else if ( pi == 0 )
+		return 0 ;
+
+	double lf0 = LogGammaDensity( x, k[0], theta[0] ) ;
+	double lf1 = LogGammaDensity( x, k[1], theta[1] ) ; 
+	return (double)1.0 / ( 1.0 + exp( lf1 + log( 1 - pi ) - lf0 - log( pi ) ) ) ;
+}
+
+//****************************************************************************80
+
+double alnorm ( double x, bool upper )
+
+//****************************************************************************80
+//
+//  Purpose:
+//
+//    ALNORM computes the cumulative density of the standard normal distribution.
+//
+//  Licensing:
+//
+//    This code is distributed under the GNU LGPL license. 
+//
+//  Modified:
+//
+//    17 January 2008
+//
+//  Author:
+//
+//    Original FORTRAN77 version by David Hill.
+//    C++ version by John Burkardt.
+//
+//  Reference:
+//
+//    David Hill,
+//    Algorithm AS 66:
+//    The Normal Integral,
+//    Applied Statistics,
+//    Volume 22, Number 3, 1973, pages 424-427.
+//
+//  Parameters:
+//
+//    Input, double X, is one endpoint of the semi-infinite interval
+//    over which the integration takes place.
+//
+//    Input, bool UPPER, determines whether the upper or lower
+//    interval is to be integrated:
+//    .TRUE.  => integrate from X to + Infinity;
+//    .FALSE. => integrate from - Infinity to X.
+//
+//    Output, double ALNORM, the integral of the standard normal
+//    distribution over the desired interval.
+//
+{
+	double a1 = 5.75885480458;
+	double a2 = 2.62433121679;
+	double a3 = 5.92885724438;
+	double b1 = -29.8213557807;
+	double b2 = 48.6959930692;
+	double c1 = -0.000000038052;
+	double c2 = 0.000398064794;
+	double c3 = -0.151679116635;
+	double c4 = 4.8385912808;
+	double c5 = 0.742380924027;
+	double c6 = 3.99019417011;
+	double con = 1.28;
+	double d1 = 1.00000615302;
+	double d2 = 1.98615381364;
+	double d3 = 5.29330324926;
+	double d4 = -15.1508972451;
+	double d5 = 30.789933034;
+	double ltone = 7.0;
+	double p = 0.398942280444;
+	double q = 0.39990348504;
+	double r = 0.398942280385;
+	bool up;
+	double utzero = 18.66;
+	double value;
+	double y;
+	double z;
+
+	up = upper;
+	z = x;
+
+	if ( z < 0.0 )
+	{
+		up = !up;
+		z = - z;
+	}
+
+	if ( ltone < z && ( ( !up ) || utzero < z ) )
+	{
+		if ( up )
+		{
+			value = 0.0;
+		}
+		else
+		{
+			value = 1.0;
+		}
+		return value;
+	}
+
+	y = 0.5 * z * z;
+
+	if ( z <= con )
+	{
+		value = 0.5 - z * ( p - q * y 
+				/ ( y + a1 + b1 
+				/ ( y + a2 + b2 
+				/ ( y + a3 ))));
+	}
+	else
+	{
+		value = r * exp ( - y ) 
+			/ ( z + c1 + d1 
+			/ ( z + c2 + d2 
+			/ ( z + c3 + d3 
+			/ ( z + c4 + d4 
+			/ ( z + c5 + d5 
+			/ ( z + c6 ))))));
+	}
+
+	if ( !up )
+	{
+		value = 1.0 - value;
+	}
+
+	return value;
+}
+
+//****************************************************************************80
+
+double gammad ( double x, double p, int *ifault )
+
+//****************************************************************************80
+//
+//  Purpose:
+//
+//    GAMMAD computes the Incomplete Gamma Integral
+//
+//  Licensing:
+//
+//    This code is distributed under the GNU LGPL license. 
+//
+//  Modified:
+//
+//    20 January 2008
+//
+//  Author:
+//
+//    Original FORTRAN77 version by B Shea.
+//    C++ version by John Burkardt.
+//
+//  Reference:
+//
+//    B Shea,
+//    Algorithm AS 239:
+//    Chi-squared and Incomplete Gamma Integral,
+//    Applied Statistics,
+//    Volume 37, Number 3, 1988, pages 466-473.
+//
+//  Parameters:
+//
+//    Input, double X, P, the parameters of the incomplete 
+//    gamma ratio.  0 <= X, and 0 < P.
+//
+//    Output, int IFAULT, error flag.
+//    0, no error.
+//    1, X < 0 or P <= 0.
+//
+//    Output, double GAMMAD, the value of the incomplete 
+//    Gamma integral.
+//
+{
+	double a;
+	double an;
+	double arg;
+	double b;
+	double c;
+	double elimit = - 88.0;
+	double oflo = 1.0E+37;
+	double plimit = 1000.0;
+	double pn1;
+	double pn2;
+	double pn3;
+	double pn4;
+	double pn5;
+	double pn6;
+	double rn;
+	double tol = 1.0E-14;
+	bool upper;
+	double value;
+	double xbig = 1.0E+08;
+
+	value = 0.0;
+	//
+	//  Check the input.
+	//
+	if ( x < 0.0 )
+	{
+		*ifault = 1;
+		return value;
+	}
+
+	if ( p <= 0.0 )
+	{
+		*ifault = 1;
+		return value;
+	}
+
+	*ifault = 0;
+
+	if ( x == 0.0 )
+	{
+		value = 0.0;
+		return value;
+	}
+	//
+	//  If P is large, use a normal approximation.
+	//
+	if ( plimit < p )
+	{
+		pn1 = 3.0 * sqrt ( p ) * ( pow ( x / p, 1.0 / 3.0 ) 
+				+ 1.0 / ( 9.0 * p ) - 1.0 );
+
+		upper = false;
+		value = alnorm ( pn1, upper );
+		return value;
+	}
+	//
+	//  If X is large set value = 1.
+	//
+	if ( xbig < x )
+	{
+		value = 1.0;
+		return value;
+	}
+	//
+	//  Use Pearson's series expansion.
+	//  (Note that P is not large enough to force overflow in ALOGAM).
+	//  No need to test IFAULT on exit since P > 0.
+	//
+	if ( x <= 1.0 || x < p )
+	{
+		arg = p * log ( x ) - x - lgamma ( p + 1.0 );
+		c = 1.0;
+		value = 1.0;
+		a = p;
+
+		for ( ; ; )
+		{
+			a = a + 1.0;
+			c = c * x / a;
+			value = value + c;
+
+			if ( c <= tol )
+			{
+				break;
+			}
+		}
+
+		arg = arg + log ( value );
+
+		if ( elimit <= arg )
+		{
+			value = exp ( arg );
+		}
+		else
+		{
+			value = 0.0;
+		}
+	}
+	//
+	//  Use a continued fraction expansion.
+	//
+	else 
+	{
+		arg = p * log ( x ) - x - lgamma ( p );
+		a = 1.0 - p;
+		b = a + x + 1.0;
+		c = 0.0;
+		pn1 = 1.0;
+		pn2 = x;
+		pn3 = x + 1.0;
+		pn4 = x * b;
+		value = pn3 / pn4;
+
+		for ( ; ; )
+		{
+			a = a + 1.0;
+			b = b + 2.0;
+			c = c + 1.0;
+			an = a * c;
+			pn5 = b * pn3 - an * pn1;
+			pn6 = b * pn4 - an * pn2;
+
+			if ( pn6 != 0.0 )
+			{
+				rn = pn5 / pn6;
+
+				if ( fabs ( value - rn ) <= r8_min ( tol, tol * rn ) )
+				{
+					break;
+				}
+				value = rn;
+			}
+
+			pn1 = pn3;
+			pn2 = pn4;
+			pn3 = pn5;
+			pn4 = pn6;
+			//
+			//  Re-scale terms in continued fraction if terms are large.
+			//
+			if ( oflo <= fabs ( pn5 ) )
+			{
+				pn1 = pn1 / oflo;
+				pn2 = pn2 / oflo;
+				pn3 = pn3 / oflo;
+				pn4 = pn4 / oflo;
+			}
+		}
+
+		arg = arg + log ( value );
+
+		if ( elimit <= arg )
+		{
+			value = 1.0 - exp ( arg );
+		}
+		else
+		{
+			value = 1.0;
+		}
+	}
+
+	return value;
+}
+
+double r8_min ( double x, double y )
+
+//****************************************************************************80
+//
+//  Purpose:
+//
+//    R8_MIN returns the minimum of two R8's.
+//
+//  Licensing:
+//
+//    This code is distributed under the GNU LGPL license. 
+//
+//  Modified:
+//
+//    31 August 2004
+//
+//  Author:
+//
+//    John Burkardt
+//
+//  Parameters:
+//
+//    Input, double X, Y, the quantities to compare.
+//
+//    Output, double R8_MIN, the minimum of X and Y.
+//
+{
+  double value;
+
+  if ( y < x )
+  {
+    value = y;
+  } 
+  else
+  {
+    value = x;
+  }
+  return value;
+}
+
+// This function is implemented by Li Song. 
+// If Y follows (theta, k)=(1/alpha, k)-gamma distribution, then P_Y(Y<t)=gammad(alpha*t, k).
+// Furthurmore, chi-square with d.f. k is gamma distribution (2, k/2)
+double chicdf( double x, double df ) 
+{
+	int ifault ;
+	return gammad( x / 2.0, df / 2.0, &ifault ) ;
+}
+