<div dir="ltr"><div><div>Neil,<br><br></div>Here in is an implementation of the digamma function. Initially I tried Haskell's, then a Java impl from Mallet (LDA library) but they didn't align (BTW I think the Haskell impl is flat wrong for digamma (2)) and sought alternate sources.<br>
<br></div>I finally found <br><div><a href="http://code.google.com/p/pmtk3/source/browse/trunk/util/digamma.m?r=227&spec=svn227">http://code.google.com/p/pmtk3/source/browse/trunk/util/digamma.m?r=227&spec=svn227</a><br>
</div><div>from pmtk2 a probabilistic modeling toolkit for Matlab/Octave, which is MIT LICENSED so I think we are good there.<br><br></div><div>My impl is based (well ok copied) from that impl.<br><br></div><div>Octave also appears to have a Gnu Science Library FFI which has an impl for psi (digamma) as well which I've used as a sanity check for positive x values.<br>
<br></div><div> apt-get install octave<br> apt-get install octave-gls<br> apt-get install octave-gsl<br>octave:6> format long<br>octave:7> psi (10)<br>ans = 2.25175258906672<br><br>> (digamma 10.0)<br>2.251752589068216<br>
<br></div><div>Since it works for x = 10 to a few decimal places, I'm sure it works everywhere ... perfectly.<br><br></div><div>So far the Racket impl appears to be aligned to Octave+GSL, but I have no intuition as what is acceptable epsilon variance in floating point vs what is true error in implementation. The impl handles z < 0, yet Octave+GSL blows up.<br>
<br></div><div>I added addition poly coefficients up to 12 but the impl currently only uses 7.<br><br></div><div>impl follows:<br><br></div><div>#lang typed/racket/base<br><br>#|<br> Reference:<br><br> J Bernardo,<br> Psi ( Digamma ) Function,<br>
Algorithm AS 103,<br> Applied Statistics,<br> Volume 25, Number 3, pages 315-317, 1976.<br><br> From <a href="http://www.psc.edu/~burkardt/src/dirichlet/dirichlet.f">http://www.psc.edu/~burkardt/src/dirichlet/dirichlet.f</a><br>
|#<br><br>(provide:<br> [digamma (Float -> Float)])<br><br>(require<br> (only-in racket/flonum<br> fltan<br> fllog)<br> (only-in racket/math<br> pi))<br><br>(define: PI : Float pi)<br>(define: NEG-PI : Float (- PI))<br>
(define: PI-OVER-TWO : Float (/ PI 2.0))<br>(define: PI-SQUARED-OVER-SIX : Float (/ (* PI PI) 6))<br>(define: EULER-MASCHERONI : Float -0.5772156649015328606065121)<br><br>(define: DIGAMMA-1 : Float EULER-MASCHERONI) ;; digamma(1)<br>
(define: DIGAMMA-2 : Float PI-SQUARED-OVER-SIX)<br><br>(define: DIGAMMA-COEF-3 : Float (/ 1.0 12.0))<br>(define: DIGAMMA-COEF-4 : Float (/ 1.0 120.0))<br>(define: DIGAMMA-COEF-5 : Float (/ 1.0 252.0))<br>(define: DIGAMMA-COEF-6 : Float (/ 1.0 240.0))<br>
(define: DIGAMMA-COEF-7 : Float (/ 1.0 132.0))<br>;;(define: DIGAMMA-COEF-8 : Float (/ 691.0 32760.0))<br>;;(define: DIGAMMA-COEF-9 : Float (/ 1.0 12.0))<br>;;(define: DIGAMMA-COEF-10 : Float (/ 3617.0 8160.0))<br>;;(define: DIGAMMA-COEF-11 : Float (/ 43867.0 14364.0))<br>
;;(define: DIGAMMA-COEF-12 : Float (/ 174611.0 6600.0))<br><br>(define: DIGAMMA-LARGE : Float 9.5)<br>(define: DIGAMMA-SMALL : Float 0.000001)<br><br>;;anxn + … + a2x2 + a1x + a0 = a0 + x(a1 + x(a2 + x(… an) …) )<br>(: digamma-poly (Float -> Float))<br>
(define (digamma-poly z)<br> (* z (- DIGAMMA-COEF-3 <br> (* z (- DIGAMMA-COEF-4<br> (* z (- DIGAMMA-COEF-5<br> (* z (+ DIGAMMA-COEF-6<br> (* z (+ DIGAMMA-COEF-7)))))))))))<br><br>
;; Assumes z < 0<br>;; Use the reflection formula (Jeffrey 11.1.6):<br>;; digamma(-x) = digamma(x+1) + pi*cot(pi*x)<br>;; y = digamma(-x+1) + pi*cot(-pi*x);<br>;; This is related to the identity<br>;; digamma(-x) = digamma(x+1) - digamma(z) + digamma(1-z)<br>
;; where z is the fractional part of x<br>;; For example:<br>;; digamma(-3.1) = 1/3.1 + 1/2.1 + 1/1.1 + 1/0.1 + digamma(1-0.1)<br>;; = digamma(4.1) - digamma(0.1) + digamma(1-0.1)<br>;; Then we use<br>;; digamma(1-z) - digamma(z) = pi*cot(pi*z)<br>
;; cot(x) = tan (pi/2 - x)<br><br>(: cotan (Float -> Float))<br>(define (cotan x)<br> (fltan (- PI-OVER-TWO x)))<br><br>(: digamma-negative (Float -> Float))<br>(define (digamma-negative z)<br> ;; (assert (< z 0.0))<br>
(+ (digamma (+ (- z) 1.0))<br> (* PI (cotan (* z NEG-PI)))))<br><br>(: digamma (Float -> Float))<br>(define (digamma z)<br> (cond <br> ((eq? z 0.0)<br> -inf.0) <br> ((< z 0.0)<br> (digamma-negative z))<br>
((eq? z 1.0)<br> DIGAMMA-1)<br> ((<= z DIGAMMA-SMALL)<br> (/ (- DIGAMMA-1 1.0)<br> (+ z (* DIGAMMA-2 z))))<br> ((< z DIGAMMA-LARGE) ;; Reduce to digamma(x+n) where (x+n) >= large<br>
(- (digamma (+ z 1.0)) (/ 1 z)))<br> (else ;; Use de Moivre's expansion if argument >= large.<br> (let ((r (/ 1.0 z)))<br> (let ((A (- (fllog z) (* 0.5 r)))) <br> (- A (digamma-poly (* r r))))))))<br>
<br><br></div></div>