aboutsummaryrefslogtreecommitdiff
path: root/tests/test_eigen_tensor.py
diff options
context:
space:
mode:
authorDan Willemsen <dwillemsen@google.com>2023-08-23 23:12:47 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2023-08-23 23:12:47 +0000
commit54f25be124181b5b5e37d686ae78a2ef43f77594 (patch)
treea199f13004f9dca173b6bc46161736ffc224f6fc /tests/test_eigen_tensor.py
parent0d8c94d2775dd2629be4247df9847de23c073490 (diff)
parenteba01819262de24db698eeb259f488ba2b1e89e6 (diff)
downloadpybind11-54f25be124181b5b5e37d686ae78a2ef43f77594.tar.gz
Upgrade pybind11 to v2.11.0 am: df375684ae am: c3651ffda2 am: b8f469b116 am: ce45d68f7a am: eba0181926HEADandroid-14.0.0_r50mastermainandroid14-qpr3-release
Original change: https://android-review.googlesource.com/c/platform/external/python/pybind11/+/2722853 Change-Id: Iac83f632be76c6ead72145c9705e5af0233309ac Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
Diffstat (limited to 'tests/test_eigen_tensor.py')
-rw-r--r--tests/test_eigen_tensor.py288
1 files changed, 288 insertions, 0 deletions
diff --git a/tests/test_eigen_tensor.py b/tests/test_eigen_tensor.py
new file mode 100644
index 00000000..3e7ee6b7
--- /dev/null
+++ b/tests/test_eigen_tensor.py
@@ -0,0 +1,288 @@
+import sys
+
+import pytest
+
+np = pytest.importorskip("numpy")
+eigen_tensor = pytest.importorskip("pybind11_tests.eigen_tensor")
+submodules = [eigen_tensor.c_style, eigen_tensor.f_style]
+try:
+ import eigen_tensor_avoid_stl_array as avoid
+
+ submodules += [avoid.c_style, avoid.f_style]
+except ImportError as e:
+ # Ensure config, build, toolchain, etc. issues are not masked here:
+ msg = (
+ "import eigen_tensor_avoid_stl_array FAILED, while "
+ "import pybind11_tests.eigen_tensor succeeded. "
+ "Please ensure that "
+ "test_eigen_tensor.cpp & "
+ "eigen_tensor_avoid_stl_array.cpp "
+ "are built together (or both are not built if Eigen is not available)."
+ )
+ raise RuntimeError(msg) from e
+
+tensor_ref = np.empty((3, 5, 2), dtype=np.int64)
+
+for i in range(tensor_ref.shape[0]):
+ for j in range(tensor_ref.shape[1]):
+ for k in range(tensor_ref.shape[2]):
+ tensor_ref[i, j, k] = i * (5 * 2) + j * 2 + k
+
+indices = (2, 3, 1)
+
+
+@pytest.fixture(autouse=True)
+def cleanup():
+ for module in submodules:
+ module.setup()
+
+ yield
+
+ for module in submodules:
+ assert module.is_ok()
+
+
+def test_import_avoid_stl_array():
+ pytest.importorskip("eigen_tensor_avoid_stl_array")
+ assert len(submodules) == 4
+
+
+def assert_equal_tensor_ref(mat, writeable=True, modified=None):
+ assert mat.flags.writeable == writeable
+
+ copy = np.array(tensor_ref)
+ if modified is not None:
+ copy[indices] = modified
+
+ np.testing.assert_array_equal(mat, copy)
+
+
+@pytest.mark.parametrize("m", submodules)
+@pytest.mark.parametrize("member_name", ["member", "member_view"])
+def test_reference_internal(m, member_name):
+ if not hasattr(sys, "getrefcount"):
+ pytest.skip("No reference counting")
+ foo = m.CustomExample()
+ counts = sys.getrefcount(foo)
+ mem = getattr(foo, member_name)
+ assert_equal_tensor_ref(mem, writeable=False)
+ new_counts = sys.getrefcount(foo)
+ assert new_counts == counts + 1
+ assert_equal_tensor_ref(mem, writeable=False)
+ del mem
+ assert sys.getrefcount(foo) == counts
+
+
+assert_equal_funcs = [
+ "copy_tensor",
+ "copy_fixed_tensor",
+ "copy_const_tensor",
+ "move_tensor_copy",
+ "move_fixed_tensor_copy",
+ "take_tensor",
+ "take_fixed_tensor",
+ "reference_tensor",
+ "reference_tensor_v2",
+ "reference_fixed_tensor",
+ "reference_view_of_tensor",
+ "reference_view_of_tensor_v3",
+ "reference_view_of_tensor_v5",
+ "reference_view_of_fixed_tensor",
+]
+
+assert_equal_const_funcs = [
+ "reference_view_of_tensor_v2",
+ "reference_view_of_tensor_v4",
+ "reference_view_of_tensor_v6",
+ "reference_const_tensor",
+ "reference_const_tensor_v2",
+]
+
+
+@pytest.mark.parametrize("m", submodules)
+@pytest.mark.parametrize("func_name", assert_equal_funcs + assert_equal_const_funcs)
+def test_convert_tensor_to_py(m, func_name):
+ writeable = func_name in assert_equal_funcs
+ assert_equal_tensor_ref(getattr(m, func_name)(), writeable=writeable)
+
+
+@pytest.mark.parametrize("m", submodules)
+def test_bad_cpp_to_python_casts(m):
+ with pytest.raises(
+ RuntimeError, match="Cannot use reference internal when there is no parent"
+ ):
+ m.reference_tensor_internal()
+
+ with pytest.raises(RuntimeError, match="Cannot move from a constant reference"):
+ m.move_const_tensor()
+
+ with pytest.raises(
+ RuntimeError, match="Cannot take ownership of a const reference"
+ ):
+ m.take_const_tensor()
+
+ with pytest.raises(
+ RuntimeError,
+ match="Invalid return_value_policy for Eigen Map type, must be either reference or reference_internal",
+ ):
+ m.take_view_tensor()
+
+
+@pytest.mark.parametrize("m", submodules)
+def test_bad_python_to_cpp_casts(m):
+ with pytest.raises(
+ TypeError, match=r"^round_trip_tensor\(\): incompatible function arguments"
+ ):
+ m.round_trip_tensor(np.zeros((2, 3)))
+
+ with pytest.raises(TypeError, match=r"^Cannot cast array data from dtype"):
+ m.round_trip_tensor(np.zeros(dtype=np.str_, shape=(2, 3, 1)))
+
+ with pytest.raises(
+ TypeError,
+ match=r"^round_trip_tensor_noconvert\(\): incompatible function arguments",
+ ):
+ m.round_trip_tensor_noconvert(tensor_ref)
+
+ assert_equal_tensor_ref(
+ m.round_trip_tensor_noconvert(tensor_ref.astype(np.float64))
+ )
+
+ bad_options = "C" if m.needed_options == "F" else "F"
+ # Shape, dtype and the order need to be correct for a TensorMap cast
+ with pytest.raises(
+ TypeError, match=r"^round_trip_view_tensor\(\): incompatible function arguments"
+ ):
+ m.round_trip_view_tensor(
+ np.zeros((3, 5, 2), dtype=np.float64, order=bad_options)
+ )
+
+ with pytest.raises(
+ TypeError, match=r"^round_trip_view_tensor\(\): incompatible function arguments"
+ ):
+ m.round_trip_view_tensor(
+ np.zeros((3, 5, 2), dtype=np.float32, order=m.needed_options)
+ )
+
+ with pytest.raises(
+ TypeError, match=r"^round_trip_view_tensor\(\): incompatible function arguments"
+ ):
+ m.round_trip_view_tensor(
+ np.zeros((3, 5), dtype=np.float64, order=m.needed_options)
+ )
+
+ temp = np.zeros((3, 5, 2), dtype=np.float64, order=m.needed_options)
+ with pytest.raises(
+ TypeError, match=r"^round_trip_view_tensor\(\): incompatible function arguments"
+ ):
+ m.round_trip_view_tensor(
+ temp[:, ::-1, :],
+ )
+
+ temp = np.zeros((3, 5, 2), dtype=np.float64, order=m.needed_options)
+ temp.setflags(write=False)
+ with pytest.raises(
+ TypeError, match=r"^round_trip_view_tensor\(\): incompatible function arguments"
+ ):
+ m.round_trip_view_tensor(temp)
+
+
+@pytest.mark.parametrize("m", submodules)
+def test_references_actually_refer(m):
+ a = m.reference_tensor()
+ temp = a[indices]
+ a[indices] = 100
+ assert_equal_tensor_ref(m.copy_const_tensor(), modified=100)
+ a[indices] = temp
+ assert_equal_tensor_ref(m.copy_const_tensor())
+
+ a = m.reference_view_of_tensor()
+ a[indices] = 100
+ assert_equal_tensor_ref(m.copy_const_tensor(), modified=100)
+ a[indices] = temp
+ assert_equal_tensor_ref(m.copy_const_tensor())
+
+
+@pytest.mark.parametrize("m", submodules)
+def test_round_trip(m):
+ assert_equal_tensor_ref(m.round_trip_tensor(tensor_ref))
+
+ with pytest.raises(TypeError, match="^Cannot cast array data from"):
+ assert_equal_tensor_ref(m.round_trip_tensor2(tensor_ref))
+
+ assert_equal_tensor_ref(m.round_trip_tensor2(np.array(tensor_ref, dtype=np.int32)))
+ assert_equal_tensor_ref(m.round_trip_fixed_tensor(tensor_ref))
+ assert_equal_tensor_ref(m.round_trip_aligned_view_tensor(m.reference_tensor()))
+
+ copy = np.array(tensor_ref, dtype=np.float64, order=m.needed_options)
+ assert_equal_tensor_ref(m.round_trip_view_tensor(copy))
+ assert_equal_tensor_ref(m.round_trip_view_tensor_ref(copy))
+ assert_equal_tensor_ref(m.round_trip_view_tensor_ptr(copy))
+ copy.setflags(write=False)
+ assert_equal_tensor_ref(m.round_trip_const_view_tensor(copy))
+
+ np.testing.assert_array_equal(
+ tensor_ref[:, ::-1, :], m.round_trip_tensor(tensor_ref[:, ::-1, :])
+ )
+
+ assert m.round_trip_rank_0(np.float64(3.5)) == 3.5
+ assert m.round_trip_rank_0(3.5) == 3.5
+
+ with pytest.raises(
+ TypeError,
+ match=r"^round_trip_rank_0_noconvert\(\): incompatible function arguments",
+ ):
+ m.round_trip_rank_0_noconvert(np.float64(3.5))
+
+ with pytest.raises(
+ TypeError,
+ match=r"^round_trip_rank_0_noconvert\(\): incompatible function arguments",
+ ):
+ m.round_trip_rank_0_noconvert(3.5)
+
+ with pytest.raises(
+ TypeError, match=r"^round_trip_rank_0_view\(\): incompatible function arguments"
+ ):
+ m.round_trip_rank_0_view(np.float64(3.5))
+
+ with pytest.raises(
+ TypeError, match=r"^round_trip_rank_0_view\(\): incompatible function arguments"
+ ):
+ m.round_trip_rank_0_view(3.5)
+
+
+@pytest.mark.parametrize("m", submodules)
+def test_round_trip_references_actually_refer(m):
+ # Need to create a copy that matches the type on the C side
+ copy = np.array(tensor_ref, dtype=np.float64, order=m.needed_options)
+ a = m.round_trip_view_tensor(copy)
+ temp = a[indices]
+ a[indices] = 100
+ assert_equal_tensor_ref(copy, modified=100)
+ a[indices] = temp
+ assert_equal_tensor_ref(copy)
+
+
+@pytest.mark.parametrize("m", submodules)
+def test_doc_string(m, doc):
+ assert (
+ doc(m.copy_tensor) == "copy_tensor() -> numpy.ndarray[numpy.float64[?, ?, ?]]"
+ )
+ assert (
+ doc(m.copy_fixed_tensor)
+ == "copy_fixed_tensor() -> numpy.ndarray[numpy.float64[3, 5, 2]]"
+ )
+ assert (
+ doc(m.reference_const_tensor)
+ == "reference_const_tensor() -> numpy.ndarray[numpy.float64[?, ?, ?]]"
+ )
+
+ order_flag = f"flags.{m.needed_options.lower()}_contiguous"
+ assert doc(m.round_trip_view_tensor) == (
+ f"round_trip_view_tensor(arg0: numpy.ndarray[numpy.float64[?, ?, ?], flags.writeable, {order_flag}])"
+ f" -> numpy.ndarray[numpy.float64[?, ?, ?], flags.writeable, {order_flag}]"
+ )
+ assert doc(m.round_trip_const_view_tensor) == (
+ f"round_trip_const_view_tensor(arg0: numpy.ndarray[numpy.float64[?, ?, ?], {order_flag}])"
+ " -> numpy.ndarray[numpy.float64[?, ?, ?]]"
+ )