<div dir="ltr"><div><div>Neil,<br><br></div>Here in is an implementation of the digamma function.  Initially I tried Haskell&#39;s, then a Java impl from Mallet  (LDA library) but they didn&#39;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&amp;spec=svn227">http://code.google.com/p/pmtk3/source/browse/trunk/util/digamma.m?r=227&amp;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&#39;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&gt; format long<br>octave:7&gt; psi (10)<br>ans =  2.25175258906672<br><br>&gt; (digamma 10.0)<br>2.251752589068216<br>
<br></div><div>Since it works for x = 10 to a few decimal places, I&#39;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 &lt; 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 -&gt; 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 -&gt; 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 &lt; 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 -&gt; Float))<br>(define (cotan x)<br>  (fltan (- PI-OVER-TWO x)))<br><br>(: digamma-negative (Float -&gt; Float))<br>(define (digamma-negative z)<br>  ;; (assert (&lt; z 0.0))<br>
  (+ (digamma (+ (- z) 1.0))<br>     (* PI (cotan (* z NEG-PI)))))<br><br>(: digamma (Float -&gt; Float))<br>(define (digamma z)<br>  (cond <br>   ((eq? z 0.0)<br>    -inf.0) <br>   ((&lt; z 0.0)<br>    (digamma-negative z))<br>
   ((eq? z 1.0)<br>    DIGAMMA-1)<br>   ((&lt;= z DIGAMMA-SMALL)<br>    (/ (- DIGAMMA-1 1.0)<br>       (+ z (* DIGAMMA-2 z))))<br>   ((&lt; z DIGAMMA-LARGE)             ;; Reduce to digamma(x+n) where (x+n) &gt;= large<br>
    (- (digamma (+ z 1.0)) (/ 1 z)))<br>   (else                            ;; Use de Moivre&#39;s expansion if argument &gt;= 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>