289 lines
		
	
	
		
			9.2 KiB
		
	
	
	
		
			Python
		
	
	
		
		
			
		
	
	
			289 lines
		
	
	
		
			9.2 KiB
		
	
	
	
		
			Python
		
	
	
| 
								 | 
							
								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[?, ?, ?]]"
							 | 
						||
| 
								 | 
							
								    )
							 |