From f23e0b5e95b5da6d2ed0715712a224673cfa9ad5 Mon Sep 17 00:00:00 2001 From: Jason Rhinelander Date: Tue, 5 Jul 2016 16:03:43 -0400 Subject: [PATCH 1/9] Fix test diff output under python2.7 PR #220 broke failed test output under python2.7, which doesn't support the keepends argument to splitlines. --- example/run_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/example/run_test.py b/example/run_test.py index 70ce4a6c0..da0d9ee77 100755 --- a/example/run_test.py +++ b/example/run_test.py @@ -68,6 +68,6 @@ else: print('Test "%s" FAILED!' % name) print('--- output') print('+++ reference') - print(''.join(difflib.ndiff(output.splitlines(keepends=True), - reference.splitlines(keepends=True)))) + print('\n'.join(difflib.ndiff(output.splitlines(), + reference.splitlines()))) exit(-1) From b063e64b19ee89cbdafc3d2a82c3324e57e8e83f Mon Sep 17 00:00:00 2001 From: Ben North Date: Tue, 5 Jul 2016 20:01:11 +0100 Subject: [PATCH 2/9] Eigen tests: '2*' functions for col-, row-vectors --- example/eigen.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/example/eigen.cpp b/example/eigen.cpp index b6fa24a42..81c1f0bf4 100644 --- a/example/eigen.cpp +++ b/example/eigen.cpp @@ -10,6 +10,12 @@ #include "example.h" #include +Eigen::VectorXf double_col(const Eigen::VectorXf& x) +{ return 2.0f * x; } + +Eigen::RowVectorXf double_row(const Eigen::RowVectorXf& x) +{ return 2.0f * x; } + void init_eigen(py::module &m) { typedef Eigen::Matrix FixedMatrixR; typedef Eigen::Matrix FixedMatrixC; @@ -23,6 +29,9 @@ void init_eigen(py::module &m) { mat << 0, 3, 0, 0, 0, 11, 22, 0, 0, 0, 17, 11, 7, 5, 0, 1, 0, 11, 0, 0, 0, 0, 0, 11, 0, 0, 14, 0, 8, 11; + m.def("double_col", &double_col); + m.def("double_row", &double_row); + m.def("fixed_r", [mat]() -> FixedMatrixR { return FixedMatrixR(mat); }); From 4a22091d454a9e02964b09ee63f98aeb1d57e769 Mon Sep 17 00:00:00 2001 From: Ben North Date: Tue, 5 Jul 2016 20:03:02 +0100 Subject: [PATCH 3/9] Add tests for doubling row- and col-vectors Passing a non-contiguous one-dimensional numpy array gives incorrect results, so three of these tests fail. The only one passing is the simple case where the numpy array is contiguous and we are building a column-major vector. Subsequent commit will fix the three failing cases. --- example/eigen.py | 13 +++++++++++++ example/eigen.ref | 4 ++++ 2 files changed, 17 insertions(+) diff --git a/example/eigen.py b/example/eigen.py index accaf236a..13e6301f2 100644 --- a/example/eigen.py +++ b/example/eigen.py @@ -9,6 +9,7 @@ from example import dense_r, dense_c from example import dense_passthrough_r, dense_passthrough_c from example import sparse_r, sparse_c from example import sparse_passthrough_r, sparse_passthrough_c +from example import double_row, double_col import numpy as np ref = np.array( @@ -42,3 +43,15 @@ print("pt_r(sparse_r) = %s" % check(sparse_passthrough_r(sparse_r()))) print("pt_c(sparse_c) = %s" % check(sparse_passthrough_c(sparse_c()))) print("pt_r(sparse_c) = %s" % check(sparse_passthrough_r(sparse_c()))) print("pt_c(sparse_r) = %s" % check(sparse_passthrough_c(sparse_r()))) + +def check_got_vs_ref(got_x, ref_x): + return 'OK' if np.array_equal(got_x, ref_x) else 'NOT OK' + +counting_mat = np.arange(9.0, dtype=np.float32).reshape((3, 3)) +first_row = counting_mat[0, :] +first_col = counting_mat[:, 0] + +print("double_row(first_row) = %s" % check_got_vs_ref(double_row(first_row), 2.0 * first_row)) +print("double_col(first_row) = %s" % check_got_vs_ref(double_col(first_row), 2.0 * first_row)) +print("double_row(first_col) = %s" % check_got_vs_ref(double_row(first_col), 2.0 * first_col)) +print("double_col(first_col) = %s" % check_got_vs_ref(double_col(first_col), 2.0 * first_col)) diff --git a/example/eigen.ref b/example/eigen.ref index b87f8ede3..460172872 100644 --- a/example/eigen.ref +++ b/example/eigen.ref @@ -16,3 +16,7 @@ pt_r(sparse_r) = OK pt_c(sparse_c) = OK pt_r(sparse_c) = OK pt_c(sparse_r) = OK +double_row(first_row) = OK +double_col(first_row) = OK +double_row(first_col) = OK +double_col(first_col) = OK From 93594a3857a05d52c2c47e35c768c5fb080752da Mon Sep 17 00:00:00 2001 From: Ben North Date: Tue, 5 Jul 2016 20:05:10 +0100 Subject: [PATCH 4/9] Fix handling of one-dimensional input arrays In eigen.h, type_caster::load(): For the 'ndim == 1' case, use the 'InnerStride' type because there is only an inner stride for a vector. Choose between (n_elts x 1) or (1 x n_elts) according to whether we're constructing a Vector or a RowVector. --- include/pybind11/eigen.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/include/pybind11/eigen.h b/include/pybind11/eigen.h index 718107947..ecad2d547 100644 --- a/include/pybind11/eigen.h +++ b/include/pybind11/eigen.h @@ -61,7 +61,7 @@ struct type_caster::value>::t buffer_info info = buffer.request(); if (info.ndim == 1) { - typedef Eigen::Stride Strides; + typedef Eigen::InnerStride<> Strides; if (!isVector && !(Type::RowsAtCompileTime == Eigen::Dynamic && Type::ColsAtCompileTime == Eigen::Dynamic)) @@ -71,10 +71,13 @@ struct type_caster::value>::t info.shape[0] != (size_t) Type::SizeAtCompileTime) return false; - auto strides = Strides(info.strides[0] / sizeof(Scalar), 0); + auto strides = Strides(info.strides[0] / sizeof(Scalar)); + + Strides::Index n_elts = info.shape[0]; + Strides::Index unity = 1; value = Eigen::Map( - (Scalar *) info.ptr, typename Strides::Index(info.shape[0]), 1, strides); + (Scalar *) info.ptr, rowMajor ? unity : n_elts, rowMajor ? n_elts : unity, strides); } else if (info.ndim == 2) { typedef Eigen::Stride Strides; From 3e0e77932229aa18431d269b01d74a892f0bbefd Mon Sep 17 00:00:00 2001 From: Ben North Date: Tue, 5 Jul 2016 21:00:05 +0100 Subject: [PATCH 5/9] Tests: Add further '2*' functions for matrices Add and declare to Python functions double_mat_cm() --- compute 2* a column-major matrix double_mat_rm() --- compute 2* a row-major matrix to 'eigen.cpp' tests / example. --- example/eigen.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/example/eigen.cpp b/example/eigen.cpp index 81c1f0bf4..f99ae3a40 100644 --- a/example/eigen.cpp +++ b/example/eigen.cpp @@ -16,6 +16,13 @@ Eigen::VectorXf double_col(const Eigen::VectorXf& x) Eigen::RowVectorXf double_row(const Eigen::RowVectorXf& x) { return 2.0f * x; } +Eigen::MatrixXf double_mat_cm(const Eigen::MatrixXf& x) +{ return 2.0f * x; } + +typedef Eigen::Matrix MatrixXfRowMajor; +MatrixXfRowMajor double_mat_rm(const MatrixXfRowMajor& x) +{ return 2.0f * x; } + void init_eigen(py::module &m) { typedef Eigen::Matrix FixedMatrixR; typedef Eigen::Matrix FixedMatrixC; @@ -31,6 +38,8 @@ void init_eigen(py::module &m) { m.def("double_col", &double_col); m.def("double_row", &double_row); + m.def("double_mat_cm", &double_mat_cm); + m.def("double_mat_rm", &double_mat_rm); m.def("fixed_r", [mat]() -> FixedMatrixR { return FixedMatrixR(mat); From 7b8d9e024622897389c2d66203638070923cedcb Mon Sep 17 00:00:00 2001 From: Ben North Date: Tue, 5 Jul 2016 21:03:19 +0100 Subject: [PATCH 6/9] Test eigen converts slices of 3d arrays correctly --- example/eigen.py | 8 ++++++++ example/eigen.ref | 6 ++++++ 2 files changed, 14 insertions(+) diff --git a/example/eigen.py b/example/eigen.py index 13e6301f2..9c4e1ef16 100644 --- a/example/eigen.py +++ b/example/eigen.py @@ -10,6 +10,7 @@ from example import dense_passthrough_r, dense_passthrough_c from example import sparse_r, sparse_c from example import sparse_passthrough_r, sparse_passthrough_c from example import double_row, double_col +from example import double_mat_cm, double_mat_rm import numpy as np ref = np.array( @@ -55,3 +56,10 @@ print("double_row(first_row) = %s" % check_got_vs_ref(double_row(first_row), 2.0 print("double_col(first_row) = %s" % check_got_vs_ref(double_col(first_row), 2.0 * first_row)) print("double_row(first_col) = %s" % check_got_vs_ref(double_row(first_col), 2.0 * first_col)) print("double_col(first_col) = %s" % check_got_vs_ref(double_col(first_col), 2.0 * first_col)) + +counting_3d = np.arange(27.0, dtype=np.float32).reshape((3, 3, 3)) +slices = [counting_3d[0, :, :], counting_3d[:, 0, :], counting_3d[:, :, 0]] + +for slice_idx, ref_mat in enumerate(slices): + print("double_mat_cm(%d) = %s" % (slice_idx, check_got_vs_ref(double_mat_cm(ref_mat), 2.0 * ref_mat))) + print("double_mat_rm(%d) = %s" % (slice_idx, check_got_vs_ref(double_mat_rm(ref_mat), 2.0 * ref_mat))) diff --git a/example/eigen.ref b/example/eigen.ref index 460172872..bac73be9f 100644 --- a/example/eigen.ref +++ b/example/eigen.ref @@ -20,3 +20,9 @@ double_row(first_row) = OK double_col(first_row) = OK double_row(first_col) = OK double_col(first_col) = OK +double_mat_cm(0) = OK +double_mat_rm(0) = OK +double_mat_cm(1) = OK +double_mat_rm(1) = OK +double_mat_cm(2) = OK +double_mat_rm(2) = OK From 676e29885bdfc10a93aeccb97dadc15070bcd570 Mon Sep 17 00:00:00 2001 From: Ben North Date: Tue, 5 Jul 2016 21:46:09 +0100 Subject: [PATCH 7/9] Test that check() catches wrong order of elements Fails --- next commit will tighten test. --- example/eigen.py | 2 ++ example/eigen.ref | 1 + 2 files changed, 3 insertions(+) diff --git a/example/eigen.py b/example/eigen.py index 9c4e1ef16..57c516a15 100644 --- a/example/eigen.py +++ b/example/eigen.py @@ -24,6 +24,8 @@ ref = np.array( def check(mat): return 'OK' if np.sum(mat - ref) == 0 else 'NOT OK' +print("should_give_NOT_OK = %s" % check(ref[:, ::-1])) + print("fixed_r = %s" % check(fixed_r())) print("fixed_c = %s" % check(fixed_c())) print("pt_r(fixed_r) = %s" % check(fixed_passthrough_r(fixed_r()))) diff --git a/example/eigen.ref b/example/eigen.ref index bac73be9f..03091cc24 100644 --- a/example/eigen.ref +++ b/example/eigen.ref @@ -1,3 +1,4 @@ +should_give_NOT_OK = NOT OK fixed_r = OK fixed_c = OK pt_r(fixed_r) = OK From 150a0fa786157dea15c71090f01645a0a88a6351 Mon Sep 17 00:00:00 2001 From: Ben North Date: Tue, 5 Jul 2016 19:59:28 +0100 Subject: [PATCH 8/9] check(): Stricter check in tests Previous version would give false 'OK' if, for example, we were supposed to get [1, 2, 3] but instead got [2, 1, 3]. --- example/eigen.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/eigen.py b/example/eigen.py index 57c516a15..7ff9aed5d 100644 --- a/example/eigen.py +++ b/example/eigen.py @@ -22,7 +22,7 @@ ref = np.array( def check(mat): - return 'OK' if np.sum(mat - ref) == 0 else 'NOT OK' + return 'OK' if np.sum(abs(mat - ref)) == 0 else 'NOT OK' print("should_give_NOT_OK = %s" % check(ref[:, ::-1])) From f57133aa2efff6b078005bb627f0b9e777a65af8 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Wed, 6 Jul 2016 05:43:52 +0200 Subject: [PATCH 9/9] correction to #266 fix --- example/run_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/example/run_test.py b/example/run_test.py index da0d9ee77..c31ea0989 100755 --- a/example/run_test.py +++ b/example/run_test.py @@ -68,6 +68,6 @@ else: print('Test "%s" FAILED!' % name) print('--- output') print('+++ reference') - print('\n'.join(difflib.ndiff(output.splitlines(), - reference.splitlines()))) + print(''.join(difflib.ndiff(output.splitlines(True), + reference.splitlines(True)))) exit(-1)