/** * Returns the value of log[Γ(b) / Γ(a + b)] for a ≥ 0 and b ≥ 10. Based on the <em>NSWC Library * of Mathematics Subroutines</em> double precision implementation, {@code DLGDIV}. In {@code * BetaTest.testLogGammaMinusLogGammaSum()}, this private method is accessed through reflection. * * @param a First argument. * @param b Second argument. * @return the value of {@code log(Gamma(b) / Gamma(a + b))}. * @throws NumberIsTooSmallException if {@code a < 0.0} or {@code b < 10.0}. */ private static double logGammaMinusLogGammaSum(final double a, final double b) throws NumberIsTooSmallException { if (a < 0.0) { throw new NumberIsTooSmallException(a, 0.0, true); } if (b < 10.0) { throw new NumberIsTooSmallException(b, 10.0, true); } /* * d = a + b - 0.5 */ final double d; final double w; if (a <= b) { d = b + (a - 0.5); w = deltaMinusDeltaSum(a, b); } else { d = a + (b - 0.5); w = deltaMinusDeltaSum(b, a); } final double u = d * FastMath.log1p(a / b); final double v = a * (FastMath.log(b) - 1.0); return u <= v ? (w - u) - v : (w - v) - u; }
/** * Returns the value of log Γ(a + b) for 1 ≤ a, b ≤ 2. Based on the <em>NSWC Library of * Mathematics Subroutines</em> double precision implementation, {@code DGSMLN}. In {@code * BetaTest.testLogGammaSum()}, this private method is accessed through reflection. * * @param a First argument. * @param b Second argument. * @return the value of {@code log(Gamma(a + b))}. * @throws OutOfRangeException if {@code a} or {@code b} is lower than {@code 1.0} or greater than * {@code 2.0}. */ private static double logGammaSum(final double a, final double b) throws OutOfRangeException { if ((a < 1.0) || (a > 2.0)) { throw new OutOfRangeException(a, 1.0, 2.0); } if ((b < 1.0) || (b > 2.0)) { throw new OutOfRangeException(b, 1.0, 2.0); } final double x = (a - 1.0) + (b - 1.0); if (x <= 0.5) { return Gamma.logGamma1p(1.0 + x); } else if (x <= 1.5) { return Gamma.logGamma1p(x) + FastMath.log1p(x); } else { return Gamma.logGamma1p(x - 1.0) + FastMath.log(x * (1.0 + x)); } }
/** {@inheritDoc} * */ @Override public double logDensity(double x) { recomputeZ(); if (x < 0 || x > 1) { return Double.NEGATIVE_INFINITY; } else if (x == 0) { if (alpha < 1) { throw new NumberIsTooSmallException( LocalizedFormats.CANNOT_COMPUTE_BETA_DENSITY_AT_0_FOR_SOME_ALPHA, alpha, 1, false); } return Double.NEGATIVE_INFINITY; } else if (x == 1) { if (beta < 1) { throw new NumberIsTooSmallException( LocalizedFormats.CANNOT_COMPUTE_BETA_DENSITY_AT_1_FOR_SOME_BETA, beta, 1, false); } return Double.NEGATIVE_INFINITY; } else { double logX = FastMath.log(x); double log1mX = FastMath.log1p(-x); return (alpha - 1) * logX + (beta - 1) * log1mX - z; } }
@Test public void testSamplerHelper1() { final double tol = 1e-12; final double[] testValues = { FastMath.nextUp(-1.), -1e-1, -1e-2, -1e-3, -1e-4, -1e-5, -1e-6, -1e-7, -1e-8, -1e-9, -1e-10, -1e-11, 0., 1e-11, 1e-10, 1e-9, 1e-8, 1e-7, 1e-6, 1e-5, 1e-4, 1e-3, 1e-2, 1e-1, 1e0 }; for (final double testValue : testValues) { final double expected = FastMath.log1p(testValue); TestUtils.assertRelativelyEquals( expected, ZipfRejectionInversionSampler.helper1(testValue) * testValue, tol); } }
/** {@inheritDoc} */ public double value(double x) { return FastMath.log1p(x); }
/** * Returns the value of log B(p, q) for 0 ≤ x ≤ 1 and p, q > 0. Based on the <em>NSWC Library of * Mathematics Subroutines</em> implementation, {@code DBETLN}. * * @param p First argument. * @param q Second argument. * @return the value of {@code log(Beta(p, q))}, {@code NaN} if {@code p <= 0} or {@code q <= 0}. */ public static double logBeta(final double p, final double q) { if (Double.isNaN(p) || Double.isNaN(q) || (p <= 0.0) || (q <= 0.0)) { return Double.NaN; } final double a = FastMath.min(p, q); final double b = FastMath.max(p, q); if (a >= 10.0) { final double w = sumDeltaMinusDeltaSum(a, b); final double h = a / b; final double c = h / (1.0 + h); final double u = -(a - 0.5) * FastMath.log(c); final double v = b * FastMath.log1p(h); if (u <= v) { return (((-0.5 * FastMath.log(b) + HALF_LOG_TWO_PI) + w) - u) - v; } else { return (((-0.5 * FastMath.log(b) + HALF_LOG_TWO_PI) + w) - v) - u; } } else if (a > 2.0) { if (b > 1000.0) { final int n = (int) FastMath.floor(a - 1.0); double prod = 1.0; double ared = a; for (int i = 0; i < n; i++) { ared -= 1.0; prod *= ared / (1.0 + ared / b); } return (FastMath.log(prod) - n * FastMath.log(b)) + (Gamma.logGamma(ared) + logGammaMinusLogGammaSum(ared, b)); } else { double prod1 = 1.0; double ared = a; while (ared > 2.0) { ared -= 1.0; final double h = ared / b; prod1 *= h / (1.0 + h); } if (b < 10.0) { double prod2 = 1.0; double bred = b; while (bred > 2.0) { bred -= 1.0; prod2 *= bred / (ared + bred); } return FastMath.log(prod1) + FastMath.log(prod2) + (Gamma.logGamma(ared) + (Gamma.logGamma(bred) - logGammaSum(ared, bred))); } else { return FastMath.log(prod1) + Gamma.logGamma(ared) + logGammaMinusLogGammaSum(ared, b); } } } else if (a >= 1.0) { if (b > 2.0) { if (b < 10.0) { double prod = 1.0; double bred = b; while (bred > 2.0) { bred -= 1.0; prod *= bred / (a + bred); } return FastMath.log(prod) + (Gamma.logGamma(a) + (Gamma.logGamma(bred) - logGammaSum(a, bred))); } else { return Gamma.logGamma(a) + logGammaMinusLogGammaSum(a, b); } } else { return Gamma.logGamma(a) + Gamma.logGamma(b) - logGammaSum(a, b); } } else { if (b >= 10.0) { return Gamma.logGamma(a) + logGammaMinusLogGammaSum(a, b); } else { // The following command is the original NSWC implementation. // return Gamma.logGamma(a) + // (Gamma.logGamma(b) - Gamma.logGamma(a + b)); // The following command turns out to be more accurate. return FastMath.log(Gamma.gamma(a) * Gamma.gamma(b) / Gamma.gamma(a + b)); } } }