Skip to content

Enforce consistency between input numpy layout and expected Eigen object memory layout #330

@ManifoldFR

Description

@ManifoldFR

Consider the following example:

template <typename MatType>
const Eigen::Ref<const MatType> asConstRef(Eigen::Ref<const MatType> mat) {
  doSomething(mat);
  return Eigen::Ref<const MatType>(mat);
}

// in BOOST_PYTHON_MODULE
  bp::def("asRef", asConstRef<Eigen::MatrixXd>);   // notice this is the *ColMajor* matrix
//

This will work:

A = np.eye(2, order="F")
aref = asConstRef(A)

assert np.array_equal(A, aref)
assert not aref.flags.owndata
assert not aref.flags.writeable

This will not:

A = np.eye(2, order="C")  # "C" is the numpy default
aref = asConstRef(A)

assert np.array_equal(A, aref)  # fails
assert not aref.flags.owndata  # ok
assert not aref.flags.writeable  # ok

After discussing with @jcarpent we came to the conclusion that the return statement in C++, due to how EigenAllocator<Eigen::Ref<cv MatType>, ...> is wired up, returns something pointing to the wrong memory - actually, to temporary C++ memory created by EigenAllocator and of which an Eigen::Ref is given to asConstRef. This temporary memory is a (col-major) Eigen::MatrixXd created to host a copy of the (row-major) Numpy input, and destroyed in the destructor of the rvalue_from_python_data handler before the function returns. What is inside the returned aref view is random memory.

This was discovered while working in PR #325.

Two possibilities:

  • redefine the Eigen::Ref types to have dynamic stride as to allow mapping from column major to row major; rework the EigenAllocator
  • disallow the above.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions