Implementing a distribution - UBC-Stat-ML/blangSDK GitHub Wiki

There are three essential steps involved in implementing a non-standard distribution in Blang:

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)
    ]