@@ -50,7 +50,7 @@ inline plain_type_t<Vec> sum_to_zero_constrain(const Vec& y) {
5050 value_type_t <Vec> sum_w (0 );
5151 for (int i = N; i > 0 ; --i) {
5252 double n = static_cast <double >(i);
53- auto w = y_ref (i - 1 ) * inv_sqrt (n * (n + 1 ));
53+ auto w = y_ref. coeff (i - 1 ) * inv_sqrt (n * (n + 1 ));
5454 sum_w += w;
5555
5656 z.coeffRef (i - 1 ) += sum_w;
@@ -61,47 +61,81 @@ 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+
79+ plain_type_t <Mat> Z = Eigen::MatrixXd::Zero (N + 1 , M + 1 );
80+ if (unlikely (N == 0 || M == 0 )) {
81+ return Z;
82+ }
83+ auto && x_ref = to_ref (x);
84+
85+ Eigen::Matrix<value_type_t <Mat>, -1 , 1 > beta = Eigen::VectorXd::Zero (N);
86+
87+ for (int j = M - 1 ; j >= 0 ; --j) {
88+ value_type_t <Mat> ax_previous (0 );
89+
90+ double a_j = inv_sqrt ((j + 1.0 ) * (j + 2.0 ));
91+ double b_j = (j + 1.0 ) * a_j;
92+
93+ for (int i = N - 1 ; i >= 0 ; --i) {
94+ double a_i = inv_sqrt ((i + 1.0 ) * (i + 2.0 ));
95+ double b_i = (i + 1.0 ) * a_i;
96+
97+ auto b_i_x = b_i * x_ref.coeff (i, j) - ax_previous;
98+
99+ Z.coeffRef (i, j) = (b_j * b_i_x) - beta.coeff (i);
100+ beta.coeffRef (i) += a_j * b_i_x;
101+
102+ Z.coeffRef (N, j) -= Z.coeff (i, j);
103+ Z.coeffRef (i, M) -= Z.coeff (i, j);
104+
105+ ax_previous += a_i * x_ref.coeff (i, j);
106+ }
107+ Z.coeffRef (N, M) -= Z.coeff (N, j);
108+ }
109+
110+ return Z;
111+ }
112+
113+ /* *
114+ * Return a vector or matrix with sum zero corresponding to the specified
115+ * free input.
80116 *
81117 * This is a linear transform, with no Jacobian.
82118 *
83- * @tparam Vec type of the vector
119+ * @tparam T type of the input, either a vector or a matrix
84120 * @tparam Lp unused
85- * @param y Free vector input of dimensionality K - 1.
121+ * @param y Free vector or matrix
86122 * @param lp unused
87- * @return Zero-sum vector of dimensionality K.
123+ * @return Zero-sum vector or matrix which is one larger in each dimension
88124 */
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) {
125+ template <typename T, typename Lp, require_not_st_var<T>* = nullptr >
126+ inline plain_type_t <T> sum_to_zero_constrain (const T& y, Lp& lp) {
92127 return sum_to_zero_constrain (y);
93128}
94129
95130/* *
96- * Return a vector with sum zero corresponding to the specified
97- * free vector .
131+ * Return a vector or matrix with sum zero corresponding to the specified
132+ * free input .
98133 * This overload handles looping over the elements of a standard vector.
99134 *
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`
135+ * @tparam T A standard vector with inner type that is either a vector or a
136+ * matrix
137+ * @param[in] y free vector or matrix
138+ * @return Zero-sum vectors or matrices which are one larger in each dimension
105139 */
106140template <typename T, require_std_vector_t <T>* = nullptr >
107141inline auto sum_to_zero_constrain (const T& y) {
@@ -114,13 +148,12 @@ inline auto sum_to_zero_constrain(const T& y) {
114148 * free vector.
115149 * This overload handles looping over the elements of a standard vector.
116150 *
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
151+ * @tparam T A standard vector with inner type that is either a vector or a
152+ * matrix
120153 * @tparam Lp unused
121- * @param[in] y free vector
154+ * @param[in] y free vector or matrix
122155 * @param[in, out] lp unused
123- * @return Zero-sum vectors of dimensionality one greater than `y`
156+ * @return Zero-sum vectors or matrices which are one larger in each dimension
124157 */
125158template <typename T, typename Lp, require_std_vector_t <T>* = nullptr ,
126159 require_convertible_t <return_type_t <T>, Lp>* = nullptr >
@@ -130,36 +163,19 @@ inline auto sum_to_zero_constrain(const T& y, Lp& lp) {
130163}
131164
132165/* *
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- *
166+ * Return a vector or matrix with sum zero corresponding to the specified
167+ * free input.
150168 * This is a linear transform, with no Jacobian.
151169 *
152170 * @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
171+ * @tparam T type of the input
156172 * @tparam Lp unused
157- * @param[in] y free vector
173+ * @param[in] y free vector or matrix
158174 * @param[in, out] lp unused
159- * @return Zero-sum vector of dimensionality one greater than `y`
175+ * @return Zero-sum vector or matrix which is one larger in each dimension
160176 */
161- template <bool Jacobian, typename Vec , typename Lp>
162- inline plain_type_t <Vec > sum_to_zero_constrain (const Vec & y, Lp& lp) {
177+ template <bool Jacobian, typename T , typename Lp>
178+ inline plain_type_t <T > sum_to_zero_constrain (const T & y, Lp& lp) {
163179 return sum_to_zero_constrain (y);
164180}
165181
0 commit comments