Skip to content

Feature request: row_centred_matrix and column_centred_matrix #3361

Description

@jaburgoyne

To facilitate working with the sum-to-zero constraint in multivariate contexts, it would be valuable to be able to specify not only individual sum_to_zero_vectors but also matrices for which all rows or all columns are constrained to sum to zero. To my knowledge, there is no universally standard name for such a mathematical object (nor to Claude's knowledge, which only produced a list of suggestions), but for the sake of starting a conversation, I would propose something like row_centered_matrix and col_centered_matrix (and perhaps row_centred_matrix and col_centred_matrix as aliases for spelling-dialect neutrality).

The primary use case for me would be hierarchical modelling with correlated multivariate random effects, e.g., random-slopes models or multivariate item-response models. To reduce problems with funnel geometries during sampling, it can be wise to write such models using ‘raw’ parameters with standard normal priors and then construct the actual parameters in the transformed parameters block via matrix multiplication with standard-deviation and correlation-matrix hyperpriors (e.g., the User's Guide chapter on ‘Regression Models’).

That's awkward to do using the sum_to_zero_vector type alone. The N raw parameter vectors (let's say of dimension K) have to be stored as array[K] sum_to_zero_vector[N]. This structure needs to be pulled apart and restructured as matrix[K, N] or array[N] vector[K] in transformed parameters in order to be multiplied by a cholesky_factor_corr[K]. In practice, the problem is mostly that this restructuring step is ugly and confusing to read, but in theory, it is also memory inefficient and could cause slowdowns at large model scale.

Activity

  1. transferred this issue fromstan-dev/stanon Aug 19, 2026
  2. bob-carpenter commented on Aug 21, 2026

    @bob-carpenter
    Member

    If you need a matrix where the rows sum to zero, then you can do this quite directly with our new built-in transform functions with Jacobians. Here's a user-defined function to do this along with a trivial program with a proper prior to run it.

    functions {
      matrix sum_to_zero_rows_jacobian(array[] vector alpha_raw) {
         int M = size(alpha_raw);
         if (M == 0) reject("require M > 0, found M = ", M);
         int N = rows(alpha_raw[1]) + 1;
         matrix[M, N] alpha;
         for (m in 1:M) {
           alpha[m] = sum_to_zero_jacobian(alpha_raw[m])';
         } 
        return alpha;
      }
    }
    data {
      int<lower=1> M;
      int<lower=1> N;
    }
    parameters {
      array[M] vector[N - 1] alpha_raw;
    }
    transformed parameters {
      matrix[M, N] alpha = sum_to_zero_rows_jacobian(alpha_raw);  // flat over sum-to-zero row matrices
    }
    model {
      to_vector(alpha) ~ normal(0, 1);
    }
    

    It'll get the job done, but it's definitely not as convenient as having a sum_to_zero_row_matrix type. It wouldn't be that hard to add the type to the language. A non-optimally-performance-tuned version would just copy this to C++ rather than collapse the autodiff tree (not a really huge savings here). But then there's writing the unit tests, expanding the reference manual chapter on transforms, and the user's guide lists of types. So overall, a bit of work.

    It would be nice if we had a way to declare a matrix with constraints on the rows or columns. As is, we have to multiply built-in types in a really clunky way.

  3. bob-carpenter commented on Aug 21, 2026

    @bob-carpenter
    Member

    Also, we'd use the American spellings for consistency :-).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions