001 package calhoun.analysis.crf.statistics;
002
003 import calhoun.util.Assert;
004
005 public class GammaDistribution {
006
007 /* See http://mathworld.wolfram.com/GammaDistribution.html
008 *
009 *
010 *
011 *
012 */
013
014 public static double lgamma(double p, double lambda, double x) {
015 Assert.a(lambda>0);
016 Assert.a(x>0);
017 Assert.a(p>0);
018 double ret = (p-1)*Math.log(x) -x*lambda + p*Math.log(lambda) - lgamma(p);
019 if (Double.isNaN(ret)) {
020 Assert.a(false," pow1=" +Math.pow(x,p-1)+" exp1="+Math.exp(-x*lambda)+" pow2="+Math.pow(lambda,p) + " gamma="+gamma(p) );
021 }
022 return ret;
023 }
024
025
026 public static double gamma(double p, double lambda, double x) {
027 Assert.a(lambda>0);
028 Assert.a(x>0);
029 Assert.a(p>0);
030
031 double lret = lgamma(p,lambda,x);
032 double ret = Math.exp(lret);
033 //Assert.a(!Double.isNaN(ret));
034 Assert.a((ret != Double.NEGATIVE_INFINITY) && (ret != Double.POSITIVE_INFINITY) && (!Double.isNaN(ret)));
035 return ret;
036 }
037
038 public static double gamma(double x) {
039 /* The gamma function, which is the improper integral
040 * integral_0^infinity t^(x-1) * exp(-t) dt
041 *
042 * x*gamma(x) = gamma(x+1).
043 *
044 * For integers x>=1, gamma(x) = (x-1)!
045 */
046 Assert.a(x>0);
047 double y = lgamma(x);
048 return Math.exp(y);
049 }
050
051
052 private static double lgamma(double x) {
053 Assert.a(x>0);
054
055 double l2pi = Math.log(2*Math.PI);
056 double term, y, zz ;
057 int k, t ;
058
059 if (x<10.0) return (lgamma(x+1.0) - Math.log(x)) ;
060 y = (x-0.5)*Math.log(x) -x + 0.5*l2pi ;
061 zz = 1.0/x ;
062 for (k=1; (2*k)<= bern.length ; k++) {
063 t = 2*k ;
064 term = bern[2*k]/(double) (t*(t-1)) ;
065 term *= zz ;
066 y += term ;
067 zz /= (x*x) ;
068 }
069
070 return y;
071 }
072
073
074
075
076 // Ported from Nick Patterson's code 20060203
077 static private double[] bern = new double[]{1.0,
078 -1.0/2.0,
079 1.0/6.0,
080 0,
081 -1.0/30.0,
082 0,
083 1.0/42.0,
084 0,
085 -1.0/30.0,
086 0,
087 5.0/66.0,
088 0,
089 -691.0/2730.0,
090 0,
091 7.0/6.0
092 };
093
094 // Ported from Nick Patterson's code 20060203
095 public static double[] mleg(double a1, double a2) // both p and lam assumed of size 1
096 // solve
097 // p/lam = a1 ; psi(p) - log(lam) = a2 ;
098 // Thus psi(p) - log(p) = a2 - log(a1)
099 {
100 int iter ;
101 double s, pp, ll ;
102 double top, bot, fval ;
103
104
105 s = a2 - Math.log(a1) ;
106
107 Assert.a(s<=0.0 , "log E(x) < E(log (x)) \n" ) ;
108 pp = -s ;
109
110 for (iter = 1; iter <= 30 ; ++iter) {
111 fval = s - (psi(pp) - Math.log (pp)) ;
112 if (fval<0.0) break ;
113 pp *= 2.0 ;
114 }
115
116 for (iter = 1; iter <= 30 ; ++iter) {
117 fval = s - (psi(pp) - Math.log (pp)) ;
118 if (fval>0.0) break ;
119 pp /= 2.0 ;
120 }
121
122 for (iter = 1; iter <= 10 ; ++iter) {
123 fval = psi(pp) - Math.log (pp) ;
124 top = s-fval ;
125 bot = tau(pp) - (1.0/pp) ;
126 pp += top/bot ;
127 }
128 ll = pp/a1 ;
129
130 return (new double[]{pp,ll});
131 }
132
133
134
135 private static double psi(double x) {
136 // Ported from Nick Pattersons code 20060203
137 // psi is the derivative of the gamma function
138
139 double y, zz, term ;
140 int k ;
141
142 Assert.a( x > 0.0 , "(psi) bad value: " + x) ;
143
144 Assert.a(bern.length == 15);
145
146 if (x<10.0) return (psi(x+1.0) - 1.0/x) ;
147
148 y = Math.log(x) - 1.0/(2.0*x) ;
149 zz = 1.0 ;
150 for (k=1; (2*k)< bern.length ; k++) {
151 zz /= (x*x) ;
152 term = bern[2*k]/(double) (2*k) ;
153 term *= zz ;
154 y -= term ;
155 }
156 return y ;
157 }
158
159
160 private static double tau(double x)
161 // Ported from Nick Pattersons code 20060203
162 // derivative of psi
163 {
164 double y, zz, term ;
165 int k ;
166
167 Assert.a(x>0.0 , "(tau) bad value: " + x) ;
168
169 if (x<10.0) return (tau(x+1.0) + 1.0/(x*x)) ;
170
171 y = 1.0/x + 1.0/(2.0*x*x) ;
172 zz = 1.0/x ;
173 for (k=1; (2*k)< bern.length ; k++) {
174 zz /= (x*x) ;
175 term = bern[2*k]/(double) (2*k) ;
176 term *= zz ;
177 term *= - (double) (2*k) ;
178 y -= term ;
179 }
180 return y ;
181 }
182
183
184 }