File indexing completed on 2025-01-18 10:17:53
0001 import sys
0002
0003 import pytest
0004
0005 np = pytest.importorskip("numpy")
0006 eigen_tensor = pytest.importorskip("pybind11_tests.eigen_tensor")
0007 submodules = [eigen_tensor.c_style, eigen_tensor.f_style]
0008 try:
0009 import eigen_tensor_avoid_stl_array as avoid
0010
0011 submodules += [avoid.c_style, avoid.f_style]
0012 except ImportError as e:
0013
0014 raise RuntimeError(
0015 "import eigen_tensor_avoid_stl_array FAILED, while "
0016 "import pybind11_tests.eigen_tensor succeeded. "
0017 "Please ensure that "
0018 "test_eigen_tensor.cpp & "
0019 "eigen_tensor_avoid_stl_array.cpp "
0020 "are built together (or both are not built if Eigen is not available)."
0021 ) from e
0022
0023 tensor_ref = np.empty((3, 5, 2), dtype=np.int64)
0024
0025 for i in range(tensor_ref.shape[0]):
0026 for j in range(tensor_ref.shape[1]):
0027 for k in range(tensor_ref.shape[2]):
0028 tensor_ref[i, j, k] = i * (5 * 2) + j * 2 + k
0029
0030 indices = (2, 3, 1)
0031
0032
0033 @pytest.fixture(autouse=True)
0034 def cleanup():
0035 for module in submodules:
0036 module.setup()
0037
0038 yield
0039
0040 for module in submodules:
0041 assert module.is_ok()
0042
0043
0044 def test_import_avoid_stl_array():
0045 pytest.importorskip("eigen_tensor_avoid_stl_array")
0046 assert len(submodules) == 4
0047
0048
0049 def assert_equal_tensor_ref(mat, writeable=True, modified=None):
0050 assert mat.flags.writeable == writeable
0051
0052 copy = np.array(tensor_ref)
0053 if modified is not None:
0054 copy[indices] = modified
0055
0056 np.testing.assert_array_equal(mat, copy)
0057
0058
0059 @pytest.mark.parametrize("m", submodules)
0060 @pytest.mark.parametrize("member_name", ["member", "member_view"])
0061 def test_reference_internal(m, member_name):
0062
0063 if not hasattr(sys, "getrefcount"):
0064 pytest.skip("No reference counting")
0065 foo = m.CustomExample()
0066 counts = sys.getrefcount(foo)
0067 mem = getattr(foo, member_name)
0068 assert_equal_tensor_ref(mem, writeable=False)
0069 new_counts = sys.getrefcount(foo)
0070 assert new_counts == counts + 1
0071 assert_equal_tensor_ref(mem, writeable=False)
0072 del mem
0073 assert sys.getrefcount(foo) == counts
0074
0075
0076 assert_equal_funcs = [
0077 "copy_tensor",
0078 "copy_fixed_tensor",
0079 "copy_const_tensor",
0080 "move_tensor_copy",
0081 "move_fixed_tensor_copy",
0082 "take_tensor",
0083 "take_fixed_tensor",
0084 "reference_tensor",
0085 "reference_tensor_v2",
0086 "reference_fixed_tensor",
0087 "reference_view_of_tensor",
0088 "reference_view_of_tensor_v3",
0089 "reference_view_of_tensor_v5",
0090 "reference_view_of_fixed_tensor",
0091 ]
0092
0093 assert_equal_const_funcs = [
0094 "reference_view_of_tensor_v2",
0095 "reference_view_of_tensor_v4",
0096 "reference_view_of_tensor_v6",
0097 "reference_const_tensor",
0098 "reference_const_tensor_v2",
0099 ]
0100
0101
0102 @pytest.mark.parametrize("m", submodules)
0103 @pytest.mark.parametrize("func_name", assert_equal_funcs + assert_equal_const_funcs)
0104 def test_convert_tensor_to_py(m, func_name):
0105 writeable = func_name in assert_equal_funcs
0106 assert_equal_tensor_ref(getattr(m, func_name)(), writeable=writeable)
0107
0108
0109 @pytest.mark.parametrize("m", submodules)
0110 def test_bad_cpp_to_python_casts(m):
0111
0112 with pytest.raises(
0113 RuntimeError, match="Cannot use reference internal when there is no parent"
0114 ):
0115 m.reference_tensor_internal()
0116
0117 with pytest.raises(RuntimeError, match="Cannot move from a constant reference"):
0118 m.move_const_tensor()
0119
0120 with pytest.raises(
0121 RuntimeError, match="Cannot take ownership of a const reference"
0122 ):
0123 m.take_const_tensor()
0124
0125 with pytest.raises(
0126 RuntimeError,
0127 match="Invalid return_value_policy for Eigen Map type, must be either reference or reference_internal",
0128 ):
0129 m.take_view_tensor()
0130
0131
0132 @pytest.mark.parametrize("m", submodules)
0133 def test_bad_python_to_cpp_casts(m):
0134
0135 with pytest.raises(
0136 TypeError, match=r"^round_trip_tensor\(\): incompatible function arguments"
0137 ):
0138 m.round_trip_tensor(np.zeros((2, 3)))
0139
0140 with pytest.raises(TypeError, match=r"^Cannot cast array data from dtype"):
0141 m.round_trip_tensor(np.zeros(dtype=np.str_, shape=(2, 3, 1)))
0142
0143 with pytest.raises(
0144 TypeError,
0145 match=r"^round_trip_tensor_noconvert\(\): incompatible function arguments",
0146 ):
0147 m.round_trip_tensor_noconvert(tensor_ref)
0148
0149 assert_equal_tensor_ref(
0150 m.round_trip_tensor_noconvert(tensor_ref.astype(np.float64))
0151 )
0152
0153 if m.needed_options == "F":
0154 bad_options = "C"
0155 else:
0156 bad_options = "F"
0157
0158 with pytest.raises(
0159 TypeError, match=r"^round_trip_view_tensor\(\): incompatible function arguments"
0160 ):
0161 m.round_trip_view_tensor(
0162 np.zeros((3, 5, 2), dtype=np.float64, order=bad_options)
0163 )
0164
0165 with pytest.raises(
0166 TypeError, match=r"^round_trip_view_tensor\(\): incompatible function arguments"
0167 ):
0168 m.round_trip_view_tensor(
0169 np.zeros((3, 5, 2), dtype=np.float32, order=m.needed_options)
0170 )
0171
0172 with pytest.raises(
0173 TypeError, match=r"^round_trip_view_tensor\(\): incompatible function arguments"
0174 ):
0175 m.round_trip_view_tensor(
0176 np.zeros((3, 5), dtype=np.float64, order=m.needed_options)
0177 )
0178
0179 with pytest.raises(
0180 TypeError, match=r"^round_trip_view_tensor\(\): incompatible function arguments"
0181 ):
0182 temp = np.zeros((3, 5, 2), dtype=np.float64, order=m.needed_options)
0183 m.round_trip_view_tensor(
0184 temp[:, ::-1, :],
0185 )
0186
0187 with pytest.raises(
0188 TypeError, match=r"^round_trip_view_tensor\(\): incompatible function arguments"
0189 ):
0190 temp = np.zeros((3, 5, 2), dtype=np.float64, order=m.needed_options)
0191 temp.setflags(write=False)
0192 m.round_trip_view_tensor(temp)
0193
0194
0195 @pytest.mark.parametrize("m", submodules)
0196 def test_references_actually_refer(m):
0197
0198 a = m.reference_tensor()
0199 temp = a[indices]
0200 a[indices] = 100
0201 assert_equal_tensor_ref(m.copy_const_tensor(), modified=100)
0202 a[indices] = temp
0203 assert_equal_tensor_ref(m.copy_const_tensor())
0204
0205 a = m.reference_view_of_tensor()
0206 a[indices] = 100
0207 assert_equal_tensor_ref(m.copy_const_tensor(), modified=100)
0208 a[indices] = temp
0209 assert_equal_tensor_ref(m.copy_const_tensor())
0210
0211
0212 @pytest.mark.parametrize("m", submodules)
0213 def test_round_trip(m):
0214
0215 assert_equal_tensor_ref(m.round_trip_tensor(tensor_ref))
0216
0217 with pytest.raises(TypeError, match="^Cannot cast array data from"):
0218 assert_equal_tensor_ref(m.round_trip_tensor2(tensor_ref))
0219
0220 assert_equal_tensor_ref(m.round_trip_tensor2(np.array(tensor_ref, dtype=np.int32)))
0221 assert_equal_tensor_ref(m.round_trip_fixed_tensor(tensor_ref))
0222 assert_equal_tensor_ref(m.round_trip_aligned_view_tensor(m.reference_tensor()))
0223
0224 copy = np.array(tensor_ref, dtype=np.float64, order=m.needed_options)
0225 assert_equal_tensor_ref(m.round_trip_view_tensor(copy))
0226 assert_equal_tensor_ref(m.round_trip_view_tensor_ref(copy))
0227 assert_equal_tensor_ref(m.round_trip_view_tensor_ptr(copy))
0228 copy.setflags(write=False)
0229 assert_equal_tensor_ref(m.round_trip_const_view_tensor(copy))
0230
0231 np.testing.assert_array_equal(
0232 tensor_ref[:, ::-1, :], m.round_trip_tensor(tensor_ref[:, ::-1, :])
0233 )
0234
0235 assert m.round_trip_rank_0(np.float64(3.5)) == 3.5
0236 assert m.round_trip_rank_0(3.5) == 3.5
0237
0238 with pytest.raises(
0239 TypeError,
0240 match=r"^round_trip_rank_0_noconvert\(\): incompatible function arguments",
0241 ):
0242 m.round_trip_rank_0_noconvert(np.float64(3.5))
0243
0244 with pytest.raises(
0245 TypeError,
0246 match=r"^round_trip_rank_0_noconvert\(\): incompatible function arguments",
0247 ):
0248 m.round_trip_rank_0_noconvert(3.5)
0249
0250 with pytest.raises(
0251 TypeError, match=r"^round_trip_rank_0_view\(\): incompatible function arguments"
0252 ):
0253 m.round_trip_rank_0_view(np.float64(3.5))
0254
0255 with pytest.raises(
0256 TypeError, match=r"^round_trip_rank_0_view\(\): incompatible function arguments"
0257 ):
0258 m.round_trip_rank_0_view(3.5)
0259
0260
0261 @pytest.mark.parametrize("m", submodules)
0262 def test_round_trip_references_actually_refer(m):
0263
0264
0265 copy = np.array(tensor_ref, dtype=np.float64, order=m.needed_options)
0266 a = m.round_trip_view_tensor(copy)
0267 temp = a[indices]
0268 a[indices] = 100
0269 assert_equal_tensor_ref(copy, modified=100)
0270 a[indices] = temp
0271 assert_equal_tensor_ref(copy)
0272
0273
0274 @pytest.mark.parametrize("m", submodules)
0275 def test_doc_string(m, doc):
0276 assert (
0277 doc(m.copy_tensor) == "copy_tensor() -> numpy.ndarray[numpy.float64[?, ?, ?]]"
0278 )
0279 assert (
0280 doc(m.copy_fixed_tensor)
0281 == "copy_fixed_tensor() -> numpy.ndarray[numpy.float64[3, 5, 2]]"
0282 )
0283 assert (
0284 doc(m.reference_const_tensor)
0285 == "reference_const_tensor() -> numpy.ndarray[numpy.float64[?, ?, ?]]"
0286 )
0287
0288 order_flag = f"flags.{m.needed_options.lower()}_contiguous"
0289 assert doc(m.round_trip_view_tensor) == (
0290 f"round_trip_view_tensor(arg0: numpy.ndarray[numpy.float64[?, ?, ?], flags.writeable, {order_flag}])"
0291 + f" -> numpy.ndarray[numpy.float64[?, ?, ?], flags.writeable, {order_flag}]"
0292 )
0293 assert doc(m.round_trip_const_view_tensor) == (
0294 f"round_trip_const_view_tensor(arg0: numpy.ndarray[numpy.float64[?, ?, ?], {order_flag}])"
0295 + " -> numpy.ndarray[numpy.float64[?, ?, ?]]"
0296 )