Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG]: Eigen::SparseMatrix -> unexpected overload-resolution / template-instantiation #5283

Open
2 of 3 tasks
sschnug opened this issue Aug 2, 2024 · 0 comments
Open
2 of 3 tasks
Labels
triage New bug, unverified

Comments

@sschnug
Copy link

sschnug commented Aug 2, 2024

Required prerequisites

What version (or hash if on master) of pybind11 are you using?

7233072

Problem description

I'm trying to wrap dense as well as sparse-matrices (py -> c++): specifically

  • Eigen::Array<T, Eigen::Dynamic, Eigen::Dynamic>
  • Eigen::SparseMatrix<T, Eigen::RowMajor>
  • Eigen::SparseMatrix<T, Eigen::ColMajor>

Having tried two approaches, one being based on EigenBase<Derived>, the other being based on splitting the target functions into a dense and a sparse variant (as shown in example).

In both cases, something unexpected is happening to the type in the sparse-case while the dense case works as expected.

tests/test_basic.py
 Dense matrix @ python
[[1. 2. 3. 4.]
[5. 6. 7. 8.]]
float32

Sparse matrix @ python
  (0, 0)        1.0
  (0, 1)        2.0
  (0, 2)        3.0
  (0, 3)        4.0
  (1, 0)        5.0
  (1, 1)        6.0
  (1, 2)        7.0
  (1, 3)        8.0
float32

CALL instantiation -> with dense => c++ receives type?
Dense overload
float
CALL instantiation -> with sparse => c++ receives type?
Sparse overload
bool ??? WHY ???

Reproducible example: https://github.com/sschnug/cmake_example/tree/eigen_sparse_matrix_overload_issue_example

Reproducible example code

See: https://github.com/sschnug/cmake_example/tree/eigen_sparse_matrix_overload_issue_example

But basically:

// OKAY BEHAVIOUR -> dense
template <typename ScalarT>
static void take_matrix(const Eigen::Array<ScalarT, Eigen::Dynamic, Eigen::Dynamic> matrix_in) {
  std::cout << "Dense overload" << std::endl;

  if constexpr(std::is_same_v<ScalarT, float>) {
    std::cout << "float" << std::endl;
  }
  else if constexpr(std::is_same_v<ScalarT, double>) {
    std::cout << "double" << std::endl;
  }
  else if constexpr(std::is_same_v<ScalarT, bool>) {
    std::cout << "bool" << std::endl;
  }
  else if constexpr(std::is_same_v<ScalarT, int8_t>) {
    std::cout << "int8_t" << std::endl;
  }
  else if constexpr(std::is_same_v<ScalarT, int16_t>) {
    std::cout << "int16_t" << std::endl;
  }
  else if constexpr(std::is_same_v<ScalarT, int32_t>) {
    std::cout << "int32_t" << std::endl;
  }
  else if constexpr(std::is_same_v<ScalarT, int64_t>) {
    std::cout << "int64_t" << std::endl;
  }
  else if constexpr(std::is_same_v<ScalarT, uint8_t>) {
    std::cout << "uint8_t" << std::endl;
  }
  else if constexpr(std::is_same_v<ScalarT, uint16_t>) {
    std::cout << "uint16_t" << std::endl;
  }
  else if constexpr(std::is_same_v<ScalarT, uint32_t>) {
    std::cout << "uint32_t" << std::endl;
  }
  else if constexpr(std::is_same_v<ScalarT, uint64_t>) {
    std::cout << "uint64_t" << std::endl;
  }
  else 
    std::cout << "some other type" << std::endl;
}

// NOT OKAY BEHAVIOUR -> sparse
template<typename ScalarT, int Options>
static void take_matrix(const Eigen::SparseMatrix<ScalarT, Options> matrix_in) {
  std::cout << "Sparse overload" << std::endl;

  if constexpr(std::is_same_v<ScalarT, float>) {
    std::cout << "float" << std::endl;
  }
  else if constexpr(std::is_same_v<ScalarT, double>) {
    std::cout << "double" << std::endl;
  }
  else if constexpr(std::is_same_v<ScalarT, bool>) {
    std::cout << "bool" << std::endl;
  }
  else if constexpr(std::is_same_v<ScalarT, int8_t>) {
    std::cout << "int8_t" << std::endl;
  }
  else if constexpr(std::is_same_v<ScalarT, int16_t>) {
    std::cout << "int16_t" << std::endl;
  }
  else if constexpr(std::is_same_v<ScalarT, int32_t>) {
    std::cout << "int32_t" << std::endl;
  }
  else if constexpr(std::is_same_v<ScalarT, int64_t>) {
    std::cout << "int64_t" << std::endl;
  }
  else if constexpr(std::is_same_v<ScalarT, uint8_t>) {
    std::cout << "uint8_t" << std::endl;
  }
  else if constexpr(std::is_same_v<ScalarT, uint16_t>) {
    std::cout << "uint16_t" << std::endl;
  }
  else if constexpr(std::is_same_v<ScalarT, uint32_t>) {
    std::cout << "uint32_t" << std::endl;
  }
  else if constexpr(std::is_same_v<ScalarT, uint64_t>) {
    std::cout << "uint64_t" << std::endl;
  }
  else 
    std::cout << "some other type" << std::endl;
}

PYBIND11_MODULE(cmake_example, m) {

    // dense
    // -----

    m.def("take_matrix", &take_matrix<bool>, pybind11::arg("matrix_in").noconvert());
    m.def("take_matrix", &take_matrix<float>, pybind11::arg("matrix_in").noconvert());
    m.def("take_matrix", &take_matrix<double>, pybind11::arg("matrix_in").noconvert());

    // sparse
    // ------

    m.def("take_matrix", &take_matrix<bool, Eigen::ColMajor>, pybind11::arg("matrix_in").noconvert());
    m.def("take_matrix", &take_matrix<bool, Eigen::RowMajor>, pybind11::arg("matrix_in").noconvert());

    m.def("take_matrix", &take_matrix<float, Eigen::ColMajor>, pybind11::arg("matrix_in").noconvert());
    m.def("take_matrix", &take_matrix<float, Eigen::RowMajor>, pybind11::arg("matrix_in").noconvert());

    m.def("take_matrix", &take_matrix<double, Eigen::ColMajor>, pybind11::arg("matrix_in").noconvert());
    m.def("take_matrix", &take_matrix<double, Eigen::RowMajor>, pybind11::arg("matrix_in").noconvert());

Is this a regression? Put the last known working version here if it is.

Not a regression

@sschnug sschnug added the triage New bug, unverified label Aug 2, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
triage New bug, unverified
Projects
None yet
Development

No branches or pull requests

1 participant