# Jacobian adjustment for parameters with parameter-based upper/lower bounds

**URL:** <https://discourse.mc-stan.org/t/jacobian-adjustment-for-parameters-with-parameter-based-upper-lower-bounds/41472>\
**Category:** Modeling\
**Created:** [July 27, 2026, 6:23am UTC](https://discourse.mc-stan.org/t/jacobian-adjustment-for-parameters-with-parameter-based-upper-lower-bounds/41472 "2026-07-27T06:23:04Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![satpal](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.mc-stan.org/satpal/32/18639_2.png) [@satpal](https://discourse.mc-stan.org/u/satpal)\
**Post date:** [July 27, 2026, 6:23am UTC](https://discourse.mc-stan.org/t/jacobian-adjustment-for-parameters-with-parameter-based-upper-lower-bounds/41472/1 "2026-07-27T06:23:04Z")

</div>

* * *

Hi all,

I’m trying to understand whether Jacobian adjustment is needed when a parameter is used as the upper and/or lower bound for other parameters.

For example,

```stan
parameters {
  vector<lower=0, upper=10>[k] a1;
  vector<lower=a1, upper=10>[k] a2;
  vector<lower=a2, upper=10>[k] a3;

  vector<lower=0, upper=10>[k] b1;
  vector<lower=b1, upper=10>[k] b2;
  vector<lower=b1 + b2, upper=10>[k] b3;
}

model {
  a1 ~ normal(0, 2);
  a2 ~ normal(0, 2);
  a3 ~ normal(0, 2);
  
  b1 ~ normal(0, 2);
  b2 ~ normal(0, 2);
  b3 ~ normal(0, 2);
}

```

I believe that when the upper and lower bounds are constants, as in `a1 and b1`, no additional Jacobian adjustment is needed.

My questions are:

- Does Stan automatically handle the constraint transformation for `a2` and `a3`, or do we need to add an explicit Jacobian adjustment in the `model` block?

- Does the answer change when the upper and lower bounds are parameters, for example `vector<lower=a1, upper=a3>[k] a2`?

- Does it make a difference if we include multiple parameters with parameter-dependent upper and lower bounds, as shown in the code for `b3`?

Any clarification would be greatly appreciated.

Thanks.

* * *

---

<div class="post-metadata">

**Author:** ![spinkney](https://avatars.discourse-cdn.com/v4/letter/s/dec6dc/32.png) [@spinkney](https://discourse.mc-stan.org/u/spinkney)\
**Post date:** [July 27, 2026, 11:26am UTC](https://discourse.mc-stan.org/t/jacobian-adjustment-for-parameters-with-parameter-based-upper-lower-bounds/41472/2 "2026-07-27T11:26:01Z")

</div>

When using the built-in constraints in the parameters block the Jacobian adjustments are performed automatically.

For upper or lower bound constraints the Jacobian adjustment is non-linear and whether the bounds are parameters or not doesn’t matter. Typically the adjustment is desirable because then you can use the adjusted parameter on its constrained domain like any other parameter in your model. There are occasions when placing a prior on the unadjusted parameter(s) are preferred but these would be declared without bounds or other constraints. For example, take a standard deviation parameter `sigma`, I might want to place a `skew_normal` prior on `log(sigma)`. I could declare `sigma` with a lower bound of 0 then take the log of it and place the prior on this but I would need a Jacobian adjustment. More efficiently I could declare an unbounded parameter `log_sigma` and place the prior directly on this. Subsequently, I would put `exp(log_sigma)` as the standard deviation into the normal density (let’s assume I’m modeling the s.d. of the normal) and I don’t need any Jacobian adjustment.

---

<div class="post-metadata">

**Author:** ![satpal](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.mc-stan.org/satpal/32/18639_2.png) [@satpal](https://discourse.mc-stan.org/u/satpal)\
**Post date:** [July 27, 2026, 12:34pm UTC](https://discourse.mc-stan.org/t/jacobian-adjustment-for-parameters-with-parameter-based-upper-lower-bounds/41472/3 "2026-07-27T12:34:15Z")

</div>

Thanks @spinkney

In my case, the parameters are regression coefficients. Let’s say `a1`, `a2`, and `a3` are the raw, unconstrained parameters, and then in the `transformed parameters` block I construct `b1`, `b2`, and `b3` using parameter-dependent upper and lower bounds.

For example, something like:

```stan
parameters {
  vector[k] a1;
  vector[k] a2;
  vector[k] a3;
}
transformed parameters {
  vector<lower=0, upper=a2>[k] b1;
  vector<lower=a1, upper=a3>[k] b2;
  vector<lower=a2, upper=10>[k] b3;
}
model {
  a1 ~ normal(0, 2);
  a2 ~ normal(0, 2);
  a3 ~ normal(0, 2);
}

```

In this setup, my priors are on the unconstrained `a1`, `a2`, and `a3`, not on the bounded `b1`, `b2`, and `b3`. In that case, would I still need to include an explicit Jacobian adjustment, if so, then could you please show how to add the Jacobian

My understanding is that if the priors are assigned to the unconstrained parameters, then the bounded parameters are just deterministic transformations, so the Jacobian would only be needed if I wanted to write the prior on the constrained scale instead. Is that correct?

I want `b1`, `b2`, and `b3` to be ordered / constrained `0 < b1 < b2 < b3 < 10`  
Could you please confirm whether this transformed-parameters setup is correct in Stan?

Thanks

---

<div class="post-metadata">

**Author:** ![spinkney](https://avatars.discourse-cdn.com/v4/letter/s/dec6dc/32.png) [@spinkney](https://discourse.mc-stan.org/u/spinkney)\
**Post date:** [July 27, 2026, 1:26pm UTC](https://discourse.mc-stan.org/t/jacobian-adjustment-for-parameters-with-parameter-based-upper-lower-bounds/41472/4 "2026-07-27T13:26:30Z")

</div>

You can use the `_jacobian` functions to perform the adjustments as I do in [Random walk with positive differences and missing data - #4 by spinkney](https://discourse.mc-stan.org/t/random-walk-with-positive-differences-and-missing-data/41470/4). You can find more documentation at [Variable Transformation Functions](https://mc-stan.org/docs/functions-reference/transform_functions.html).

---

<div class="post-metadata">

**Author:** ![satpal](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.mc-stan.org/satpal/32/18639_2.png) [@satpal](https://discourse.mc-stan.org/u/satpal)\
**Post date:** [July 27, 2026, 1:41pm UTC](https://discourse.mc-stan.org/t/jacobian-adjustment-for-parameters-with-parameter-based-upper-lower-bounds/41472/5 "2026-07-27T13:41:14Z")

</div>

Thank you for the pointer — that’s very helpful. I’ll look into the these functions.

Regards

---

<div class="post-metadata">

**Author:** ![Bob\_Carpenter](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.mc-stan.org/bob_carpenter/32/9230_2.png) [@Bob\_Carpenter](https://discourse.mc-stan.org/u/Bob_Carpenter)\
**Post date:** [August 4, 2026, 6:34pm UTC](https://discourse.mc-stan.org/t/jacobian-adjustment-for-parameters-with-parameter-based-upper-lower-bounds/41472/6 "2026-08-04T18:34:37Z")

</div>

> [@satpal](#):
>
> - Does Stan automatically handle the constraint transformation

Yes.

> [@satpal](#):
>
> - Does the answer change when the upper and lower bounds are parameters,

Yes, it will depend on those parameters in this case.

> [@satpal](#):
>
> - Does it make a difference if we include multiple parameters with parameter-dependent upper and lower bounds

It’s legal, but you should think through what the implied joint density is based on the Jacobian adjustments implied by the adjustments. Stan even lets you write things like this:

```no-highlight
y ~ normal(0, 1);
y ~ normal(0, 1);

```

If you work through the algebra, the result is equivalent to

```no-highlight
y ~ normal(0, 1 / sqrt(2));

```

> [@satpal](#):
>
> In this setup,

What you posted is not a well-formed Stan model because `b1`, `b2`, and `b3` are declared, but not defined. If you run it, the `b` values will all be not-a-number. So I’m not sure what the intent is here.

---

<div class="post-metadata">

**Author:** ![satpal](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.mc-stan.org/satpal/32/18639_2.png) [@satpal](https://discourse.mc-stan.org/u/satpal)\
**Post date:** [August 5, 2026, 12:30pm UTC](https://discourse.mc-stan.org/t/jacobian-adjustment-for-parameters-with-parameter-based-upper-lower-bounds/41472/7 "2026-08-05T12:30:37Z")

</div>

Thanks @Bob_Carpenter

I’m fitting a hierarchical piecewise spline model with subject-specific (random) change points. The outcome is a pain score measured repeatedly over time for each individual. The time variable is denoted `C_1​`, and the change points are the parameters `b_t1​,...,b_t6,`​, which are constrained to lie within certain bounds.

Below are my current `parameters` and `transformed parameters` blocks. The model code was generated using **brms** , which manually adds truncated-normal log densities to `lprior` for the bounded change-point parameters `b_t1​,...,b_t6`. Do I need to add any additional Jacobian adjustments for these bounded btb\_tbt​ parameters? Key points about my setup:

- The `b_t1​,...,b_t6` parameters are declared directly as bounded parameters (with data- and parameter-dependent bounds), not as nonlinear transforms of other parameters.

- brms is already assigning truncated-normal priors to them via explicit log-density terms in `lprior`.

- I do not apply any further nonlinear transformation to `b_t1​,...,b_t6` before using them in the likelihood.

Given this, is the current truncated-normal implementation sufficient, or is an extra Jacobian adjustment still required? I’d appreciate guidance on that as well.

Thanks

```stan
parameters {
  vector[K_cons] b_cons; // regression coefficients
  vector[K_s1] b_s1; // regression coefficients
  vector[K_s2] b_s2; // regression coefficients
  vector[K_s3] b_s3; // regression coefficients
  vector[K_s4] b_s4; // regression coefficients
  vector[K_s5] b_s5; // regression coefficients
  vector[K_s6] b_s6; // regression coefficients
  vector[K_s7] b_s7; // regression coefficients
  vector<lower=min(C_1),upper=max(C_1)>[K_t1] b_t1; // regression coefficients
  vector<lower=min(C_1) + b_t1,upper=max(C_1)>[K_t2] b_t2; // regression coefficients
  vector<lower=min(C_1) + b_t2,upper=max(C_1)>[K_t3] b_t3; // regression coefficients
  vector<lower=min(C_1) + b_t3,upper=max(C_1)>[K_t4] b_t4; // regression coefficients
  vector<lower=min(C_1) + b_t4,upper=max(C_1)>[K_t5] b_t5; // regression coefficients
  vector<lower=min(C_1) + b_t5,upper=max(C_1)>[K_t6] b_t6; // regression coefficients
  real<lower=0> sigma; // dispersion parameter
  vector<lower=0>[M_1] sd_1; // group-level standard deviations
  matrix[M_1, N_1] z_1; // standardized group-level effects
  cholesky_factor_corr[M_1] L_1; // cholesky factor of correlation matrix
}
transformed parameters {
  matrix[N_1, M_1] r_1; // actual group-level effects
  // using vectors speeds up indexing in loops
  vector[N_1] r_1_cons_1;
  vector[N_1] r_1_s1_2;
  vector[N_1] r_1_s2_3;
  vector[N_1] r_1_s3_4;
  vector[N_1] r_1_s4_5;
  vector[N_1] r_1_s5_6;
  vector[N_1] r_1_s6_7;
  vector[N_1] r_1_s7_8;
  vector[N_1] r_1_t1_9;
  vector[N_1] r_1_t2_10;
  vector[N_1] r_1_t3_11;
  vector[N_1] r_1_t4_12;
  vector[N_1] r_1_t5_13;
  vector[N_1] r_1_t6_14;
  // prior contributions to the log posterior
  real lprior = 0;
  // compute actual group-level effects
  r_1 = scale_r_cor(z_1, sd_1, L_1);
  r_1_cons_1 = r_1[, 1];
  r_1_s1_2 = r_1[, 2];
  r_1_s2_3 = r_1[, 3];
  r_1_s3_4 = r_1[, 4];
  r_1_s4_5 = r_1[, 5];
  r_1_s5_6 = r_1[, 6];
  r_1_s6_7 = r_1[, 7];
  r_1_s7_8 = r_1[, 8];
  r_1_t1_9 = r_1[, 9];
  r_1_t2_10 = r_1[, 10];
  r_1_t3_11 = r_1[, 11];
  r_1_t4_12 = r_1[, 12];
  r_1_t5_13 = r_1[, 13];
  r_1_t6_14 = r_1[, 14];
  lprior += normal_lpdf(b_cons | 0, 1);
  lprior += normal_lpdf(b_s1 | 0, 5);
  lprior += normal_lpdf(b_s2 | 0, 5);
  lprior += normal_lpdf(b_s3 | 0, 5);
  lprior += normal_lpdf(b_s4 | 0, 5);
  lprior += normal_lpdf(b_s5 | 0, 5);
  lprior += normal_lpdf(b_s6 | 0, 5);
  lprior += normal_lpdf(b_s7 | 0, 5);
  lprior += normal_lpdf(b_t1 | 0, 5)
    - 1 * log_diff_exp(normal_lcdf(max(C_1) | 0, 5), normal_lcdf(min(C_1) | 0, 5));
  lprior += normal_lpdf(b_t2 | 0, 5)
    - 1 * log_diff_exp(normal_lcdf(max(C_1) | 0, 5), normal_lcdf(min(C_1) + b_t1 | 0, 5));
  lprior += normal_lpdf(b_t3 | 0, 5)
    - 1 * log_diff_exp(normal_lcdf(max(C_1) | 0, 5), normal_lcdf(min(C_1) + b_t2 | 0, 5));
  lprior += normal_lpdf(b_t4 | 0, 5)
    - 1 * log_diff_exp(normal_lcdf(max(C_1) | 0, 5), normal_lcdf(min(C_1) + b_t3 | 0, 5));
  lprior += normal_lpdf(b_t5 | 0, 5)
    - 1 * log_diff_exp(normal_lcdf(max(C_1) | 0, 5), normal_lcdf(min(C_1) + b_t4 | 0, 5));
  lprior += normal_lpdf(b_t6 | 0, 5)
    - 1 * log_diff_exp(normal_lcdf(max(C_1) | 0, 5), normal_lcdf(min(C_1) + b_t5 | 0, 5));
  lprior += student_t_lpdf(sigma | 3, 0, 11.9)
    - 1 * student_t_lccdf(0 | 3, 0, 11.9);
  lprior += normal_lpdf(sd_1[1] | 0, 1)
    - 1 * normal_lccdf(0 | 0, 1);
  lprior += normal_lpdf(sd_1[2] | 0, 2)
    - 1 * normal_lccdf(0 | 0, 2);
  lprior += normal_lpdf(sd_1[3] | 0, 2)
    - 1 * normal_lccdf(0 | 0, 2);
  lprior += normal_lpdf(sd_1[4] | 0, 2)
    - 1 * normal_lccdf(0 | 0, 2);
  lprior += normal_lpdf(sd_1[5] | 0, 2)
    - 1 * normal_lccdf(0 | 0, 2);
  lprior += normal_lpdf(sd_1[6] | 0, 2)
    - 1 * normal_lccdf(0 | 0, 2);
  lprior += normal_lpdf(sd_1[7] | 0, 2)
    - 1 * normal_lccdf(0 | 0, 2);
  lprior += normal_lpdf(sd_1[8] | 0, 2)
    - 1 * normal_lccdf(0 | 0, 2);
  lprior += normal_lpdf(sd_1[9] | 0, 5)
    - 1 * normal_lccdf(0 | 0, 5);
  lprior += normal_lpdf(sd_1[10] | 0, 5)
    - 1 * normal_lccdf(0 | 0, 5);
  lprior += normal_lpdf(sd_1[11] | 0, 5)
    - 1 * normal_lccdf(0 | 0, 5);
  lprior += normal_lpdf(sd_1[12] | 0, 5)
    - 1 * normal_lccdf(0 | 0, 5);
  lprior += normal_lpdf(sd_1[13] | 0, 5)
    - 1 * normal_lccdf(0 | 0, 5);
  lprior += normal_lpdf(sd_1[14] | 0, 5)
    - 1 * normal_lccdf(0 | 0, 5);
  lprior += lkj_corr_cholesky_lpdf(L_1 | 1);
}

```

---

<div class="post-metadata">

**Author:** ![Bob\_Carpenter](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.mc-stan.org/bob_carpenter/32/9230_2.png) [@Bob\_Carpenter](https://discourse.mc-stan.org/u/Bob_Carpenter)\
**Post date:** [August 5, 2026, 6:59pm UTC](https://discourse.mc-stan.org/t/jacobian-adjustment-for-parameters-with-parameter-based-upper-lower-bounds/41472/8 "2026-08-05T18:59:43Z")

</div>

> [@satpal](#):
>
> Do I need to add any additional Jacobian adjustments for these bounded btb\_tbt​ parameters?

No. But if you give them distributions based on parameters, then you need the truncation statements. That is, if `mu` and `sigma` are parameters, then you need to write:

```stan
parameters {
  real mu;
  real<lower=0> sigma;
  vector<lower=LB, upper=UB> [N] alpha;
  ...
model {
  alpha ~ normal(0, 2); // (1)
  alpha ~ normal(mu, sigma) T[LB, UB]; // (2)
  ...

```

In case (1), you don’t need the truncation bound because the parameters to the normal are constants. For case (2), you need the truncation because it contributes a non-constant term (that depends on `mu` and `sigma`) to the log density.

For example, here you have a parameter `b_t4` involved in the lower bound of `b_t5`.

```stan
vector<lower=min(C_1) + b_t4,upper=max(C_1)>[K_t5] b_t5; // regression coefficients
...
prior += normal_lpdf(b_t5 | 0, 5)
    - 1 * log_diff_exp(normal_lcdf(max(C_1) | 0, 5), normal_lcdf(min(C_1) + b_t4 | 0, 5));

```

This is correctly doing the adjustment. In general, never multiply by `-1`, just negate, so that `-1 * x` becomes `-x` (it’s faster and uses less memory). You can reduce the `lpdf` to `lupdf` to drop unnecessary normalizing constants.

I’m not sure why you’re defining `prior` as a variable as we have a built in that would let you write this as

```stan
b_t5 ~ normal_lpdf(0, 5) T[max(C_1), min(C_1) + b_t4];

```

You didn’t provide the model, but I take it `C_1` is data. I’m generally wary of putting hard boundaries on parameters based on data. For example, if I have a observations y\_1, \ldots, y\_N, I fit

```stan
y ~ normal(mu, sigma);

```

I wouldn’t declare `mu` as

```no-highlight
real<lower=min(y), upper=max(y)> mu;

```

It may be that `mu` values outside the data range are consistent with the data and putting the hard boundaries will give you a different answer even when you expect your posterior mean to lie in that interval.

---

<div class="post-metadata">

**Author:** ![satpal](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.mc-stan.org/satpal/32/18639_2.png) [@satpal](https://discourse.mc-stan.org/u/satpal)\
**Post date:** [August 6, 2026, 3:50pm UTC](https://discourse.mc-stan.org/t/jacobian-adjustment-for-parameters-with-parameter-based-upper-lower-bounds/41472/9 "2026-08-06T15:50:26Z")

</div>

Thank you @Bob_Carpenter

This is very helpful, as always!

Regards
