From 805158502f1d13d2e4154f4f89747c78f0caf3fd Mon Sep 17 00:00:00 2001 From: Matthew Woehlke Date: Wed, 29 Jun 2022 15:05:15 -0400 Subject: [PATCH] Fix arrays with zero-size dimensions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When converting an array to an Eigen matrix, ignore the strides if any dimension size is 0. If the array is empty, the strides aren't relevant, and especially numpy ≥ 1.23 explicitly sets the strides to 0 in this case. (See numpy commit dd5ab7b11520.) Update tests to verify that this works, and continues to work. (This test likely would have caught the behavior change with numpy 1.23.) This expands on the tests added by #38, but also reverts the logic change from that PR, as it is no longer needed, and brings the code closer in line with upstream, since #38 was never pushed upstream. --- include/pybind11/eigen.h | 20 +++++++++++++------- tests/test_eigen.py | 2 ++ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/include/pybind11/eigen.h b/include/pybind11/eigen.h index 98f3bd6390..9fd7817055 100644 --- a/include/pybind11/eigen.h +++ b/include/pybind11/eigen.h @@ -97,13 +97,19 @@ template struct EigenConformable { template bool stride_compatible() const { // To have compatible strides, we need (on both dimensions) one of fully dynamic strides, - // matching strides, or a dimension size of 1 (in which case the stride value is irrelevant) - return - !negativestrides && - (props::inner_stride == Eigen::Dynamic || props::inner_stride == stride.inner() || - (EigenRowMajor ? cols : rows) <= 1) && - (props::outer_stride == Eigen::Dynamic || props::outer_stride == stride.outer() || - (EigenRowMajor ? rows : cols) <= 1); + // matching strides, or a dimension size of 1 (in which case the stride value is + // irrelevant). Alternatively, if any dimension size is 0, the strides are not relevant + // (and numpy ≥ 1.23 sets the strides to 0 in that case, so we need to check explicitly). + if (negativestrides) { + return false; + } + if (rows == 0 || cols == 0) { + return true; + } + return (props::inner_stride == Eigen::Dynamic || props::inner_stride == stride.inner() + || (EigenRowMajor ? cols : rows) == 1) + && (props::outer_stride == Eigen::Dynamic || props::outer_stride == stride.outer() + || (EigenRowMajor ? rows : cols) == 1); } // NOLINTNEXTLINE(google-explicit-constructor) operator bool() const { return conformable; } diff --git a/tests/test_eigen.py b/tests/test_eigen.py index 6464dadcbb..ea846c3fc1 100644 --- a/tests/test_eigen.py +++ b/tests/test_eigen.py @@ -840,6 +840,8 @@ def test_issue738_issue2038(): ) assert np.all(m.iss738_f1(np.zeros((0, 2))) == np.zeros((0, 2))) + assert np.all(m.iss738_f1(np.zeros((2, 0))) == np.zeros((2, 0))) + assert np.all(m.iss738_f2(np.zeros((0, 2))) == np.zeros((0, 2))) assert np.all(m.iss738_f2(np.zeros((2, 0))) == np.zeros((2, 0)))