Implementing a distribution - UBC-Stat-ML/blangSDK GitHub Wiki
There are three essential steps involved in implementing a non-standard distribution in Blang:
- Defining its log factors:
DistributionName
.bl
- Generating the random variable:
Generators.java
- Automated testing:
Examples.xtend
- [(Optional) Submit as a standard distribution to Blang:
BuiltInDistributions.xtend
] (#BuiltInDistributions.xtend)
DistributionName.bl
Gamma distribution example: ./src/main/java/blang/distributions/Gamma.bl
First, declare its parameters
random RealVar realization
param RealVar shape
param RealVar rate
then in the laws{}
block, define its log probability density function using log factors. The sum of logf() should make up your log probability density function. When possible, -for computational reasons- split up the log factors into separate blocks with as few variables as needed per block.
laws{
logf(shape, rate, realization) {
if (shape <= 0.0 || rate <= 0) return NEGATIVE_INFINITY
if (realization <= 0.0) return NEGATIVE_INFINITY
return (shape - 1.0) * log(realization * rate)
}
logf(realization, rate) {
if (rate <= 0) return NEGATIVE_INFINITY
if (realization <= 0.0) return NEGATIVE_INFINITY
return - realization * rate
}
logf(shape) {
if (shape <= 0.0) return NEGATIVE_INFINITY
return - lnGamma(shape)
}
logf(rate) {
if (rate <= 0.0) return NEGATIVE_INFINITY
return log(rate)
}
}
In the generate(rand){}
block, make a call to the Generator's random variable generating method.
generate(rand) {
rand.gamma(shape, rate)
}
Normal laws{}
block example:
laws {
logf() {
- log(2*PI) / 2.0
}
logf(variance) {
if (variance < 0.0) return NEGATIVE_INFINITY
return - 0.5 * log(variance)
}
logf(mean, variance, realization) {
if (variance < 0.0) return NEGATIVE_INFINITY
return - 0.5 * pow(mean - realization, 2) / variance
}
}
Generators.java
Gamma distribution example: ./src/main/java/blang/distributions/Generators.java
Implement a method to return a random variable sampled from your custom distribution using implementations from other packages, or by other means such as the inverse CDF method
public static double gamma(Random random, double shape, double rate)
{
double result = new GammaDistribution(generator(random), shape, 1.0/rate).sample();
if (result == 0.0) // avoid crash-inducing zero probability corner cases
result = ZERO_PLUS_EPS;
return result;
}
Examples.xtend
Gamma distribution example: ./src/test/java/blang/Examples.xtend
To enable automated tests,
public val gamma = add(
new Gamma.Builder()
.setRate(fixedReal(2.1))
.setShape(fixedReal(0.9))
.setRealization(latentReal)
.build,
realRealizationSquared
)
Now run TestSDKDistributions.xtend as a JUnit test!
BuiltInDistributions.xtend
Gamma distribution example: ./src/main/java/blang/runtime/internals/doc/contents/BuiltInDistributions.xtend
For developers contributing to the BlangSDK, add the respective distribution name under the corresponding sections to include the distribution under the documentations section.
section("Continuous") [
documentClass(ContinuousUniform)
documentClass(Exponential)
documentClass(Normal)
documentClass(Beta)
documentClass(Gamma)
documentClass(StudentT)
documentClass(HalfStudentT)
documentClass(ChiSquared)
]