From 6155e4a61be8408c6885d3dda34de07a1625eba9 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Mon, 24 Jan 2022 17:24:38 -0700 Subject: [PATCH 1/7] Use ndindex.iter_indices in _test_stacks in the linalg tests This adds ndindex >= 1.6 as a dependency of the test suite. --- array_api_tests/test_linalg.py | 49 +++++++++++++++++++++++++--------- requirements.txt | 1 + 2 files changed, 38 insertions(+), 12 deletions(-) diff --git a/array_api_tests/test_linalg.py b/array_api_tests/test_linalg.py index 2d8289d7..721fbf45 100644 --- a/array_api_tests/test_linalg.py +++ b/array_api_tests/test_linalg.py @@ -16,7 +16,8 @@ import pytest from hypothesis import assume, given from hypothesis.strategies import (booleans, composite, none, tuples, integers, - shared, sampled_from, data, just) + shared, sampled_from, one_of, data, just) +from ndindex import iter_indices from .array_helpers import assert_exactly_equal, asarray, equal, zero, infinity from .hypothesis_helpers import (xps, dtypes, shapes, kwargs, matrix_shapes, @@ -43,25 +44,49 @@ # Standin strategy for not yet implemented tests todo = none() -def _test_stacks(f, *args, res=None, dims=2, true_val=None, **kw): +def _test_stacks(f, *args, res=None, dims=2, true_val=None, matrix_axes=(-2, -1), **kw): """ Test that f(*args, **kw) maps across stacks of matrices - dims is the number of dimensions f should have for a single n x m matrix - stack. + dims is the number of dimensions f(*args) should have for a single n x m + matrix stack. + + matrix_axes are the axes along which matrices (or vectors) are stacked in + the input. + + true_val may be a function such that true_val(*x_stacks, **kw) gives the + true value for f on a stack. + + res should be the result of f(*args, **kw). It is computed if not passed + in. - true_val may be a function such that true_val(*x_stacks) gives the true - value for f on a stack """ if res is None: res = f(*args, **kw) - shape = args[0].shape if len(args) == 1 else broadcast_shapes(*[x.shape - for x in args]) - for _idx in sh.ndindex(shape[:-2]): - idx = _idx + (slice(None),)*dims - res_stack = res[idx] - x_stacks = [x[_idx + (...,)] for x in args] + shapes = [x.shape for x in args] + + for (x_idxes, (res_idx,)) in zip( + iter_indices(*shapes, skip_axes=matrix_axes), + iter_indices(res.shape, skip_axes=tuple(range(-dims, 0)))): + x_idxes = [x_idx.raw for x_idx in x_idxes] + res_idx = res_idx.raw + # res should have `dims` slices in it. Cases where there are more than + # `dims` slices are ambiguous, but that should only occur in cases + # where axes = (-2, -1). + # res_idx2 = [] + # d = dims + # for i in res_idx: + # if isinstance(i, slice): + # if d: + # res_idx2.append(i) + # d -= 1 + # else: + # res_idx2.append(i) + # res_idx2 = tuple(res_idx2) + + res_stack = res[res_idx] + x_stacks = [x[x_idx] for x, x_idx in zip(args, x_idxes)] decomp_res_stack = f(*x_stacks, **kw) assert_exactly_equal(res_stack, decomp_res_stack) if true_val: diff --git a/requirements.txt b/requirements.txt index 8773b0e9..95a49cfa 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ pytest hypothesis>=6.31.1 +ndindex>=1.6 regex removestar From b2fdefd70cbd24db7aa053ed240c816975dd9b42 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Mon, 24 Jan 2022 17:32:37 -0700 Subject: [PATCH 2/7] Remove some commented out code --- array_api_tests/test_linalg.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/array_api_tests/test_linalg.py b/array_api_tests/test_linalg.py index 721fbf45..78c73307 100644 --- a/array_api_tests/test_linalg.py +++ b/array_api_tests/test_linalg.py @@ -71,19 +71,6 @@ def _test_stacks(f, *args, res=None, dims=2, true_val=None, matrix_axes=(-2, -1) iter_indices(res.shape, skip_axes=tuple(range(-dims, 0)))): x_idxes = [x_idx.raw for x_idx in x_idxes] res_idx = res_idx.raw - # res should have `dims` slices in it. Cases where there are more than - # `dims` slices are ambiguous, but that should only occur in cases - # where axes = (-2, -1). - # res_idx2 = [] - # d = dims - # for i in res_idx: - # if isinstance(i, slice): - # if d: - # res_idx2.append(i) - # d -= 1 - # else: - # res_idx2.append(i) - # res_idx2 = tuple(res_idx2) res_stack = res[res_idx] x_stacks = [x[x_idx] for x, x_idx in zip(args, x_idxes)] From 958d5c5d1cb62ccbdf57c5bd825b22a1aa13e86a Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Mon, 24 Jan 2022 17:32:44 -0700 Subject: [PATCH 3/7] Comment out a slotdet test that doesn't hold exactly --- array_api_tests/test_linalg.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/array_api_tests/test_linalg.py b/array_api_tests/test_linalg.py index 78c73307..3a56ff0c 100644 --- a/array_api_tests/test_linalg.py +++ b/array_api_tests/test_linalg.py @@ -464,10 +464,12 @@ def test_slogdet(x): # Check that when the determinant is 0, the sign and logabsdet are (0, # -inf). - d = linalg.det(x) - zero_det = equal(d, zero(d.shape, d.dtype)) - assert_exactly_equal(sign[zero_det], zero(sign[zero_det].shape, x.dtype)) - assert_exactly_equal(logabsdet[zero_det], -infinity(logabsdet[zero_det].shape, x.dtype)) + # TODO: This test does not necessarily hold exactly. Update it to test it + # approximately. + # d = linalg.det(x) + # zero_det = equal(d, zero(d.shape, d.dtype)) + # assert_exactly_equal(sign[zero_det], zero(sign[zero_det].shape, x.dtype)) + # assert_exactly_equal(logabsdet[zero_det], -infinity(logabsdet[zero_det].shape, x.dtype)) # More generally, det(x) should equal sign*exp(logabsdet), but this does # not hold exactly due to floating-point loss of precision. From adc9646c37b0fd02281707791e535827fce7d662 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Mon, 24 Jan 2022 17:44:34 -0700 Subject: [PATCH 4/7] Don't generate 0-dimensional inputs to test_vecdot --- array_api_tests/test_linalg.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/array_api_tests/test_linalg.py b/array_api_tests/test_linalg.py index 3a56ff0c..1721cdbb 100644 --- a/array_api_tests/test_linalg.py +++ b/array_api_tests/test_linalg.py @@ -628,7 +628,7 @@ def true_trace(x_stack): @given( dtypes=mutually_promotable_dtypes(dtypes=dh.numeric_dtypes), - shape=shapes(), + shape=shapes(min_dims=1), data=data(), ) def test_vecdot(dtypes, shape, data): From 1a435ae4b65825c67aaf3f5e423f9dbe13009329 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Mon, 24 Jan 2022 17:44:50 -0700 Subject: [PATCH 5/7] Update the xfail skips for numpy for linalg --- .github/workflows/numpy.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/numpy.yml b/.github/workflows/numpy.yml index aed716f1..3581edee 100644 --- a/.github/workflows/numpy.yml +++ b/.github/workflows/numpy.yml @@ -34,8 +34,9 @@ jobs: array_api_tests/test_creation_functions.py::test_linspace # https://github.com/numpy/numpy/issues/20870 array_api_tests/test_data_type_functions.py::test_can_cast - # linalg tests generally need more mulling over - array_api_tests/test_linalg.py + # The return dtype for trace is not consistent in the spec + # (https://github.com/data-apis/array-api/issues/202#issuecomment-952529197) + array_api_tests/test_linalg.py::test_trace # waiting on NumPy to allow/revert distinct NaNs for np.unique # https://github.com/numpy/numpy/issues/20326#issuecomment-1012380448 array_api_tests/test_set_functions.py From 933e6105ea87b8b657eb8ade995fa0b4e25ab04a Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Mon, 24 Jan 2022 17:45:05 -0700 Subject: [PATCH 6/7] Allow specifying the equality check in the linalg _test_stacks We will eventually change this to be a float-point approximate check rather than exact equality. For now, this is not updated. See https://github.com/data-apis/array-api-tests/issues/44 --- array_api_tests/test_linalg.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/array_api_tests/test_linalg.py b/array_api_tests/test_linalg.py index 1721cdbb..a1fce242 100644 --- a/array_api_tests/test_linalg.py +++ b/array_api_tests/test_linalg.py @@ -44,7 +44,8 @@ # Standin strategy for not yet implemented tests todo = none() -def _test_stacks(f, *args, res=None, dims=2, true_val=None, matrix_axes=(-2, -1), **kw): +def _test_stacks(f, *args, res=None, dims=2, true_val=None, matrix_axes=(-2, -1), + assert_equal=assert_exactly_equal, **kw): """ Test that f(*args, **kw) maps across stacks of matrices @@ -75,9 +76,9 @@ def _test_stacks(f, *args, res=None, dims=2, true_val=None, matrix_axes=(-2, -1) res_stack = res[res_idx] x_stacks = [x[x_idx] for x, x_idx in zip(args, x_idxes)] decomp_res_stack = f(*x_stacks, **kw) - assert_exactly_equal(res_stack, decomp_res_stack) + assert_equal(res_stack, decomp_res_stack) if true_val: - assert_exactly_equal(decomp_res_stack, true_val(*x_stacks)) + assert_equal(decomp_res_stack, true_val(*x_stacks)) def _test_namedtuple(res, fields, func_name): """ From a3423fe040f19462daeb3eae81e48b8db345c18a Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Mon, 24 Jan 2022 17:47:47 -0700 Subject: [PATCH 7/7] Fix pyflakes issues --- array_api_tests/test_linalg.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/array_api_tests/test_linalg.py b/array_api_tests/test_linalg.py index a1fce242..62c93562 100644 --- a/array_api_tests/test_linalg.py +++ b/array_api_tests/test_linalg.py @@ -16,10 +16,10 @@ import pytest from hypothesis import assume, given from hypothesis.strategies import (booleans, composite, none, tuples, integers, - shared, sampled_from, one_of, data, just) + shared, sampled_from, data, just) from ndindex import iter_indices -from .array_helpers import assert_exactly_equal, asarray, equal, zero, infinity +from .array_helpers import assert_exactly_equal, asarray from .hypothesis_helpers import (xps, dtypes, shapes, kwargs, matrix_shapes, square_matrix_shapes, symmetric_matrices, positive_definite_matrices, MAX_ARRAY_SIZE,