Wow, that looks weird.
If that helps anybody, I tried building a Stan model that should match all of the operations and their order as in the offset-multiplier (mod1) and it still does not match the (bad) results in mod1:
mod3.stan
:
parameters{
real<lower = 0> sigma;
real x_raw;
}
transformed parameters {
}
model{
real x;
target += log(sigma);
x = fma(sigma, x_raw, 0);
sigma ~ std_normal();
x ~ normal(0, sigma);
}
So the only remaining difference IMHO is that in mod1 the constraining transform overwrites the x
local variable while in mod3 we create a new local variable to hold the result… Which should not matter much?
BTW, I’ve always found the offset-multiplier way of doing stuff confusing as a) it requires me to use sigma
at two potentially distant places in the code, increasing risk I will change one but not the other in the future and b) I always have to think hard whether the multiplier is from unconstrained-to-constrained or the other way around. In fact, our own manual used to have the wrong way! (contrast 2.26 manual with the current version, see also docs issue #356 )
In addition the offset-multiplier logic (both manual and controled by Stan) adds unnecessary operations to the model: log(sigma)
is added in the Jacobian correction, only to be subtracted in x ~ normal(0, sigma)
, with a penalty for both performance and numerical precision, so I think the best way to code non-centering is the IMHO simpler:
parameters{
real<lower = 0> sigma;
real x_raw;
}
transformed parameters {
real x = x_raw * sigma;
}
model{
sigma ~ std_normal();
x_raw ~ std_normal();
}