From 0668c68b031351488712f21290c77412b02c5de8 Mon Sep 17 00:00:00 2001 From: Gael Guennebaud Date: Wed, 27 Jan 2021 23:29:33 +0100 Subject: [PATCH] Allow for negative strides. Note that using a stride of -1 is still not possible because it would clash with the definition of Eigen::Dynamic. This fixes #747. --- Eigen/src/Core/Stride.h | 9 +++++++-- test/mapstride.cpp | 28 +++++++++++++++++++++++++++- test/product_extra.cpp | 16 ++++++++++++++++ 3 files changed, 50 insertions(+), 3 deletions(-) diff --git a/Eigen/src/Core/Stride.h b/Eigen/src/Core/Stride.h index 513742f34..cbcb0a503 100644 --- a/Eigen/src/Core/Stride.h +++ b/Eigen/src/Core/Stride.h @@ -26,7 +26,7 @@ namespace Eigen { * * The outer stride is the pointer increment between two consecutive rows of a row-major matrix or * between two consecutive columns of a column-major matrix. - * + * * These two values can be passed either at compile-time as template parameters, or at runtime as * arguments to the constructor. * @@ -38,6 +38,10 @@ namespace Eigen { * \include Map_general_stride.cpp * Output: \verbinclude Map_general_stride.out * + * Both strides can be negative, however, a negative stride of -1 cannot be specified at compiletime + * because of the ambiguity with Dynamic which is defined to -1 (historically, negative strides were + * not allowed). + * * \sa class InnerStride, class OuterStride, \ref TopicStorageOrders */ template @@ -55,6 +59,8 @@ class Stride Stride() : m_outer(OuterStrideAtCompileTime), m_inner(InnerStrideAtCompileTime) { + // FIXME: for Eigen 4 we should use DynamicIndex instead of Dynamic. + // FIXME: for Eigen 4 we should also unify this API with fix<> eigen_assert(InnerStrideAtCompileTime != Dynamic && OuterStrideAtCompileTime != Dynamic); } @@ -63,7 +69,6 @@ class Stride Stride(Index outerStride, Index innerStride) : m_outer(outerStride), m_inner(innerStride) { - eigen_assert(innerStride>=0 && outerStride>=0); } /** Copy constructor */ diff --git a/test/mapstride.cpp b/test/mapstride.cpp index 09196600b..fde73f2ec 100644 --- a/test/mapstride.cpp +++ b/test/mapstride.cpp @@ -162,6 +162,32 @@ template void map_class_matrix(const MatrixTy VERIFY_IS_APPROX(map,s1*m); } + // test negative strides + { + Matrix::Map(a_array1, arraysize+1).setRandom(); + Index outerstride = m.innerSize()+4; + Scalar* array = array1; + + { + Map > map1(array, rows, cols, OuterStride<>( outerstride)); + Map > map2(array+(m.outerSize()-1)*outerstride, rows, cols, OuterStride<>(-outerstride)); + if(MatrixType::IsRowMajor) VERIFY_IS_APPROX(map1.colwise().reverse(), map2); + else VERIFY_IS_APPROX(map1.rowwise().reverse(), map2); + } + + { + Map > map1(array, rows, cols, OuterStride<>( outerstride)); + Map > map2(array+(m.outerSize()-1)*outerstride+m.innerSize()-1, rows, cols, Stride(-outerstride,-1)); + VERIFY_IS_APPROX(map1.reverse(), map2); + } + + { + Map > map1(array, rows, cols, OuterStride<>( outerstride)); + Map > map2(array+(m.outerSize()-1)*outerstride+m.innerSize()-1, rows, cols, Stride(-outerstride,-1)); + VERIFY_IS_APPROX(map1.reverse(), map2); + } + } + internal::aligned_delete(a_array1, arraysize+1); } @@ -200,7 +226,7 @@ void bug1453() EIGEN_DECLARE_TEST(mapstride) { for(int i = 0; i < g_repeat; i++) { - int maxn = 30; + int maxn = 3; CALL_SUBTEST_1( map_class_vector(Matrix()) ); CALL_SUBTEST_1( map_class_vector(Matrix()) ); CALL_SUBTEST_2( map_class_vector(Vector4d()) ); diff --git a/test/product_extra.cpp b/test/product_extra.cpp index bd31df84d..15c69896e 100644 --- a/test/product_extra.cpp +++ b/test/product_extra.cpp @@ -93,6 +93,22 @@ template void product_extra(const MatrixType& m) VERIFY_IS_APPROX(m1.col(j2).adjoint() * m1.block(0,j,m1.rows(),c), m1.col(j2).adjoint().eval() * m1.block(0,j,m1.rows(),c).eval()); VERIFY_IS_APPROX(m1.block(i,0,r,m1.cols()) * m1.row(i2).adjoint(), m1.block(i,0,r,m1.cols()).eval() * m1.row(i2).adjoint().eval()); + + // test negative strides + { + Map > map1(&m1(rows-1,cols-1), rows, cols, Stride(-m1.outerStride(),-1)); + Map > map2(&m2(rows-1,cols-1), rows, cols, Stride(-m2.outerStride(),-1)); + Map > mapv1(&v1(v1.size()-1), v1.size(), InnerStride<-1>(-1)); + Map > mapvc2(&vc2(vc2.size()-1), vc2.size(), InnerStride<-1>(-1)); + VERIFY_IS_APPROX(MatrixType(map1), m1.reverse()); + VERIFY_IS_APPROX(MatrixType(map2), m2.reverse()); + VERIFY_IS_APPROX(m3.noalias() = MatrixType(map1) * MatrixType(map2).adjoint(), m1.reverse() * m2.reverse().adjoint()); + VERIFY_IS_APPROX(m3.noalias() = map1 * map2.adjoint(), m1.reverse() * m2.reverse().adjoint()); + VERIFY_IS_APPROX(map1 * vc2, m1.reverse() * vc2); + VERIFY_IS_APPROX(m1 * mapvc2, m1 * mapvc2); + VERIFY_IS_APPROX(map1.adjoint() * v1.transpose(), m1.adjoint().reverse() * v1.transpose()); + VERIFY_IS_APPROX(m1.adjoint() * mapv1.transpose(), m1.adjoint() * v1.reverse().transpose()); + } // regression test MatrixType tmp = m1 * m1.adjoint() * s1;