Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consolidate tests specific to fork for Drake #56

Draft
wants to merge 1 commit into
base: drake
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ set(PYBIND11_TEST_FILES
test_copy_move.cpp
test_custom_type_casters.cpp
test_docstring_options.cpp
test_drake_eigen.cpp
test_drake_smart_ptr.cpp
test_eigen.cpp
test_enum.cpp
test_eval.cpp
Expand All @@ -122,7 +124,6 @@ set(PYBIND11_TEST_FILES
test_numpy_vectorize.cpp
test_opaque_types.cpp
test_operator_overloading.cpp
test_ownership_transfer.cpp
test_pickling.cpp
test_pytypes.cpp
test_sequences_and_iterators.cpp
Expand Down Expand Up @@ -174,7 +175,8 @@ set(PYBIND11_CROSS_MODULE_GIL_TESTS test_gil_scoped.py)
# keep it in PYBIND11_PYTEST_FILES, so that we get the "eigen is not installed"
# skip message).
list(FIND PYBIND11_TEST_FILES test_eigen.cpp PYBIND11_TEST_FILES_EIGEN_I)
if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1)
list(FIND PYBIND11_TEST_FILES test_drake_eigen.cpp PYBIND11_TEST_FILES_EIGEN_I2)
if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1 OR PYBIND11_TEST_FILES_EIGEN_I2 GREATER -1)
# Try loading via newer Eigen's Eigen3Config first (bypassing tools/FindEigen3.cmake).
# Eigen 3.3.1+ exports a cmake 3.0+ target for handling dependency requirements, but also
# produces a fatal error if loaded from a pre-3.0 cmake.
Expand Down
9 changes: 0 additions & 9 deletions tests/test_builtin_casters.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -268,13 +268,4 @@ TEST_SUBMODULE(builtin_casters, m) {
m.def("takes_const_ptr", [](const ConstRefCasted* x) { return x->tag; });
m.def("takes_const_ref", [](const ConstRefCasted& x) { return x.tag; });
m.def("takes_const_ref_wrap", [](std::reference_wrapper<const ConstRefCasted> x) { return x.get().tag; });

// For Drake issue: https://github.com/RobotLocomotion/drake/issues/9398
m.def("test_pointer_caster", []() -> bool {
UserType a;
UserType *a_ptr = &a;
py::object o = py::cast(&a); // Rvalue
py::object o1 = py::cast(a_ptr); // Non-rvalue
return (py::cast<UserType*>(o) == a_ptr && py::cast<UserType*>(o1) == a_ptr);
});
}
4 changes: 0 additions & 4 deletions tests/test_builtin_casters.py
Original file line number Diff line number Diff line change
Expand Up @@ -535,7 +535,3 @@ def test_const_ref_caster():
assert m.takes_const_ptr(x) == 5
assert m.takes_const_ref(x) == 4
assert m.takes_const_ref_wrap(x) == 4


def test_pointer_caster():
assert m.test_pointer_caster()
18 changes: 0 additions & 18 deletions tests/test_class.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -477,24 +477,6 @@ TEST_SUBMODULE(class_, m) {
py::class_<OtherDuplicateNested>(gt, "OtherDuplicateNested");
py::class_<OtherDuplicateNested>(gt, "YetAnotherDuplicateNested");
});

// Test #1922 (drake#11424).
class ExampleVirt2 {
public:
virtual ~ExampleVirt2() = default;
virtual std::string get_name() const { return "ExampleVirt2"; }
};
class PyExampleVirt2 : public ExampleVirt2 {
public:
std::string get_name() const override {
PYBIND11_OVERLOAD(std::string, ExampleVirt2, get_name, );
}
};
py::class_<ExampleVirt2, PyExampleVirt2>(m, "ExampleVirt2")
.def(py::init())
.def("get_name", &ExampleVirt2::get_name);
m.def("example_virt2_get_name",
[](const ExampleVirt2& obj) { return obj.get_name(); });
}

template <int N> class BreaksBase { public:
Expand Down
37 changes: 0 additions & 37 deletions tests/test_class.py
Original file line number Diff line number Diff line change
Expand Up @@ -475,40 +475,3 @@ class ClassScope:
m.register_duplicate_nested_class_type(ClassScope)
expected = 'generic_type: type "YetAnotherDuplicateNested" is already registered!'
assert str(exc_info.value) == expected


@pytest.mark.skip(
reason="Generally reproducible in CPython, Python 3, non-debug, on Linux. "
"However, hard to pin this down for CI."
)
def test_1922():
# Test #1922 (drake#11424).
# Define a derived class which *does not* overload the method.
# WARNING: The reproduction of this failure may be platform-specific, and
# seems to depend on the order of definition and/or the name of the classes
# defined. For example, trying to place this and the C++ code in
# `test_virtual_functions` makes `assert id_1 == id_2` below fail.
class Child1(m.ExampleVirt2):
pass

id_1 = id(Child1)
assert m.example_virt2_get_name(m.ExampleVirt2()) == "ExampleVirt2"
assert m.example_virt2_get_name(Child1()) == "ExampleVirt2"

# Now delete everything (and ensure it's deleted).
wref = weakref.ref(Child1)
del Child1
pytest.gc_collect()
assert wref() is None

# Define a derived class which *does* define an overload.
class Child2(m.ExampleVirt2):
def get_name(self):
return "Child2"

id_2 = id(Child2)
assert id_1 == id_2 # This happens in CPython; not sure about PyPy.
assert m.example_virt2_get_name(m.ExampleVirt2()) == "ExampleVirt2"
# THIS WILL FAIL: This is using the cached `ExampleVirt2.get_name`, rather
# than re-inspect the Python dictionary.
assert m.example_virt2_get_name(Child2()) == "Child2"
128 changes: 128 additions & 0 deletions tests/test_drake_eigen.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
#include "pybind11_tests.h"
#include <pybind11/eigen.h>

#include <unsupported/Eigen/AutoDiff>
#include "Eigen/src/Core/util/DisableStupidWarnings.h"

typedef Eigen::AutoDiffScalar<Eigen::VectorXd> ADScalar;

template <typename Scalar>
using MatrixX = Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic>;

typedef Eigen::Matrix<ADScalar, Eigen::Dynamic, 1> VectorXADScalar;
typedef Eigen::Matrix<ADScalar, 1, Eigen::Dynamic> VectorXADScalarR;
typedef Eigen::Matrix<ADScalar, 5, 1> Vector5ADScalar;
typedef Eigen::Matrix<ADScalar, 1, 6> Vector6ADScalarR;
PYBIND11_NUMPY_OBJECT_DTYPE(ADScalar);

using DenseADScalarMatrixR = Eigen::Matrix<ADScalar, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;
using DenseADScalarMatrixC = Eigen::Matrix<ADScalar, Eigen::Dynamic, Eigen::Dynamic>;

VectorXADScalar& get_cm_adscalar() {
static VectorXADScalar value(1);
return value;
};
VectorXADScalarR& get_rm_adscalar() {
static VectorXADScalarR value(1);
return value;
};

TEST_SUBMODULE(drake_general, m) {
m.def("double_adscalar_col", [](const VectorXADScalar &x) -> VectorXADScalar { return 2.0f * x; });
m.def("double_adscalar_col5", [](const Vector5ADScalar &x) -> Vector5ADScalar { return 2.0f * x; });
m.def("double_adscalar_row", [](const VectorXADScalarR &x) -> VectorXADScalarR { return 2.0f * x; });
m.def("double_adscalar_row6", [](const Vector6ADScalarR &x) -> Vector6ADScalarR { return 2.0f * x; });
m.def("add_rm_adscalar", [](py::EigenDRef<VectorXADScalarR> x) { x.array() += 2; });
m.def("add_cm_adscalar", [](py::EigenDRef<VectorXADScalar> x) { x.array() += 2; });
m.def("get_cm_ref_adscalar", []() {
return py::EigenDRef<VectorXADScalar>(get_cm_adscalar());
});
m.def("get_rm_ref_adscalar", []() {
return py::EigenDRef<VectorXADScalarR>(get_rm_adscalar());
});
m.def("get_cm_const_ref_adscalar", []() { return Eigen::Ref<const VectorXADScalar>(get_cm_adscalar()); });
m.def("get_rm_const_ref_adscalar", []() { return Eigen::Ref<const VectorXADScalarR>(get_rm_adscalar()); });

// Increments ADScalar Matrix, returns a copy.
m.def("incr_adscalar_matrix", [](const Eigen::Ref<const DenseADScalarMatrixC>& m, double v) {
DenseADScalarMatrixC out = m;
out.array() += v;
return out;
});

// test_eigen_return_references, test_eigen_keepalive
// return value referencing/copying tests:
class ReturnTester {
Eigen::MatrixXd mat = create();
DenseADScalarMatrixR ad_mat = create_ADScalar_mat();
public:
ReturnTester() { print_created(this); }
~ReturnTester() { print_destroyed(this); }
static Eigen::MatrixXd create() { return Eigen::MatrixXd::Ones(10, 10); }
static DenseADScalarMatrixR create_ADScalar_mat() { DenseADScalarMatrixR ad_mat(2, 2);
ad_mat << 1, 2, 3, 7; return ad_mat; }
static const Eigen::MatrixXd createConst() { return Eigen::MatrixXd::Ones(10, 10); }
Eigen::MatrixXd &get() { return mat; }
DenseADScalarMatrixR& get_ADScalarMat() {return ad_mat;}
Eigen::MatrixXd *getPtr() { return &mat; }
const Eigen::MatrixXd &view() { return mat; }
const Eigen::MatrixXd *viewPtr() { return &mat; }
Eigen::Ref<Eigen::MatrixXd> ref() { return mat; }
Eigen::Ref<const Eigen::MatrixXd> refConst() { return mat; }
Eigen::Block<Eigen::MatrixXd> block(int r, int c, int nrow, int ncol) { return mat.block(r, c, nrow, ncol); }
Eigen::Block<const Eigen::MatrixXd> blockConst(int r, int c, int nrow, int ncol) const { return mat.block(r, c, nrow, ncol); }
py::EigenDMap<Eigen::Matrix2d> corners() { return py::EigenDMap<Eigen::Matrix2d>(mat.data(),
py::EigenDStride(mat.outerStride() * (mat.outerSize()-1), mat.innerStride() * (mat.innerSize()-1))); }
py::EigenDMap<const Eigen::Matrix2d> cornersConst() const { return py::EigenDMap<const Eigen::Matrix2d>(mat.data(),
py::EigenDStride(mat.outerStride() * (mat.outerSize()-1), mat.innerStride() * (mat.innerSize()-1))); }
};
using rvp = py::return_value_policy;
py::class_<ReturnTester>(m, "ReturnTester")
.def(py::init<>())
.def_static("create", &ReturnTester::create)
.def_static("create_const", &ReturnTester::createConst)
.def("get", &ReturnTester::get, rvp::reference_internal)
.def("get_ADScalarMat", &ReturnTester::get_ADScalarMat, rvp::reference_internal)
.def("get_ptr", &ReturnTester::getPtr, rvp::reference_internal)
.def("view", &ReturnTester::view, rvp::reference_internal)
.def("view_ptr", &ReturnTester::view, rvp::reference_internal)
.def("copy_get", &ReturnTester::get) // Default rvp: copy
.def("copy_view", &ReturnTester::view) // "
.def("ref", &ReturnTester::ref) // Default for Ref is to reference
.def("ref_const", &ReturnTester::refConst) // Likewise, but const
.def("ref_safe", &ReturnTester::ref, rvp::reference_internal)
.def("ref_const_safe", &ReturnTester::refConst, rvp::reference_internal)
.def("copy_ref", &ReturnTester::ref, rvp::copy)
.def("copy_ref_const", &ReturnTester::refConst, rvp::copy)
.def("block", &ReturnTester::block)
.def("block_safe", &ReturnTester::block, rvp::reference_internal)
.def("block_const", &ReturnTester::blockConst, rvp::reference_internal)
.def("copy_block", &ReturnTester::block, rvp::copy)
.def("corners", &ReturnTester::corners, rvp::reference_internal)
.def("corners_const", &ReturnTester::cornersConst, rvp::reference_internal)
;

py::class_<ADScalar>(m, "AutoDiffXd")
.def("__init__",
[](ADScalar & self,
double value,
const Eigen::VectorXd& derivatives) {
new (&self) ADScalar(value, derivatives);
})
.def("value", [](const ADScalar & self) {
return self.value();
})
.def("__repr__", [](const ADScalar& self) {
return py::str("<ADScalar {} deriv={}>").format(self.value(), self.derivatives());
})
;

m.def("iss1105_col_obj", [](VectorXADScalar) { return true; });
m.def("iss1105_row_obj", [](VectorXADScalarR) { return true; });
m.def("cpp_matrix_shape", [](const MatrixX<ADScalar>& A) {
return py::make_tuple(A.rows(), A.cols());
});
m.def("cpp_matrix_shape_ref", [](const Eigen::Ref<const MatrixX<ADScalar>>& A) {
return py::make_tuple(A.rows(), A.cols());
});
}
99 changes: 99 additions & 0 deletions tests/test_drake_eigen.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# -*- coding: utf-8 -*-
import pytest

import env # noqa: F401

from pybind11_tests import drake_eigen as m


def float_to_adscalar(arr, deriv):
arr = np.asarray(arr)
assert arr.dtype == float
new_arr = [m.AutoDiffXd(x, deriv) for x in arr.flat]
return np.array(new_arr).reshape(arr.shape)


def adscalar_to_float(arr):
arr = np.asarray(arr)
assert arr.dtype == object
new_arr = [x.value() for x in arr.flat]
return np.array(new_arr).reshape(arr.shape)


def check_array(a, b):
a, b = (np.asarray(x) for x in (a, b))
assert a.shape == b.shape and a.dtype == b.dtype
for index, (ai, bi) in enumerate(zip(a.flat, b.flat)):
assert m.equal_to(ai, bi), index


def test_eigen_passing_adscalar():
assert m.equal_to(1.0, 1.0)
assert not m.equal_to(1.0, 1.1)
assert m.equal_to(m.AutoDiffXd(0, [1.0]), m.AutoDiffXd(0, [1.0]))
assert not m.equal_to(m.AutoDiffXd(0, [1.0]), m.AutoDiffXd(0, [1.1]))

adscalar_mat = float_to_adscalar(ref, deriv=[1.0])
adscalar_vec_col = adscalar_mat[:, 0]
adscalar_vec_row = adscalar_mat[0, :]

# Checking if a Python vector is getting doubled, when passed into a dynamic or fixed
# row or col vector in Eigen.
double_adscalar_mat = float_to_adscalar(2 * ref, deriv=[2.0])
check_array(m.double_adscalar_col(adscalar_vec_col), double_adscalar_mat[:, 0])
check_array(m.double_adscalar_col5(adscalar_vec_col), double_adscalar_mat[:, 0])
check_array(m.double_adscalar_row(adscalar_vec_row), double_adscalar_mat[0, :])
check_array(m.double_adscalar_row6(adscalar_vec_row), double_adscalar_mat[0, :])

# Adding 7 to the a dynamic matrix using reference.
incr_adscalar_mat = float_to_adscalar(ref + 7, deriv=[1.0])
check_array(m.incr_adscalar_matrix(adscalar_mat, 7.0), incr_adscalar_mat)
# The original adscalar_mat remains unchanged in spite of passing by reference, since
# `Eigen::Ref<const CType>` permits copying, and copying is the only valid operation for
# `dtype=object`.
check_array(adscalar_to_float(adscalar_mat), ref)

# Changes in Python are not reflected in C++ when internal_reference is returned.
# These conversions should be disabled at runtime.

def expect_ref_error(func):
with pytest.raises(RuntimeError) as excinfo:
func()
assert "dtype=object" in str(excinfo.value)
assert "reachable" not in str(excinfo.value)

# - Return arguments.
expect_ref_error(lambda: m.get_cm_ref_adscalar())
expect_ref_error(lambda: m.get_rm_ref_adscalar())
expect_ref_error(lambda: m.get_cm_const_ref_adscalar())
expect_ref_error(lambda: m.get_rm_const_ref_adscalar())
# - - Mutable lvalues referenced via `reference_internal`.
return_tester = m.ReturnTester()
expect_ref_error(lambda: return_tester.get_ADScalarMat())
# - Input arguments, writeable `Ref<>`s.
expect_ref_error(lambda: m.add_cm_adscalar(adscalar_vec_col))
expect_ref_error(lambda: m.add_rm_adscalar(adscalar_vec_row))

# Checking Issue 1105
assert m.iss1105_col_obj(adscalar_vec_col[:, None])
assert m.iss1105_row_obj(adscalar_vec_row[None, :])

with pytest.raises(TypeError) as excinfo:
m.iss1105_row_obj(adscalar_vec_col[:, None])
assert "incompatible function arguments" in str(excinfo.value)
with pytest.raises(TypeError) as excinfo:
m.iss1105_col_obj(adscalar_vec_row[None, :])
assert "incompatible function arguments" in str(excinfo.value)


def test_eigen_obj_shape():
# Ensure that matrices are of the same shape for dtype=object when given a 1D NumPy array.
# RobotLocomotion/drake#8620
x_f = np.array([1, -1], dtype=float)
shape_f = m.cpp_matrix_shape(x_f)
x_ad = np.array([m.AutoDiffXd(0, []), m.AutoDiffXd(0, [])], dtype=object)
shape_ad = m.cpp_matrix_shape(x_ad)
shape_ad_ref = m.cpp_matrix_shape_ref(x_ad)
assert shape_f == shape_ad
assert shape_f == shape_ad_ref

Loading