8000 More improvements to test_linalg by asmeurer · Pull Request #101 · data-apis/array-api-tests · GitHub
[go: up one dir, main page]

Skip to content

More improvements to test_linalg #101

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

Merged
merged 61 commits into from
Feb 26, 2024
Merged

Conversation

asmeurer
Copy link
Member

So far just vector_norm, but more are incoming.

Note that NumPy will fail this test without numpy/numpy#21084.

I also realized that the linalg tests should be making better use of the helper functions. I've done so for this test, but still need to update the other tests.

Copy link
Member
@honno honno left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good.

Note that NumPy will fail this test without numpy/numpy#21084.

Ah, we should skip this in the workflow anywho because we're pinning against NumPy releases now.

@asmeurer
Copy link
Member Author

@honno see what you think of the allclose helpers I've added here. They can likely use some improvement, but they should work for now.

Comment on lines 152 to 172
def allclose(x, y, rel_tol=0.25, abs_tol=1, return_indices=False):
"""
Return True all elements of x and y are within tolerance

If return_indices=True, returns (False, (i, j)) when the arrays are not
close, where i and j are the indices into x and y of corresponding
non-close elements.
"""
for i, j in iter_indices(x.shape, y.shape):
i, j = i.raw, j.raw
a = x[i]
b = y[j]
if not (math.isfinite(a) and math.isfinite(b)):
# TODO: If a and b are both infinite, require the same type of infinity
continue
close = math.isclose(a, b, rel_tol=rel_tol, abs_tol=abs_tol)
if not close:
if return_indices:
return (False, (i, j))
return False
return True
Copy link
Member
@honno honno Apr 26, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep looks good, this is really how we have to do it if we can't use masking.

Maybe move this and assert_allclose to test_linalg.py for now, as ideally what we'd be doing is standardising these utils across function groups then, and scoping these utils for each function group makes it easier to distinguish where they're used and their subtle differences. (standardising seems quite doable, I just need to get round to it)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One problem here is represents a pretty significant performance hit. Before this commit (with the exact equality test), the linalg tests take 15 seconds on my computer. After, they take 44 seconds. Performance isn't our top priority, but maybe we should try array operations and only fallback to this when there are nonfinite values.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe move this and assert_allclose to test_linalg.py for now, as ideally what we'd be doing is standardising these utils across function groups then, and scoping these utils for each function group makes it easier to distinguish where they're used and their subtle differences. (standardising seems quite doable, I just need to get round to it)

Think I'd still like this for now, esp as I'm generally rethinking the pytest helpers for #200/general look at vectorisation. Not blocking tho.

Copy link
Member Author

Choose a reason for hiding this 8000 comment

The reason will be displayed to describe this comment to others. Learn more.

Actually I'm just going to delete them. I'm not using them anymore, because testing float arrays from linalg functions like this turned out to be too difficult.

@asmeurer
Copy link
Member Author

It looks like it isn't as straightforward as this. CuPy has several failures:

_________________________________________________________________________________________________________ test_eigh __________________________________________________________________________________________________________

    @pytest.mark.xp_extension('linalg')
>   @given(x=symmetric_matrices(finite=True))

array_api_tests/test_linalg.py:239: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
array_api_tests/test_linalg.py:256: in test_eigh
    _test_stacks(lambda x: linalg.eigh(x).eigenvectors, x,
array_api_tests/test_linalg.py:90: in _test_stacks
    assert_equal(res_stack, decomp_res_stack)
array_api_tests/test_linalg.py:49: in assert_equal
    assert_allclose(x, y)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

x = Array([[-0.40824828,  0.70710677,  0.57735026],
       [-0.40824828, -0.70710677,  0.57735026],
       [ 0.81649655,  0.        ,  0.57735026]], dtype=float32)
y = Array([[ 0.8164966 ,  0.        , -0.57735026],
       [-0.4082483 , -0.70710677, -0.5773504 ],
       [-0.40824828,  0.7071068 , -0.5773503 ]], dtype=float32), rel_tol = 0.25, abs_tol = 1

    def assert_allclose(x, y, rel_tol=0.25, abs_tol=1):
        """
        Test that x and y are approximately equal to each other.
    
        Also asserts that x and y have the same shape and dtype.
        """
        assert x.shape == y.shape, f"The input arrays do not have the same shapes ({x.shape} != {y.shape})"
    
        assert x.dtype == y.dtype, f"The input arrays do not have the same dtype ({x.dtype} != {y.dtype})"
    
        c = allclose(x, y, rel_tol=rel_tol, abs_tol=abs_tol, return_indices=True)
        if c is not True:
            _, (i, j) = c
>           raise AssertionError(f"The input arrays are not close with {rel_tol = } and {abs_tol = } at indices {i = } and {j = }")
E           AssertionError: The input arrays are not close with rel_tol = 0.25 and abs_tol = 1 at indices i = (0, 0) and j = (0, 0)

array_api_tests/array_helpers.py:187: AssertionError
--------------------------------------------------------------------------------------------------------- Hypothesis ---------------------------------------------------------------------------------------------------------
Falsifying example: test_eigh(
    x=Array([[[1., 1., 1.],
            [1., 1., 1.],
            [1., 1., 1.]]], dtype=float32),
)
___

Note that the sort order of eigenvalues is unspecified in the spec.

Also svd and svdvals fail with errors like

________________________________________________________________________________________________________ test_svdvals ________________________________________________________________________________________________________

    @pytest.mark.xp_extension('linalg')
>   @given(
        x=finite_matrices(),
    )

array_api_tests/test_linalg.py:562: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
array_api_tests/test_linalg.py:577: in test_svdvals
    _test_stacks(linalg.svdvals, x, dims=1, res=res)
array_api_tests/test_linalg.py:90: in _test_stacks
    assert_equal(res_stack, decomp_res_stack)
array_api_tests/test_linalg.py:49: in assert_equal
    assert_allclose(x, y)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

x = Array([34741248.,        0.], dtype=float32), y = Array([3.4741252e+07, 1.3871019e+00], dtype=float32), rel_tol = 0.25, abs_tol = 1

    def assert_allclose(x, y, rel_tol=0.25, abs_tol=1):
        """
        Test that x and y are approximately equal to each other.
    
        Also asserts that x and y have the same shape and dtype.
        """
        assert x.shape == y.shape, f"The input arrays do not have the same shapes ({x.shape} != {y.shape})"
    
        assert x.dtype == y.dtype, f"The input arrays do not have the same dtype ({x.dtype} != {y.dtype})"
    
        c = allclose(x, y, rel_tol=rel_tol, abs_tol=abs_tol, return_indices=True)
        if c is not True:
            _, (i, j) = c
>           raise AssertionError(f"The input arrays are not close with {rel_tol = } and {abs_tol = } at indices {i = } and {j = }")
E           AssertionError: The input arrays are not close with rel_tol = 0.25 and abs_tol = 1 at indices i = (1,) and j = (1,)

array_api_tests/array_helpers.py:187: AssertionError

which shows that using an absolute tolerance is not going to work.

There are also some test failures which I think are due to bugs in cupy. This one I'm not sure about

>>> import cupy.array_api
>>> x1 = cupy.array_api.asarray([51307], dtype=cupy.array_api.uint16)
>>> x2 = cupy.array_api.asarray([[[327]]], dtype=cupy.array_api.uint16)
>>> cupy.array_api.linalg.matmul(x1, x2)
Array([[172]], dtype=uint16)
>>> cupy.array_api.linalg.matmul(x1, x2[0, ...])
Array([173], dtype=uint16)
>>> x1
Array([51307], dtype=uint16)
>>> import numpy as np
>>> np.asarray([51307*327], dtype=np.uint16)
array([173], dtype=uint16)

CuPy is clearly doing something weird here, but should this overflow behavior be something that we should be avoiding in the test suite? If so then I guess we need to be more careful about how we generate integer array inputs to functions like matmul.