From 59df2a7f0494e3f6fe922df08051a296da3ed573 Mon Sep 17 00:00:00 2001 From: rtrangucci Date: Thu, 11 Dec 2025 15:41:12 -0800 Subject: [PATCH 1/3] Updated sum-to-zero stan code --- src/reference-manual/transforms.qmd | 42 +++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/src/reference-manual/transforms.qmd b/src/reference-manual/transforms.qmd index 4b67aaa27..afd36a832 100644 --- a/src/reference-manual/transforms.qmd +++ b/src/reference-manual/transforms.qmd @@ -543,24 +543,24 @@ $$ and $$ y_n = (S_n - x_{n + 1}) \frac{\sqrt{n (n + 1)}}{n}. $$ -The mathematical expression above is equivalently expressed within Stan as: +This transform is expressed in Stan code as: ``` stan - vector manual_sum_to_zero_jacobian(vector y) { - int N = num_elements(y); - vector[N + 1] z = zeros_vector(N + 1); + vector manual_sum_to_zero_jacobian(vector x) { + int N = size(x) - 1; + vector[N] y; + y[N] = -x[N+1] * sqrt(1 + 1. / N); real sum_w = 0; - for (n in 1:N) { - int i = N - n + 1; - real w = y[i] * inv_sqrt(i * (i + 1)); + for (n in 1:(N-1)) { + int i = N - n; + int i_p_1 = i + 1; + real w = y[i_p_1] * inv_sqrt(i_p_1 * (i_p_1 + 1)); sum_w += w; - z[i] += sum_w; - z[i + 1] -= w * i; + y[i] = (sum_w - x[i_p_1]) * sqrt(i_p_1 * i) / i; } - return z; + return(y); } ``` -Note that there is no `target +=` increment because the Jacobian is zero. ### Sum-to-zero vector inverse transform {.unnumbered} @@ -585,6 +585,26 @@ subtracted from $x_{n + 1}$ the number of times it shows up in earlier terms. The inverse transform is a linear operation, leading to a constant Jacobian determinant which is therefore not included. +The sum-to-zero inverse transform is expressed within Stan as: + +``` stan + vector manual_sum_to_zero_inverse_jacobian(vector y) { + int N = num_elements(y); + vector[N + 1] z = zeros_vector(N + 1); + real sum_w = 0; + for (n in 1:N) { + int i = N - n + 1; + real w = y[i] * inv_sqrt(i * (i + 1)); + sum_w += w; + z[i] += sum_w; + z[i + 1] -= w * i; + } + return z; + } +``` + +Note that there is no `target +=` increment because the Jacobian is zero. + ### Sum-to-zero matrix transform {.unnumbered} The matrix case of the sum-to-zero transform generalizes the vector case to From 0d64beed7668527fcc3f93024e043ff1ca9247db Mon Sep 17 00:00:00 2001 From: rtrangucci Date: Thu, 11 Dec 2025 15:56:25 -0800 Subject: [PATCH 2/3] Fixed parentheses around return value --- src/reference-manual/transforms.qmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/reference-manual/transforms.qmd b/src/reference-manual/transforms.qmd index afd36a832..9c5ced8c8 100644 --- a/src/reference-manual/transforms.qmd +++ b/src/reference-manual/transforms.qmd @@ -558,7 +558,7 @@ This transform is expressed in Stan code as: sum_w += w; y[i] = (sum_w - x[i_p_1]) * sqrt(i_p_1 * i) / i; } - return(y); + return y; } ``` From 58c329891cd89f6ca90b5f26042a6992b40ac70e Mon Sep 17 00:00:00 2001 From: rtrangucci Date: Thu, 11 Dec 2025 15:58:07 -0800 Subject: [PATCH 3/3] switch z to x to match doc --- src/reference-manual/transforms.qmd | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/reference-manual/transforms.qmd b/src/reference-manual/transforms.qmd index 9c5ced8c8..936797446 100644 --- a/src/reference-manual/transforms.qmd +++ b/src/reference-manual/transforms.qmd @@ -590,16 +590,16 @@ The sum-to-zero inverse transform is expressed within Stan as: ``` stan vector manual_sum_to_zero_inverse_jacobian(vector y) { int N = num_elements(y); - vector[N + 1] z = zeros_vector(N + 1); + vector[N + 1] x = zeros_vector(N + 1); real sum_w = 0; for (n in 1:N) { int i = N - n + 1; real w = y[i] * inv_sqrt(i * (i + 1)); sum_w += w; - z[i] += sum_w; - z[i + 1] -= w * i; + x[i] += sum_w; + x[i + 1] -= w * i; } - return z; + return x; } ```