@Override protected double computeNormalizationEnergy() { final double alpha = _alphaMinusOne + 1; final double logBeta = _beta != 0 ? Math.log(_beta) : 0.0; return -(org.apache.commons.math3.special.Gamma.logGamma(alpha) - alpha * logBeta); }
/** * Compute the error of Stirling's series at the given value. * * <p>References: * * <ol> * <li>Eric W. Weisstein. "Stirling's Series." From MathWorld--A Wolfram Web Resource. <a * target="_blank" href="http://mathworld.wolfram.com/StirlingsSeries.html"> * http://mathworld.wolfram.com/StirlingsSeries.html</a> * </ol> * * @param z the value. * @return the Striling's series error. */ static double getStirlingError(double z) { double ret; if (z < 15.0) { double z2 = 2.0 * z; if (FastMath.floor(z2) == z2) { ret = EXACT_STIRLING_ERRORS[(int) z2]; } else { ret = Gamma.logGamma(z + 1.0) - (z + 0.5) * FastMath.log(z) + z - HALF_LOG_2_PI; } } else { double z2 = z * z; ret = (0.083333333333333333333 - (0.00277777777777777777778 - (0.00079365079365079365079365 - (0.000595238095238095238095238 - 0.0008417508417508417508417508 / z2) / z2) / z2) / z2) / z; } return ret; }
/** Recompute the normalization factor. */ private void recomputeZ() { if (Double.isNaN(z)) { z = Gamma.logGamma(alpha) + Gamma.logGamma(beta) - Gamma.logGamma(alpha + beta); } }
// Make proposal @Override public BlockProposal next(Value[] currentValue, Domain[] variableDomain) { final DimpleRandom rand = activeRandom(); double proposalForwardEnergy = 0; double proposalReverseEnergy = 0; int argumentIndex = 0; int argumentLength = currentValue.length; Value[] newValue = new Value[argumentLength]; for (int i = 0; i < argumentLength; i++) newValue[i] = Value.create(variableDomain[i]); // Get the current alpha values double[] alpha; double[] alphaEnergy; double alphaSum = 0; if (_customFactor.isAlphaEnergyRepresentation()) { alphaEnergy = _customFactor.getCurrentAlpha(); alpha = new double[alphaEnergy.length]; for (int i = 0; i < alphaEnergy.length; i++) { alpha[i] = Math.exp(-alphaEnergy[i]); alphaSum += alpha[i]; } } else { alpha = _customFactor.getCurrentAlpha(); alphaEnergy = new double[alpha.length]; for (int i = 0; i < alpha.length; i++) { alphaEnergy[i] = -Math.log(alpha[i]); alphaSum += alpha[i]; } } if (alphaSum == 0) // Shouldn't happen, but can during initialization { Arrays.fill(alpha, 1); Arrays.fill(alphaEnergy, 0); alphaSum = alpha.length; } int nextN = _constantN; if (!_hasConstantN) { // If N is variable, sample N uniformly int previousN = currentValue[argumentIndex].getIndex(); int NDomainSize = requireNonNull(variableDomain[0].asDiscrete()).size(); nextN = rand.nextInt(NDomainSize); newValue[argumentIndex].setIndex(nextN); argumentIndex++; // Add this portion of -log p(x_proposed -> x_previous) proposalReverseEnergy += -org.apache.commons.math3.special.Gamma.logGamma(previousN + 1) + previousN * Math.log(alphaSum); // Add this portion of -log p(x_previous -> x_proposed) proposalForwardEnergy += -org.apache.commons.math3.special.Gamma.logGamma(nextN + 1) + nextN * Math.log(alphaSum); } // Given N and alpha, resample the outputs // Multinomial formed by successively sampling from a binomial and subtracting each count from // the total // FIXME: Assumes all outputs are variable (no constant outputs) int remainingN = nextN; int alphaIndex = 0; for (; argumentIndex < argumentLength; argumentIndex++, alphaIndex++) { double alphai = alpha[alphaIndex]; double alphaEnergyi = alphaEnergy[alphaIndex]; int previousX = currentValue[argumentIndex].getIndex(); int nextX; if (argumentIndex < argumentLength - 1) nextX = rand.nextBinomial(remainingN, alphai / alphaSum); else // Last value nextX = remainingN; newValue[argumentIndex].setIndex(nextX); remainingN -= nextX; // Subtract the sample value from the remaining total count alphaSum -= alphai; // Subtract this alpha value from the sum used for normalization double previousXNegativeLogAlphai; double nextXNegativeLogAlphai; if (alphai == 0 && previousX == 0) previousXNegativeLogAlphai = 0; else previousXNegativeLogAlphai = previousX * alphaEnergyi; if (alphai == 0 && nextX == 0) nextXNegativeLogAlphai = 0; else nextXNegativeLogAlphai = nextX * alphaEnergyi; // Add this portion of -log p(x_proposed -> x_previous) proposalReverseEnergy += previousXNegativeLogAlphai + org.apache.commons.math3.special.Gamma.logGamma(previousX + 1); // Add this portion of -log p(x_previous -> x_proposed) proposalForwardEnergy += nextXNegativeLogAlphai + org.apache.commons.math3.special.Gamma.logGamma(nextX + 1); } return new BlockProposal(newValue, proposalForwardEnergy, proposalReverseEnergy); }