Thanks to visit codestin.com
Credit goes to github.com

Skip to content

TST: use xpx.testing assertions#25143

Draft
prady0t wants to merge 12 commits into
scipy:mainfrom
prady0t:use_array-api-extra-testing
Draft

TST: use xpx.testing assertions#25143
prady0t wants to merge 12 commits into
scipy:mainfrom
prady0t:use_array-api-extra-testing

Conversation

@prady0t
Copy link
Copy Markdown

@prady0t prady0t commented May 15, 2026

Reference issue

What does this implement/fix?

This is a draft PR to check how much we have progressed with xp_assert functions in the array_api_extra library. We are checking how easy it is for other libraries to implement these testing functions in their existing test suite.

Additional information

This is to remain a draft PR for reference. In case we want to merge it, I can put some effort into cleaning it and making some necessary changes in the test_array_api.py file. There are 2 things different in the array_api_extra version of tests from scipy:

  • Checking namespaces cannot be disabled, which can be done in SciPy. See
  • SciPy allows for Python lists and scalars in the assert functions; these are not allowed in the array_api_extra version. See

I've made some efforts to take these into account and reduce the number of failing tests by checking for Python scalars and lists, and converting them with xp.asarray before feeding them to array_api_extra.
Most test failures are due to our inability to disable namespace checks; they originate from test_array_api.py.

See:

AI Generation Disclosure

No AI tools used

@github-actions github-actions Bot added scipy.interpolate scipy.cluster scipy._lib array types Items related to array API support and input array validation (see gh-18286) labels May 15, 2026
@prady0t prady0t changed the title Use array api extra testing Use array_api_extra's testing functions May 15, 2026
@prady0t
Copy link
Copy Markdown
Author

prady0t commented May 15, 2026

We have 8 total failures. All of them are expected namespace mismatch failures, as array_api_extra doesn't allow disabling the namespace check.

Comment thread scipy/interpolate/tests/test_bsplines.py Outdated
Comment thread scipy/_lib/_array_api.py Outdated
Signed-off-by: Pradyot Ranjan <[email protected]>
Comment thread scipy/interpolate/tests/test_bsplines.py Outdated
Comment thread scipy/_lib/_array_api.py Outdated
Comment thread scipy/_lib/_array_api.py Outdated
Comment thread scipy/_lib/_array_api.py Outdated
Comment thread scipy/_lib/tests/test_array_api.py Outdated
Comment thread scipy/_lib/_array_api.py Outdated
Comment on lines +298 to +300
if xp is None:
try:
xp = _default_xp_ctxvar.get()
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

just a thought, I wonder whether this is something that could be ported to xpx.testing...

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

For that, we will have to keep xp as a keyword argument inside the xp_assert function in array_api_extra. Could be useful, also:

        try:
            xp = _default_xp_ctxvar.get()

Would help in accessing the namespace if the test is run inside a context manager. Something like

with default_xp(torch):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Right, I think a good first step would be to add an xp=None parameter to the assertions in array-api-extra, then copy across

def _assert_matching_namespace(actual, desired, xp):
__tracebackhide__ = True # Hide traceback for py.test
desired_arr_space = array_namespace(desired)
_msg = ("Namespace of desired array does not match expectations "
"set by the `default_xp` context manager or by the `xp`"
"pytest fixture.\n"
f"Desired array's space: {desired_arr_space.__name__}\n"
f"Expected namespace: {xp.__name__}")
assert desired_arr_space == xp, _msg
actual_arr_space = array_namespace(actual)
_msg = ("Namespace of actual and desired arrays do not match.\n"
f"Actual: {actual_arr_space.__name__}\n"
f"Desired: {xp.__name__}")
assert actual_arr_space == xp, _msg
for use in place of https://github.com/data-apis/array-api-extra/blob/aacc9db0fe2f3aab0c46173da82f315464f65b2b/src/array_api_extra/_lib/_testing.py#L60-L64.

Then perhaps we can see, with the example of scikit-image, whether anything relating to the context manager should also be ported over.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Well, for this code to make sense, we need to have the context manager first. I've opened a PR, lets move this discussion: data-apis/array-api-extra#747

