### Partial solutions for some of the recent modelling examples

Here is a simple hierarchical model to attack this problem:

ex.vax.Vaccines
package ex.vax import static extension ex.vax.Utils.* model Vaccines { param GlobalDataSource data param Plate<String> trials param Plate<String> arms param Plated<IntVar> groupSizes random Plated<IntVar> numbersOfCases random Plated<RealVar> efficacies random Plated<RealVar> incidences random RealVar a ?: latentReal, b ?: latentReal, c ?: latentReal, d ?: latentReal laws { a ~ Exponential(0.001) b ~ Exponential(0.001) c ~ Exponential(0.001) d ~ Exponential(0.001) for (Index<String> trial : trials.indices) { efficacies.get(trial) | a,b ~ Beta(a, b) incidences.get(trial) | c,d ~ Beta(c, d) } for (Index<String> trial : trials.indices) { for (Index<String> arm : arms.indices) { numbersOfCases.get(trial, arm) | IntVar groupSize = groupSizes.get(trial, arm), RealVar efficacy = if (arm.isControl) 0.0 else efficacies.get(trial), RealVar incidence = incidences.get(trial) ~ Binomial(groupSize, incidence * (1.0 - efficacy)) } } } }

Instead of assuming a logistic change in failure probability, this model assumes that it is piecewise constant with an unknown change point:

ex.cp.ChallengerChangePoint
package ex.cp model ChallengerChangePoint { random List<RealVar> failureParameters ?: latentRealList(2) random RealVar changePoint ?: latentReal random IntVar prediction ?: latentInt random List<IntVar> incidents param List<RealVar> temperatures laws { for (RealVar failureParameter : failureParameters) { failureParameter ~ Normal(0.0, 100.0) } changePoint ~ Normal(70.0, 100.0) for (int i : 0 ..< incidents.size) { incidents.get(i) | RealVar temperature = temperatures.get(i), failureParameters, changePoint ~ Bernoulli(logistic(failureParameters.get(if (temperature < changePoint) 0 else 1))) } prediction | failureParameters, changePoint ~ Bernoulli(logistic(failureParameters.get(if (31 < changePoint) 0 else 1))) } }

To summarize all the marginal likelihood for different models we tried on the challenger data:

Description

Blang model

Marginal likelihood

Basic logistic regression

Challenger.bl

-18.8

Spike-and-slab logistic regression

ChallengerSpiked.bl

-17.7

Spike-and-slab logistic regression (modified prior)

ChallengerSpiked2.bl

-17.2

Change point model

ChallengerChangePoint.bl

-13.9

So we can see that the change point model outperforms the other ones according to the marginal likelihood criterion.

I mentioned briefly the German Tank problem. Here is an implementation of a simple model to approach this problem.

Read the example given in this wikipedia article, In the code below, I am using the same observation as in the wikipedia example (summarized with the sufficient statistics that 4 serial numbers were sampled, and that the maximum observed in these 4 is the serial number '60'):

ex.gt.SerialInference
package ex.gt model SerialInference { random IntVar observedMax ?: fixedInt(60) random IntVar populationSize ?: latentInt param IntVar nDraws ?: fixedInt(4) laws { // An example of a discrete prior: here in this historical example, // the unknown quantity of // interest was the number of tanks in the German WW2 army. // The distribution DiscreteUniform comes from the standard library. populationSize ~ DiscreteUniform(0, 101) // In contrast, we will construct SerialSampling by ourself. // "observedMax" will be matched to the variables marked as random in // SerialSampling.bl, while "populationSize, nDraws" will be matched to // the variables marked as param in SerialSampling.bl. observedMax | populationSize, nDraws ~ SerialSampling(populationSize, nDraws) } }

We obtain a credible interval from 61 to 91.

The implementation of the likelihood, SerialSampling, is shown below:

package ex.gt import static extension java.util.Collections.shuffle model SerialSampling { random IntVar maxRealization param IntVar populationSize param IntVar nDraws laws { // This illustrates a second method from specifying a (conditional) // distribution: writing a function that computes the (log) of the // (conditional) probability. logf(maxRealization; populationSize, nDraws) { // The probability is 0 (i.e. the log probability is minus infinity) // when the parameters are invalid. if (populationSize < 1 || nDraws < 1 || maxRealization < 0) { return NEGATIVE_INFINITY } if (maxRealization + 1 < nDraws) { return NEGATIVE_INFINITY } if (populationSize <= maxRealization) { return NEGATIVE_INFINITY } return logBinomial(maxRealization, nDraws - 1) - logBinomial(populationSize, nDraws) } } // This part specifies how to *sample* from the probability model. You can think of rand // as being an outcome, and the code below is then responsible to evaluate the random // variable at that realization. generate(rand) { val List<Integer> list = (0 ..< populationSize).toList list.shuffle(rand) return list.subList(0, nDraws).max } }