Your link won’t load for me but I found this which I think is either the same paper or closely related.
This observe
function creates a conditional distribution so I guess the closest equivalent in Stan is a sampling statement. However, observe uses discrete logic that cannot really be expressed in Stan.
Let’s take a look at the Skill Rating In Online Games example found in section 4 of the paper I linked.
The idea is that three players, A, B, and C, play two games, first A verses B and then B versus C, and the we’ll model their skills. The code is
float skillA, skillB,skillC;
float perfA1,perfB1,perfB2,
perfC2,perfA3,perfC3;
skillA = Gaussian(100,10);
skillB = Gaussian(100,10);
skillC = Gaussian(100,10);
// first game:A vs B, A won
perfA1 = Gaussian(skillA,15);
perfB1 = Gaussian(skillB,15);
observe(perfA1 > perfB1);
// second game:B vs C, B won
perfB2 = Gaussian(skillB,15);
perfC2 = Gaussian(skillC,15);
observe(perfB2 > perfC2);
// third game:A vs C, A won
perfA3 = Gaussian(skillA,15);
perfC3 = Gaussian(skillC,15);
observe(perfA3 > perfC3);
Here the calls to observe
tell the model who wins each game.
But performance supposed to be the player’s total score, right? It’s known so why not just model it directly instead of only “observing” the winner. I’d write a Stan model like this:
data {
real perfA;
real perfB1;
real perfB2;
real perfC;
} parameters {
real skillA;
real skillB;
real skillC;
} model {
skillA ~ normal(100, 10);
skillB1 ~ normal(100, 10);
skillB2 ~ normal(100, 10);
skillC ~ normal(100, 10);
perfA ~ normal(skillA, 15);
perfB1 ~ normal(skillB, 15);
perfB2 ~ normal(skillB, 15);
perfC ~ normal(skillC, 15);
}
If performance is not directly observable but a hypothetical latent variable that only exists to recognize the fact that the more skilled player does not always win it should be integrated out. Stan code is now
parameters {
real skillA;
real skillB;
real skillC;
} model {
skillA ~ normal(100, 10);
skillB ~ normal(100, 10);
skillC ~ normal(100, 10);
// first game, 0 means B won, 1 means A won
1 ~ bernoulli(normal_cdf(skillA, skillB, sqrt(2)*15));
// second game, 0 means C won, 1 means B won
1 ~ bernoulli(normal_cdf(skillB, skillC, sqrt(2)*15));
}
But fine, if you really want to keep perf
s as estimated unknowns you can declare the observed constraints in the parameters block
parameters {
real skillA;
real skillB;
real skillC;
real perfA;
real<upper=perfA> perfB1; // A won the first game
real perfB2;
real<upper=perfB2> perfC; // B won the second game
} model {
skillA ~ normal(100, 10);
skillB ~ normal(100, 10);
skillC ~ normal(100, 10);
perfA ~ normal(skillA, 15);
perfB1 ~ normal(skillB, 15);
perfB2 ~ normal(skillB, 15);
perfC ~ normal(skillC, 15);
}
The latter two models give almost (but strangely not exactly!) the same results as the paper. Here we see that Stan takes quite different approach to probabilistic modeling.