@lucascolley lucascolley changed the title Use array_api_extra's testing functions TST: use xpx.testing assertions May 16, 2026
@lucascolley lucascolley added maintenance Items related to regular maintenance tasks and removed scipy.interpolate scipy.cluster scipy._lib labels May 16, 2026
Signed-off-by: Pradyot Ranjan <[email protected]>
@lucascolley
Copy link
Copy Markdown
Member

The GPU failures demonstrate something we need to address — atol and rtol should either be explicitly rejected if they are arrays (rather than Python scalars), or explicitly accepted and converted either to NumPy arrays or a scalar type. I'm not sure exactly what we'd want yet.

The 'install into venv' failure I think can be addressed when we make the assertions public by ensuring that their module does not depend on pytest.

RuntimeError: Can't call numpy() on Tensor that has conjugate bit set. Use tensor.resolve_conj().numpy() instead.

That's probably something we want to fix over in array-api-extra.

@lucascolley
Copy link
Copy Markdown
Member

The GPU failures demonstrate something we need to address — atol and rtol should either be explicitly rejected if they are arrays (rather than Python scalars), or explicitly accepted and converted either to NumPy arrays or a scalar type. I'm not sure exactly what we'd want yet.

I think we should try explicitly accepting 0-D arrays in array-api-extra for these parameters, but rejecting higher-dimensional arrays. If there are a bunch of failures for 1-D arrays with a single element then maybe it makes sense to accept that also.

@prady0t
Copy link
Copy Markdown
Author

prady0t commented May 16, 2026

or explicitly accepted and converted either to NumPy arrays or a scalar type.

I don't know much about pytorch tensors, but can we do

float(4e-16 * ref.integral)

In the tests to make this work?

Or : https://docs.pytorch.org/docs/2.12/tensors.html

@prady0t
Copy link
Copy Markdown
Author

prady0t commented May 16, 2026

But ofc this won't be a proper fix as we need to address these inputs in the extra module.

@lucascolley
Copy link
Copy Markdown
Member

lucascolley commented May 16, 2026

What I am proposing is something like (in array-api-extra):

actual_np = as_numpy_array(actual, xp=xp)
desired_np = as_numpy_array(desired, xp=xp)

if not isinstance(atol, float):
    atol = as_numpy_array(atol, xp=xp)
    if atol.ndim > 0:
        # raise error

if not isinstance(rtol, float):
    rtol = as_numpy_array(rtol, xp=xp)
    if rtol.ndim > 0:
        # raise error

np.testing.assert_allclose(actual_np, desired_np, atol, rtol, ...)

@prady0t
Copy link
Copy Markdown
Author

prady0t commented May 16, 2026

Makes sense. Let me open a PR in array_api_extra.

@lucascolley
Copy link
Copy Markdown
Member

I think we want to add to https://github.com/data-apis/array-api-extra/blob/main/src/array_api_extra/_lib/_testing.py#L126-L127 array = array.resolve_conj().

Comment thread scipy/_lib/_array_api.py Outdated
@ev-br
Copy link
Copy Markdown
Member

ev-br commented May 17, 2026

Having a single canonical set of assertions across the ecosystem would be very valuable indeed. Now, the scipy versions have had a lot of effort and polishing. They have basically stabilized a while ago and have been shown to work in a non-trivial project (scipy).

Decisions like whether to wrap native asserions vs convert to numpy etc have already been discussed extensively, so what would be a reason to re-discover issues like pytorch conjugation bits #25143 (comment)?

The array-api-extra versions are IIUC adapted from the scipy versions, and simplified to meet internal needs of array-api-strict. Now if this effort is to make the array-api-versions be the versions for the ecosystem, I'd expect that the more polished/full-featured versions just go into array-api-strict, modulo potentially ripping out too specific parts (_0d shenanigans etc, potentially xp_capabilities).

So I'd expect that this is what happens: copy-paste the scipy versions into array-api-strict, and have a clear summary/discussion of where the line is drawn between "scipy-specific" and "generally useful".

Is this not what's going on here?

@prady0t
Copy link
Copy Markdown
Author

prady0t commented May 17, 2026

Is this not what's going on here?

Yes, that's exactly what we are trying to do. I have already discussed a few things, scipy testing functions and the array_api_extras function does differently in the PR discussion. Our current tests match very closely with _strict_checks in Scipy.

@ev-br
Copy link
Copy Markdown
Member

ev-br commented May 17, 2026

Yes, that's exactly what we are trying to do. I have already discussed a few things, scipy testing functions and the array_api_extras function does differently in the PR discussion. Our current tests match very closely with _strict_checks in Scipy.

Then #25143 (comment) shouldn't be even a thing because this was figured in the scipy versions long long time ago.

@lucascolley
Copy link
Copy Markdown
Member

lucascolley commented May 17, 2026

(I assume you mean array-api-extra when you say array-api-strict)

Decisions like whether to wrap native asserions vs convert to numpy etc have already been discussed extensively

I'm not so sure... I have a feeling this is basically a decision I made a few years ago that seemed alright to those involved at the time.

So I'd expect that this is what happens: copy-paste the scipy versions into array-api-strict, and have a clear summary/discussion of where the line is drawn between "scipy-specific" and "generally useful".

Is this not what's going on here?

When @crusaderky was working on adding primitive versions of these assertions for array-api-extra, I think it became clear that wrapping of native assertions was itself a scipy-specific historical quirk that we didn't have good reason to keep around. At the time, when I was getting to grips with the array API standard, I think I was too much in the mindset of 'keep everything native where possible' — now, I don't think such a heuristic applies so simply to the test-suite. I'm happy to take the blame for that, although it was one of the first things I ever worked on 😉.

Unfortunately, there is no clear summary/discussion accurate to the current state, as the development has been dispersed across multiple years. @crusaderky highlighted the main initial differences (aside from the non-use of native assertions) at data-apis/array-api-extra#17 (comment), but since then @mdhaber and @prady0t have worked on adding missing features that SciPy and scikit-learn need.

Is there anything in particular you are worried about being missing / erroneously dropped from the SciPy versions? Perhaps you are worried that we are silently dropping strictness? I'm pretty confident that we haven't missed anything crucial — Guido and Matt spent a significant amount of time on this initially, and the importance of @prady0t's work now is to help demonstrate that in CI. But the purpose of this PR is to figure out precisely if we are missing anything.


If a full write-up of the differences between the SciPy versions and the xpx versions is what is required to get this merged, then we should do that eventually once we have the public API in xpx.testing. I'm convinced, however, that starting again from scratch by copy-pasting over the implementations from SciPy would be a waste of time.

@prady0t
Copy link
Copy Markdown
Author

prady0t commented May 17, 2026

Well, we are trying to always convert to numpy with this PR. Scipy uses torch.testing for torch xp, whereas in array_api_extra we try to convert everything to a numpy array. This is something that sklearn also does for testing.

@lucascolley
Copy link
Copy Markdown
Member

lucascolley commented May 17, 2026

Yes, that's exactly what we are trying to do. I have already discussed a few things, scipy testing functions and the array_api_extras function does differently in the PR discussion. Our current tests match very closely with _strict_checks in Scipy.

Then #25143 (comment) shouldn't be even a thing because this was figured in the scipy versions long long time ago.

That's not true — this isn't a problem that was 'figured out' in SciPy a long time ago. It is a problem that was entirely avoided by the decision to wrap native assertions (which, to be fair, I don't know if you were involved in at all).

If you feel strongly that wrapping native assertions is the way to go, then we could have a new discussion on that (probably in a new array-api-extra issue?). But let's avoid assuming that because something was decided in SciPy years ago, it is the gold-standard against which alternatives must prove their legitimacy. I certainly trust my judgement now more than my judgement back then, at least 😅

@ev-br
Copy link
Copy Markdown
Member

ev-br commented May 17, 2026

I assume you mean array-api-extra when you say array-api-strict)

Indeed a typo, thanks!

(native assertions vs np.testing)

I have a feeling this is basically a decision I made a few years ago

Okay, did not know it was your decision back then. The decision worked quite well (kudos!), and I'm just surprised there's a need to redesign. It certainly ain't broken.
But no, this is very much something I don't want to re-litigate or re-discuss.

