cov_matrix_free checks squareness and a positive diagonal but never checks that the LLT factorization succeeded. An indefinite matrix with positive diagonal is accepted, and the returned vector does not round-trip:
Eigen::MatrixXd S(2, 2);
S << 1, 2, 2, 1; // indefinite, positive diagonal
Eigen::VectorXd z = stan::math::cov_matrix_free(S); // no throw
// z = [0, 2, 0]
Eigen::MatrixXd back = stan::math::cov_matrix_constrain(z, 2);
// back = [[1, 2], [2, 5]] — a different matrix than the input
The sibling cov_matrix_free_lkj does check the factorization and throws on the same input (LLT factorization failed), so the guard pattern already exists in the codebase.
This is reachable from user code through the unconstraining path (e.g. CmdStan's unconstrain with bad inits): invalid input silently produces a wrong unconstrained parameter vector instead of an error.
Check llt.info() like cov_matrix_free_lkj does is probably the only thing needed.
Environment:
- Ubuntu 22.04.5 LTS (WSL2, kernel 6.18.33.2-microsoft-standard-WSL2), x86_64
- stan-dev/math develop @ d20b238
- g++ 11.4.0 (Ubuntu 22.04), -std=c++17
cov_matrix_freechecks squareness and a positive diagonal but never checks that the LLT factorization succeeded. An indefinite matrix with positive diagonal is accepted, and the returned vector does not round-trip:The sibling
cov_matrix_free_lkjdoes check the factorization and throws on the same input (LLT factorization failed), so the guard pattern already exists in the codebase.This is reachable from user code through the unconstraining path (e.g. CmdStan's
unconstrainwith bad inits): invalid input silently produces a wrong unconstrained parameter vector instead of an error.Check
llt.info()likecov_matrix_free_lkjdoes is probably the only thing needed.Environment: