package com.spbsu.bernulli.caches; import org.apache.commons.math3.special.Gamma; import org.apache.commons.math3.util.FastMath; import java.util.Arrays; //calculate and cache Beta(alpha + m, beta + n -m) / Beta(alpha,beta) public class DigammaCache { int maxOffset; double base; final double[] values; final boolean[] cached; public DigammaCache(double base, int n) { values = new double[n + 2]; cached = new boolean[n + 2]; this.base = base; this.maxOffset = n; } // 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; public final double calculate(int offset) { if (cached[offset]) { return values[offset]; } else { double x = base + offset; if (x > 0 && x <= S_LIMIT) { // use method 5 from Bernardo AS103 // accurate to O(x) values[offset] = -Gamma.GAMMA - 1 / x; cached[offset] = true; } else 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 values[offset] = FastMath.log(x) - 0.5 / x - inv * ((1.0 / 12) + inv * (1.0 / 120 - inv / 252)); cached[offset] = true; } else if (offset == (maxOffset + 1)) { cached[offset] = true; values[offset] = Gamma.digamma(base + offset); } else { values[offset] = calculate(offset + 1) - 1.0 / x; cached[offset] = true; } } return values[offset]; } final public void update(double newBase) { if (this.base == newBase) return; this.base = newBase; Arrays.fill(cached, false); } }