Taking a step back, the main feature of assertions is that they are stable and "boring", so that when porting a library or writing a new library, the author can concentrate on the functionality and rely on assertions just doing the right thing. What exactly is the right thing can be open to discussions of course, and we had way too many of those in scipy at least (I'm sure elsewhere too), which is why I thought they just work, and there's no need to rewrite then stabilize them, again.

As a data point, I'm currently working on adding array api support to scikit-image. The first thing I did was to drop the scipy assertions in-tree, with an expectation that they'll make their way to xpx and I'll be able to just replace them by from xpx.testing import ..... Apparently not without changes; unfortunate but well, okay.

So from this perspective, what's most important for porting a new library:

  • strictness and knobs to turn some checks off. Strictness, accepting lists vs array-likes vs arrays only, check_* keywords etc --- this is something a library can and will customize; what's needed downstream is a way to customize the behavior without copy-pasting the whole thing, with just small wrappers on top of xpx assertions.
  • default_xp: this one is large and extremely useful.
  • xp_capabilities should probably best decoupled from core assertions.

@lucascolley
Copy link
Copy Markdown
Member

Taking a step back, the main feature of assertions is that they are stable and "boring", so that when porting a library or writing a new library, the author can concentrate on the functionality and rely on assertions just doing the right thing. What exactly is the right thing can be open to discussions of course, and we had way too many of those in scipy at least (I'm sure elsewhere too), which is why I thought they just work, and there's no need to rewrite then stabilize them, again.

Yeah, part of the motivation for avoiding use of native assertions is for this kind of stability (or at least consistency) — we don't want to have to deal with bugs or nuances of various different xp.testing libraries if we can achieve it all via np.testing anyway.

I think you have a good argument if the question was "should we put in a bunch of thought to re-write these now?" But really, that work has already been done by Guido and Matt, so I think we are right to make use of it (presuming, fairly, that that work is of good quality).

Apparently not without changes; unfortunate but well, okay.

Let's check what changes would be required before we finalise the public API! Would you be willing to try these out in scikit-image and report any difficulties? I already have my eyes on wilsonrljr/sysidentpy#232 and glass-dev/glass#1076 too.

  • default_xp: this one is large and extremely useful.

I think this may be the key remaining area of work.

@prady0t
Copy link
Copy Markdown
Author

prady0t commented May 17, 2026

strictness and knobs to turn some checks off. Strictness, accepting lists vs array-likes vs arrays only, check_* keywords etc --- this is something a library can and will customize; what's needed downstream is a way to customize the behavior without copy-pasting the whole thing, with just small wrappers on top of xpx assertions.

default_xp: this one is large and extremely useful.

xp_capabilities should probably best decoupled from core assertions.

I think these are useful points. Numpy also has a strict parameter; if set to True, it checks for dtypes, scalars, and shape. It could be useful for libraries that want to rely on this parameter. Can we open a tracking issue in array_api_extra for these improvements?

@lucascolley
Copy link
Copy Markdown
Member

Numpy also has a strict parameter; if set to True, it checks for dtypes, scalars, and shape. It could be useful for libraries that want to rely on this parameter.

I would wait until it is explicitly requested, the finer grained parameters we have already are better IMO.

@prady0t
Copy link
Copy Markdown
Author

prady0t commented May 17, 2026

the finer grained parameters we have already are better IMO.

True.

@prady0t
Copy link
Copy Markdown
Author

prady0t commented May 18, 2026

All checks are passing now!

@lucascolley
Copy link
Copy Markdown
Member

nice, I think #25143 (comment) is the next step

Signed-off-by: Pradyot Ranjan <[email protected]>
@prady0t
Copy link
Copy Markdown
Author

prady0t commented May 19, 2026

Only assert_array_almost_equal_nulp remains as a non-array_api_extra testing utility here. Can we port it too? If so, we can remove _strict_check and _assert_matching_namespace helper functions entirely from here.

@lucascolley
Copy link
Copy Markdown
Member

Only assert_array_almost_equal_nulp remains as a non-array_api_extra testing utility here. Can we port it too? If so, we can remove _strict_check and _assert_matching_namespace helper functions entirely from here.

Let's leave it for now, but yes let's return to it when we bring this PR out of draft.

[skip circle]
@lucascolley lucascolley force-pushed the use_array-api-extra-testing branch from 92416b6 to 4e0a864 Compare May 22, 2026 15:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

array types Items related to array API support and input array validation (see gh-18286) maintenance Items related to regular maintenance tasks

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants