Skip to content

eigendecompose_sym, eigenvalues_sym and eigenvectors_sym do not compile for complex matrices of fvar<var> or fvar<fvar<double>> #3477

Open

Description

@jachymb

Context: At #3470 @WardBrian remarked that we don't have full coverage, so tests not failing is not a bulletproof evidence that it's safe to merge. I was trying to kind of dig into how a more solid evidence could be constructed and while working on that, I ran into this issue that is apparently a fail with no test coverage.

The following notes are claude generated.

Description

The prim versions of the three functions call check_symmetric on their argument (eigendecompose_sym.hpp:32, eigenvalues_sym.hpp:29, eigenvectors_sym.hpp:20). Complex matrices of forward-mode scalars reach these prim versions. check_symmetric compares the two off-diagonal entries with value_of, which removes only one autodiff layer (stan/math/prim/err/check_symmetric.hpp:45):

if (!(fabs(value_of(y_ref(m, n)) - value_of(y_ref(n, m)))
      <= CONSTRAINT_TOLERANCE)) {

For std::complex<fvar<var>> the difference is still std::complex<var>, so fabs resolves to libstdc++'s extension std::fabs(const std::complex<T>&) (<complex>:2430 in GCC 14). That calls the generic std::abs, which reads __z.__rep() (<complex>:892), a member of the primary template only. Stan's specializations std::complex<var> and std::complex<fvar<T>> do not have it, so compilation fails. std::complex<fvar<fvar<double>>> fails the same way, because its difference is std::complex<fvar<double>>. std::complex<var> and std::complex<fvar<double>> compile, because value_of reduces them to std::complex<double>.

How Stan programs reach it. eigendecompose_sym(complex_matrix), eigenvalues_sym(complex_matrix) and eigenvectors_sym(complex_matrix) applied to a parameter-dependent matrix compile with the usual flags. They do not compile with STAN_MODEL_FVAR_VAR, which also instantiates log_prob for fvar<var>. BridgeStan sets that macro in its BRIDGESTAN_AD_HESSIAN build (Makefile:40), so such a model cannot be built there.

The mix tests of the three functions use only real matrices, which is why no test fails.

A small, separate defect is in the same place: eigenvectors_sym passes "eigenvalues_sym" as the function name to check_symmetric (eigenvectors_sym.hpp:20), so its error messages name the wrong function.

Example

#include <stan/math/mix.hpp>

int main() {
  using stan::math::fvar;
  using stan::math::var;
  Eigen::Matrix<std::complex<fvar<var>>, -1, -1> m(2, 2);
  auto res = stan::math::eigendecompose_sym(m);
}

It was compiled from the repository root at 5792530, in WSL2 (Debian 13):

g++ -std=c++17 -D_REENTRANT -DBOOST_DISABLE_ASSERTS -fsyntax-only -I . -isystem lib/eigen_5.0.1 -isystem lib/boost_1.87.0 -isystem lib/sundials_6.1.1/include -isystem lib/tbb_2020.3/include eig.cpp

g++ 14.2.0 reports:

/usr/include/c++/14/complex:892:61: error: ‘const class std::complex<stan::math::var_value<double> >’ has no member named ‘__rep’

clang++ 18.1.8 reports the same error, with this instantiation chain (directory prefixes removed):

c++/14/complex:892:61: error: no member named '__rep' in 'std::complex<stan::math::var>'
c++/14/complex:2430:19: note: in instantiation of function template specialization 'std::abs<stan::math::var_value<double>>' requested here
stan/math/prim/err/check_symmetric.hpp:45:13: note: in instantiation of function template specialization 'std::fabs<stan::math::var_value<double>>' requested here
stan/math/prim/fun/eigendecompose_sym.hpp:32:3: note: in instantiation of function template specialization 'stan::math::check_symmetric<Eigen::Matrix<std::complex<stan::math::fvar<stan::math::var_value<double>>>, -1, -1>, nullptr>' requested here

eigenvalues_sym and eigenvectors_sym fail the same way with fvar<var>, and eigendecompose_sym also with fvar<fvar<double>>.

From Stan, with stanc3 793b15d:

parameters {
  real x;
}
model {
  complex_matrix[2, 2] m = to_complex([[x, 0], [0, x]], 0);
  complex_vector[2] v = eigenvalues_sym(m);
}

The generated C++ compiles with the stan headers at stan-dev/stan@849cec8da. With -DSTAN_MODEL_FVAR_VAR it fails with the same __rep error.

Expected Output

All three functions compile and run for complex matrices of every autodiff type, like they do for real matrices.

Comparing with value_of_rec, which is meant for exactly such error checks, fixes it:

if (!(fabs(value_of_rec(y_ref(m, n)) - value_of_rec(y_ref(n, m)))
      <= CONSTRAINT_TOLERANCE)) {

with #include <stan/math/prim/fun/value_of_rec.hpp> in place of value_of.hpp. With that change the example compiles with g++ 14.2.0 for all three functions with fvar<var>, and for eigendecompose_sym with fvar<fvar<double>>. A complex input in the three mix tests would cover it.

Current Version:

v5.4.0 (develop at 5792530)

Activity

  1. WardBrian commented on Oct 23, 2026

    @WardBrian
    Member

    value_of_rec does appear to be the correct patch here

  2. jachymb commented on Oct 24, 2026

    @jachymb
    ContributorAuthor

    value_of_rec fixes the compilation crash, but I found out there's a deeper issue: the complex matrices are treated as symmetric where they should be treated as Hermitian. So fixing it with value_of_rec would actually produce buggy behavior when it compiles.

    I tried to get it to compile on #3484 with just the value_of_rec, it technically works, but then I changed my mind of actually going through with that because of this problem.

  3. jachymb commented on Oct 24, 2026

    @jachymb
    ContributorAuthor

    So maybe using the *_sym methods shouldn't even be allowed for complex valued matrices as that's confusing terminologically. There are some use cases for complex-symmetric (non-hermitian) matrices, but specialized eigendecompostition isn't one (afaik). Instead, we could have *_hermitian functions that would mirror the *_sym logic.

    Thoughts?

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions