Repository: commons-math
Updated Branches:
  refs/heads/develop f695c9ce3 -> 1325484c3


Improved digamma function, especially for negative values.


Project: http://git-wip-us.apache.org/repos/asf/commons-math/repo
Commit: http://git-wip-us.apache.org/repos/asf/commons-math/commit/53661585
Tree: http://git-wip-us.apache.org/repos/asf/commons-math/tree/53661585
Diff: http://git-wip-us.apache.org/repos/asf/commons-math/diff/53661585

Branch: refs/heads/develop
Commit: 5366158572bd88f18df7883ab823ea62b7701d77
Parents: cbae75b
Author: Eric Prescott-Gagnon <eric.prescottgag...@jda.com>
Authored: Fri Jan 8 14:18:30 2016 -0500
Committer: Eric Prescott-Gagnon <eric.prescottgag...@jda.com>
Committed: Thu May 5 13:48:13 2016 -0400

----------------------------------------------------------------------
 .../org/apache/commons/math4/special/Gamma.java | 42 ++++++++++++--------
 .../apache/commons/math4/special/GammaTest.java |  1 +
 2 files changed, 26 insertions(+), 17 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/commons-math/blob/53661585/src/main/java/org/apache/commons/math4/special/Gamma.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/math4/special/Gamma.java 
b/src/main/java/org/apache/commons/math4/special/Gamma.java
index 70aaec3..c59049c 100644
--- a/src/main/java/org/apache/commons/math4/special/Gamma.java
+++ b/src/main/java/org/apache/commons/math4/special/Gamma.java
@@ -427,18 +427,16 @@ public class Gamma {
      * <p>Computes the digamma function of x.</p>
      *
      * <p>This is an independently written implementation of the algorithm 
described in
-     * Jose Bernardo, Algorithm AS 103: Psi (Digamma) Function, Applied 
Statistics, 1976.</p>
+     * Jose Bernardo, Algorithm AS 103: Psi (Digamma) Function, Applied 
Statistics, 1976.
+     * A reflection formula 
(https://en.wikipedia.org/wiki/Digamma_function#Reflection_formula)
+     * is incorporated to improve performance on negative values.</p>
      *
      * <p>Some of the constants have been changed to increase accuracy at the 
moderate expense
-     * of run-time.  The result should be accurate to within 10^-8 absolute 
tolerance for
-     * x >= 10^-5 and within 10^-8 relative tolerance for x > 0.</p>
-     *
-     * <p>Performance for large negative values of x will be quite expensive 
(proportional to
-     * |x|).  Accuracy for negative values of x should be about 10^-8 absolute 
for results
-     * less than 10^5 and 10^-8 relative for results larger than that.</p>
+     * of run-time.  The result should be accurate to within 10^-8 relative 
tolerance for
+     * 0 < x < 10^-5 and within 10^-8 absolute tolerance otherwise.</p>
      *
      * @param x Argument.
-     * @return digamma(x) to within 10-8 relative or absolute error whichever 
is smaller.
+     * @return digamma(x) to within 10-8 relative or absolute error whichever 
is larger.
      * @see <a href="http://en.wikipedia.org/wiki/Digamma_function";>Digamma</a>
      * @see <a 
href="http://www.uv.es/~bernardo/1976AppStatist.pdf";>Bernardo&apos;s original 
article </a>
      * @since 2.0
@@ -448,22 +446,32 @@ public class Gamma {
             return x;
         }
 
+        double digamma = 0.0;
+        if (x < 0) {
+            // use reflection formula to fall back into positive values
+            digamma -= FastMath.PI / FastMath.tan(FastMath.PI * x);
+            x =  1 - x;
+        }
+
         if (x > 0 && x <= S_LIMIT) {
             // use method 5 from Bernardo AS103
             // accurate to O(x)
-            return -GAMMA - 1 / x;
+            return digamma -GAMMA - 1 / x;
         }
 
-        if (x >= C_LIMIT) {
-            // use method 4 (accurate to O(1/x^8)
-            double inv = 1 / (x * x);
-            //            1       1        1         1
-            // log(x) -  --- - ------ + ------- - -------
-            //           2 x   12 x^2   120 x^4   252 x^6
-            return FastMath.log(x) - 0.5 / x - inv * ((1.0 / 12) + inv * (1.0 
/ 120 - inv / 252));
+        while (x < C_LIMIT) {
+            digamma -= 1.0 / x;
+            x += 1.0;
         }
 
-        return digamma(x + 1) - 1 / x;
+        // use method 4 (accurate to O(1/x^8)
+        double inv = 1 / (x * x);
+        //            1       1        1         1
+        // log(x) -  --- - ------ + ------- - -------
+        //           2 x   12 x^2   120 x^4   252 x^6
+        digamma += FastMath.log(x) - 0.5 / x - inv * ((1.0 / 12) + inv * (1.0 
/ 120 - inv / 252));
+
+        return digamma;
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/commons-math/blob/53661585/src/test/java/org/apache/commons/math4/special/GammaTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/math4/special/GammaTest.java 
b/src/test/java/org/apache/commons/math4/special/GammaTest.java
index 51982a3..c320526 100644
--- a/src/test/java/org/apache/commons/math4/special/GammaTest.java
+++ b/src/test/java/org/apache/commons/math4/special/GammaTest.java
@@ -107,6 +107,7 @@ public class GammaTest {
         Assert.assertEquals(-100.56088545786867450, Gamma.digamma(0.01), eps);
         Assert.assertEquals(-4.0390398965921882955, Gamma.digamma(-0.8), eps);
         Assert.assertEquals(4.2003210041401844726, Gamma.digamma(-6.3), eps);
+        Assert.assertEquals(-3.110625123035E-5, Gamma.digamma(1.4616), eps);
     }
 
     @Test

Reply via email to