Half-normal, Half-Cauchy and Half-t


How can I specify half-normal, half-Cauchy or half-t priors in stan?


you just put a boundary on the parameter. For example

alpha ~ cauchy(...)

Although this is not a truncated distribution. Can someone back this up @martinmodrak?


How’s it not truncated?

There’s an implicit T[0, ] after it, so to speak. Because the lower boundary is zero, it drops a constant amount, so the T[0,] is unneeded.


That’s right.

naively I would assume that the mode would be ~ 0 with a truncated standard normal (at 0) distribution but it does not seem so

I see two concerns here:

  1. Specifying half-normal (or similar) priors. In this case using parameter bounds + the unmodified distribution is the usual (and almost always recommended) way.
parameters {
  real<lower=0> sigma;
model {
  sigma ~ normal(0, 1);
  1. How are parameter bounds related to truncation.

For 2), if I understand things correctly, there is no “implicit T[0,]”. Stan does very little magic around the parameters. What happens is that sigma is not allowed to take negative values, but the density is computed with a non-truncated distribution. This doesn’t matter, because the difference here is a constant and we usually care about the posterior only up to a constant. But in cases where you care about the precise likelihood (e.g. to compute loo or other similar stuff), those constant may be important, so you would want to have something like:

target += normal_lpdf(sigma | 0, 1) - normal_lcdf(0 | 0, 1);

(note that brms does this under the hood)

Similarly, explicit truncation will be AFAIK needed if the bound was not fixed.

Does that make sense?

I think what you are seeing here is just an artifact of sampling - with low enough number of samples and narrow enough bins, you sometimes see this, but if you rerun the simulation multiple times, you’ll see that the histogram just randomly fluctuates around the expected shape.

(here’s my code to generate simulations with tidyverse:

data.frame(x = rnorm(1e5,0, 1)) %>% filter(x > 0) %>% pull(x) %>% hist(breaks = 100)

Do you see the same if you sample from a half normal from stan? alpha ~ normal(0,1);?

Yes. Here’s my code:


model_code <- "
parameters {
   real<lower=0> alpha;

model {
  alpha ~ normal(0, 1);


model <- stan_model(model_code = model_code)

fit <- sampling(model, chains = 1)
hist(as.matrix(fit, pars = "alpha"), breaks = 100)

Jumps around roughly as much as I would expect.

1 Like

Hi, I did not see a clear answer. How to specific these 3 half-distributions? Thanks.

1 Like

Just specify the constraint.

parameters {
  real<lower=0> a;
  real<lower=0> b;
  real<lower=0> c;
model {
  a ~ normal(0, 1);
  b ~ cauchy(0, 1);
  c ~ student_t(3, 0, 1);