@@ -61,47 +61,82 @@ inline plain_type_t<Vec> sum_to_zero_constrain(const Vec& y) {
6161}
6262
6363/* *
64- * Return a vector with sum zero corresponding to the specified
65- * free vector .
64+ * Return a matrix that sums to zero over both the rows
65+ * and columns corresponding to the free matrix x .
6666 *
67- * The sum-to-zero transform is defined using a modified version of the
68- * the inverse of the isometric log ratio transform (ILR).
69- * See:
70- * Egozcue, Juan Jose; Pawlowsky-Glahn, Vera; Mateu-Figueras, Gloria;
71- * Barcelo-Vidal, Carles (2003), "Isometric logratio transformations for
72- * compositional data analysis", Mathematical Geology, 35 (3): 279–300,
73- * doi:10.1023/A:1023818214614, S2CID 122844634
67+ * This is a linear transform, with no Jacobian.
7468 *
75- * This implementation is closer to the description of the same using "pivot
76- * coordinates" in
77- * Filzmoser, P., Hron, K., Templ, M. (2018). Geometrical Properties of
78- * Compositional Data. In: Applied Compositional Data Analysis. Springer Series
79- * in Statistics. Springer, Cham. https://doi.org/10.1007/978-3-319-96422-5_3
69+ * @tparam Mat type of the matrix
70+ * @param x Free matrix input of dimensionality (N - 1, M - 1).
71+ * @return Zero-sum matrix of dimensionality (N, M).
72+ */
73+ template <typename Mat, require_eigen_matrix_dynamic_t <Mat>* = nullptr ,
74+ require_not_st_var<Mat>* = nullptr >
75+ inline plain_type_t <Mat> sum_to_zero_constrain (const Mat& x) {
76+ const auto N = x.rows ();
77+ const auto M = x.cols ();
78+ const auto s = std::max (N, M);
79+
80+ plain_type_t <Mat> Z = Eigen::MatrixXd::Zero (N + 1 , M + 1 );
81+ if (unlikely (N == 0 || M == 0 )) {
82+ return Z;
83+ }
84+ auto && x_ref = to_ref (x);
85+
86+ Eigen::VectorXd beta = Eigen::VectorXd::Zero (N);
87+
88+ for (int j = M - 1 ; j >= 0 ; --j) {
89+ value_type_t <Mat> ax_previous (0 );
90+
91+ double a_j = 1.0 / std::sqrt ((j + 1.0 ) * (j + 2.0 ));
92+ double b_j = (j + 1.0 ) * a_j;
93+
94+ for (int i = N - 1 ; i >= 0 ; --i) {
95+ double a_i = 1.0 / std::sqrt ((i + 1.0 ) * (i + 2.0 ));
96+ double b_i = (i + 1.0 ) * a_i;
97+
98+ auto b_i_x = b_i * x_ref (i, j) - ax_previous;
99+
100+ Z (i, j) = (b_j * b_i_x) - beta (i);
101+ beta (i) += a_j * b_i_x;
102+
103+ Z (N, j) -= Z (i, j);
104+ Z (i, M) -= Z (i, j);
105+
106+ ax_previous += a_i * x_ref (i, j);
107+ }
108+ Z (N, M) -= Z (N, j);
109+ }
110+
111+ return Z;
112+ }
113+
114+ /* *
115+ * Return a vector or matrix with sum zero corresponding to the specified
116+ * free input.
80117 *
81118 * This is a linear transform, with no Jacobian.
82119 *
83- * @tparam Vec type of the vector
120+ * @tparam T type of the input, either a vector or a matrix
84121 * @tparam Lp unused
85- * @param y Free vector input of dimensionality K - 1.
122+ * @param y Free vector or matrix
86123 * @param lp unused
87- * @return Zero-sum vector of dimensionality K.
124+ * @return Zero-sum vector or matrix which is one larger in each dimension
88125 */
89- template <typename Vec, typename Lp, require_eigen_col_vector_t <Vec>* = nullptr ,
90- require_not_st_var<Vec>* = nullptr >
91- inline plain_type_t <Vec> sum_to_zero_constrain (const Vec& y, Lp& lp) {
126+ template <typename T, typename Lp, require_not_st_var<T>* = nullptr >
127+ inline plain_type_t <T> sum_to_zero_constrain (const T& y, Lp& lp) {
92128 return sum_to_zero_constrain (y);
93129}
94130
95131/* *
96- * Return a vector with sum zero corresponding to the specified
97- * free vector .
132+ * Return a vector or matrix with sum zero corresponding to the specified
133+ * free input .
98134 * This overload handles looping over the elements of a standard vector.
99135 *
100- * @tparam Vec A standard vector with inner type inheriting from
101- * `Eigen::DenseBase` or a `var_value` with inner type inheriting from
102- * `Eigen::DenseBase` with compile time dynamic rows and 1 column
103- * @param[in] y free vector
104- * @return Zero-sum vectors of dimensionality one greater than `y`
136+ * @tparam T A standard vector with inner type that is either a vector or a
137+ * matrix
138+ * @param[in] y free vector or matrix
139+ * @return Zero-sum vectors or matrices which are one larger in each dimension
105140 */
106141template <typename T, require_std_vector_t <T>* = nullptr >
107142inline auto sum_to_zero_constrain (const T& y) {
@@ -114,13 +149,12 @@ inline auto sum_to_zero_constrain(const T& y) {
114149 * free vector.
115150 * This overload handles looping over the elements of a standard vector.
116151 *
117- * @tparam Vec A standard vector with inner type inheriting from
118- * `Eigen::DenseBase` or a `var_value` with inner type inheriting from
119- * `Eigen::DenseBase` with compile time dynamic rows and 1 column
152+ * @tparam T A standard vector with inner type that is either a vector or a
153+ * matrix
120154 * @tparam Lp unused
121- * @param[in] y free vector
155+ * @param[in] y free vector or matrix
122156 * @param[in, out] lp unused
123- * @return Zero-sum vectors of dimensionality one greater than `y`
157+ * @return Zero-sum vectors or matrices which are one larger in each dimension
124158 */
125159template <typename T, typename Lp, require_std_vector_t <T>* = nullptr ,
126160 require_convertible_t <return_type_t <T>, Lp>* = nullptr >
@@ -130,36 +164,19 @@ inline auto sum_to_zero_constrain(const T& y, Lp& lp) {
130164}
131165
132166/* *
133- * Return a vector with sum zero corresponding to the specified
134- * free vector.
135- *
136- * The sum-to-zero transform is defined using a modified version of
137- * the inverse of the isometric log ratio transform (ILR).
138- * See:
139- * Egozcue, Juan Jose; Pawlowsky-Glahn, Vera; Mateu-Figueras, Gloria;
140- * Barcelo-Vidal, Carles (2003), "Isometric logratio transformations for
141- * compositional data analysis", Mathematical Geology, 35 (3): 279–300,
142- * doi:10.1023/A:1023818214614, S2CID 122844634
143- *
144- * This implementation is closer to the description of the same using "pivot
145- * coordinates" in
146- * Filzmoser, P., Hron, K., Templ, M. (2018). Geometrical Properties of
147- * Compositional Data. In: Applied Compositional Data Analysis. Springer Series
148- * in Statistics. Springer, Cham. https://doi.org/10.1007/978-3-319-96422-5_3
149- *
167+ * Return a vector or matrix with sum zero corresponding to the specified
168+ * free input.
150169 * This is a linear transform, with no Jacobian.
151170 *
152171 * @tparam Jacobian unused
153- * @tparam Vec A type inheriting from `Eigen::DenseBase` or a `var_value` with
154- * inner type inheriting from `Eigen::DenseBase` with compile time dynamic rows
155- * and 1 column, or a standard vector thereof
172+ * @tparam T type of the input
156173 * @tparam Lp unused
157- * @param[in] y free vector
174+ * @param[in] y free vector or matrix
158175 * @param[in, out] lp unused
159- * @return Zero-sum vector of dimensionality one greater than `y`
176+ * @return Zero-sum vector or matrix which is one larger in each dimension
160177 */
161- template <bool Jacobian, typename Vec , typename Lp>
162- inline plain_type_t <Vec > sum_to_zero_constrain (const Vec & y, Lp& lp) {
178+ template <bool Jacobian, typename T , typename Lp>
179+ inline plain_type_t <T > sum_to_zero_constrain (const T & y, Lp& lp) {
163180 return sum_to_zero_constrain (y);
164181}
165182
0 commit comments