You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by ps...@apache.org on 2009/05/24 08:03:19 UTC

svn commit: r778091 - in /commons/proper/math/trunk/src: java/org/apache/commons/math/special/Gamma.java site/xdoc/changes.xml test/org/apache/commons/math/special/GammaTest.java

Author: psteitz
Date: Sun May 24 06:03:19 2009
New Revision: 778091

URL: http://svn.apache.org/viewvc?rev=778091&view=rev
Log:
Added digamma function.
JIRA: MATH-267
Contributed by Ted Dunning

Modified:
    commons/proper/math/trunk/src/java/org/apache/commons/math/special/Gamma.java
    commons/proper/math/trunk/src/site/xdoc/changes.xml
    commons/proper/math/trunk/src/test/org/apache/commons/math/special/GammaTest.java

Modified: commons/proper/math/trunk/src/java/org/apache/commons/math/special/Gamma.java
URL: http://svn.apache.org/viewvc/commons/proper/math/trunk/src/java/org/apache/commons/math/special/Gamma.java?rev=778091&r1=778090&r2=778091&view=diff
==============================================================================
--- commons/proper/math/trunk/src/java/org/apache/commons/math/special/Gamma.java (original)
+++ commons/proper/math/trunk/src/java/org/apache/commons/math/special/Gamma.java Sun May 24 06:03:19 2009
@@ -33,6 +33,9 @@
     /** Serializable version identifier */
     private static final long serialVersionUID = -6587513359895466954L;
 
+    /** <a href="http://en.wikipedia.org/wiki/Euler-Mascheroni_constant">Euler-Mascheroni constant</a> */
+    public static final double GAMMA = 0.577215664901532860606512090082;
+
     /** Maximum allowed numerical error. */
     private static final double DEFAULT_EPSILON = 10e-15;
 
@@ -59,7 +62,7 @@
     /** Avoid repeated computation of log of 2 PI in logGamma */
     private static final double HALF_LOG_2_PI = 0.5 * Math.log(2.0 * Math.PI);
 
-    
+
     /**
      * Default constructor.  Prohibit instantiation.
      */
@@ -261,4 +264,45 @@
 
         return ret;
     }
+
+
+    // limits for switching algorithm in digamma
+    /** C limit */
+    private static final double C_LIMIT = 49;
+    /** S limit */
+    private static final double S_LIMIT = 1e-5;
+
+    /**
+     * <p>Computes the <a href="http://en.wikipedia.org/wiki/Digamma_function">digamma function</a>
+     * using the algorithm defined in <br/>
+     * Jose Bernardo, Algorithm AS 103: Psi (Digamma) Function, Applied Statistics, 1976.</p>
+     * 
+     * <p>Some of the constants have been changed to increase accuracy at the moderate expense
+     * of run-time performance.  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.
+     * @param x argument
+     * @return value of the digamma function
+     */
+    public static double digamma(double x) {
+        if (x > 0 && x <= S_LIMIT) {
+            // use method 5 from Bernardo AS103
+            // accurate to O(x)
+            return -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 Math.log(x) - 0.5 / x - inv * ((1.0 / 12) + inv * (1.0 / 120 - inv / 252));
+        }
+
+        return digamma(x + 1) - 1 / x;
+    }
 }

Modified: commons/proper/math/trunk/src/site/xdoc/changes.xml
URL: http://svn.apache.org/viewvc/commons/proper/math/trunk/src/site/xdoc/changes.xml?rev=778091&r1=778090&r2=778091&view=diff
==============================================================================
--- commons/proper/math/trunk/src/site/xdoc/changes.xml (original)
+++ commons/proper/math/trunk/src/site/xdoc/changes.xml Sun May 24 06:03:19 2009
@@ -39,6 +39,9 @@
   </properties>
   <body>
     <release version="2.0" date="TBD" description="TBD">
+      <action dev="psteitz" type="add" issue="MATH-267" due=to="Ted Dunning">
+        Added digamma function.
+      </action>
       <action dev="psteitz" type="add" issue="MATH-136" due=to="John Gant">
         Added Spearman's rank correlation (SpearmansCorrelation).
       </action>

Modified: commons/proper/math/trunk/src/test/org/apache/commons/math/special/GammaTest.java
URL: http://svn.apache.org/viewvc/commons/proper/math/trunk/src/test/org/apache/commons/math/special/GammaTest.java?rev=778091&r1=778090&r2=778091&view=diff
==============================================================================
--- commons/proper/math/trunk/src/test/org/apache/commons/math/special/GammaTest.java (original)
+++ commons/proper/math/trunk/src/test/org/apache/commons/math/special/GammaTest.java Sun May 24 06:03:19 2009
@@ -25,10 +25,7 @@
  * @version $Revision$ $Date$
  */
 public class GammaTest extends TestCase {
-    /**
-     * Constructor for BetaTest.
-     * @param name
-     */
+     
     public GammaTest(String name) {
         super(name);
     }
@@ -92,4 +89,37 @@
     public void testLogGammaPositive() {
         testLogGamma(0.6931471805599457, 3.0);
     }
+
+    public void testDigammaLargeArgs() {
+        double eps = 1e-8;
+        assertEquals(4.6001618527380874002, Gamma.digamma(100), eps);
+        assertEquals(3.9019896734278921970, Gamma.digamma(50), eps);
+        assertEquals(2.9705239922421490509, Gamma.digamma(20), eps);
+        assertEquals(2.9958363947076465821, Gamma.digamma(20.5), eps);
+        assertEquals(2.2622143570941481605, Gamma.digamma(10.1), eps);
+        assertEquals(2.1168588189004379233, Gamma.digamma(8.8), eps);
+        assertEquals(1.8727843350984671394, Gamma.digamma(7), eps);
+        assertEquals(0.42278433509846713939, Gamma.digamma(2), eps);
+        assertEquals(-100.56088545786867450, Gamma.digamma(0.01), eps);
+        assertEquals(-4.0390398965921882955, Gamma.digamma(-0.8), eps);
+        assertEquals(4.2003210041401844726, Gamma.digamma(-6.3), eps);
+    }
+
+    public void testDigammaSmallArgs() {
+        // values for negative powers of 10 from 1 to 30 as computed by webMathematica with 20 digits
+        // see functions.wolfram.com
+        double[] expected = {-10.423754940411076795, -100.56088545786867450, -1000.5755719318103005,
+                -10000.577051183514335, -100000.57719921568107, -1.0000005772140199687e6, -1.0000000577215500408e7,
+                -1.0000000057721564845e8, -1.0000000005772156633e9, -1.0000000000577215665e10, -1.0000000000057721566e11,
+                -1.0000000000005772157e12, -1.0000000000000577216e13, -1.0000000000000057722e14, -1.0000000000000005772e15, -1e+16,
+                -1e+17, -1e+18, -1e+19, -1e+20, -1e+21, -1e+22, -1e+23, -1e+24, -1e+25, -1e+26,
+                -1e+27, -1e+28, -1e+29, -1e+30};
+        for (double n = 1; n < 30; n++) {
+            checkRelativeError(String.format("Test %.0f: ", n), expected[(int) (n - 1)], Gamma.digamma(Math.pow(10.0, -n)), 1e-8);
+        }
+    }
+
+    private void checkRelativeError(String msg, double expected, double actual, double tolerance) {
+        assertEquals(msg, expected, actual, Math.abs(tolerance * actual));
+    }
 }