Skip to content

CWiseUnaryView inner/outer stride incorrect with pointer-to-implementation types

Summary

Eigen's CWiseUnaryView determines the inner & outer stride as:

MatrixTypeInnerStride =  inner_stride_at_compile_time<MatrixType>::ret

int(MatrixTypeInnerStride) * int(sizeof(typename traits<MatrixType>::Scalar) / sizeof(Scalar))

However when the view is returning a value from a pointer-to-implementation variable, the inner stride should be set using the sizeof of the implementation, not the pointer.

As an example, consider a struct containing two values and view which returns the first value:

struct inner {
    double val1_ = 1.0;
    double val2_ = 1.0;
};

struct view_inner_op {
  EIGEN_EMPTY_STRUCT_CTOR(view_inner_op)
  EIGEN_DEVICE_FUNC
  EIGEN_STRONG_INLINE const double& 
    operator()(const inner& v) const { return v.val1_; }
};

The innerStride is correctly set to 2:

int main() {
    inner a11, a12;
    a11.val1_ = 1.0;
    a11.val2_ = 2.0;
    a12.val1_ = 1.0;
    a12.val2_ = 2.0;

    Eigen::Matrix<inner,-1,1> inner_vec(2);
    inner_vec << a11, a12;

    Eigen::CwiseUnaryView<view_inner_op, Eigen::Matrix<inner,-1,1>> view_inner(inner_vec);
    
    std::cout << Eigen::Map<const Eigen::VectorXd>(&(inner_vec[0].val1_), 4) << std::endl
              << "View innerStride: " << view_inner.innerStride() << std::endl;

    return 0;
}
ASM generation compiler returned: 0
Execution build compiler returned: 0
Program returned: 0
1
2
1
2
View innerStride: 2

If we add an additional struct whose only member is a pointer to the first, and subsequently revise the view operation:

struct inner {
    double val1_ = 1.0;
    double val2_ = 1.0;
};

struct outer {
  inner* ptr_;
};

struct view_outer_op {
  EIGEN_EMPTY_STRUCT_CTOR(view_outer_op)
  EIGEN_DEVICE_FUNC
  EIGEN_STRONG_INLINE const double& 
    operator()(const outer& v) const { return v.ptr_->val1_; }
};

The innerStride is now derived as 1:

int main() {
    inner a11, a12;
    a11.val1_ = 1.0;
    a11.val2_ = 2.0;
    a12.val1_ = 1.0;
    a12.val2_ = 2.0;

    outer a21, a22;
    a21.ptr_ = &a11;
    a22.ptr_ = &a12;
    
    Eigen::Matrix<outer,-1,1> outer_vec(2);
    outer_vec << a21, a22;

    Eigen::CwiseUnaryView<view_outer_op, Eigen::Matrix<outer,-1,1>> view_outer(outer_vec);

    std::cout << Eigen::Map<const Eigen::VectorXd>(&(outer_vec[0].ptr_->val1_), 4) << std::endl
              << "View innerStride: " << view_outer.innerStride() << std::endl;

    return 0;
}
ASM generation compiler returned: 0
Execution build compiler returned: 0
Program returned: 0
1
2
1
2
View innerStride: 1

I believe this could be resolved by adding Stride arguments/parameters to CWiseUnaryView, like those used for Map

Environment

  • Operating System : Linux
  • Architecture : x64
  • Eigen Version : 3.4
  • Compiler Version : Gcc10.1
  • Compile Flags : -O3
  • Vector Extension : N/A