Skip to content

Conversation

ilan-gold
Copy link
Contributor

@ilan-gold ilan-gold commented Mar 5, 2024

Reference issue

Towards #18915

What does this implement/fix?

This begins the works towards an array_api compliant implementation. I've take some first steps here just to see what works and what doesn't and document how to do this. Of note is that most of the current errors arise from either indexing problems or lack of 1d support. See also #20128

Additional information

In terms of current failing tests on account of missing names/functions (i.e., not 1D issues), we have a sampling here, although this is probably the majority:

FAILED array_api_tests/test_data_type_functions.py::test_isdtype - AssertionError: isdtype is not defined in scipy.array_api
FAILED array_api_tests/test_has_names.py::test_has_names[linalg-cross] - AssertionError: scipy.array_api is missing the linalg extension function cross()
FAILED array_api_tests/test_has_names.py::test_has_names[linalg-diagonal] - AssertionError: scipy.array_api is missing the linalg extension function diagonal()
FAILED array_api_tests/test_has_names.py::test_has_names[linalg-matmul] - AssertionError: scipy.array_api is missing the linalg extension function matmul()
FAILED array_api_tests/test_has_names.py::test_has_names[linalg-matrix_norm] - AssertionError: scipy.array_api is missing the linalg extension function matrix_norm()
FAILED array_api_tests/test_has_names.py::test_has_names[linalg-matrix_power] - AssertionError: scipy.array_api is missing the linalg extension function matrix_power()
FAILED array_api_tests/test_has_names.py::test_has_names[linalg-matrix_rank] - AssertionError: scipy.array_api is missing the linalg extension function matrix_rank()
FAILED array_api_tests/test_has_names.py::test_has_names[linalg-matrix_transpose] - AssertionError: scipy.array_api is missing the linalg extension function matrix_transpose()
FAILED array_api_tests/test_has_names.py::test_has_names[linalg-outer] - AssertionError: scipy.array_api is missing the linalg extension function outer()
FAILED array_api_tests/test_has_names.py::test_has_names[linalg-slogdet] - AssertionError: scipy.array_api is missing the linalg extension function slogdet()
FAILED array_api_tests/test_has_names.py::test_has_names[linalg-tensordot] - AssertionError: scipy.array_api is missing the linalg extension function tensordot()
FAILED array_api_tests/test_has_names.py::test_has_names[linalg-trace] - AssertionError: scipy.array_api is missing the linalg extension function trace()
FAILED array_api_tests/test_has_names.py::test_has_names[linalg-vecdot] - AssertionError: scipy.array_api is missing the linalg extension function vecdot()
FAILED array_api_tests/test_has_names.py::test_has_names[linalg-vector_norm] - AssertionError: scipy.array_api is missing the linalg extension function vector_norm()
FAILED array_api_tests/test_has_names.py::test_has_names[statistical-prod] - AssertionError: scipy.array_api is missing the statistical function prod()
FAILED array_api_tests/test_has_names.py::test_has_names[statistical-std] - AssertionError: scipy.array_api is missing the statistical function std()
FAILED array_api_tests/test_has_names.py::test_has_names[statistical-var] - AssertionError: scipy.array_api is missing the statistical function var()
FAILED array_api_tests/test_has_names.py::test_has_names[set-unique_all] - AssertionError: scipy.array_api is missing the set function unique_all()
FAILED array_api_tests/test_has_names.py::test_has_names[set-unique_counts] - AssertionError: scipy.array_api is missing the set function unique_counts()
FAILED array_api_tests/test_has_names.py::test_has_names[set-unique_inverse] - AssertionError: scipy.array_api is missing the set function unique_inverse()
FAILED array_api_tests/test_has_names.py::test_has_names[set-unique_values] - AssertionError: scipy.array_api is missing the set function unique_values()
FAILED array_api_tests/test_has_names.py::test_has_names[searching-nonzero] - AssertionError: scipy.array_api is missing the searching function nonzero()
FAILED array_api_tests/test_has_names.py::test_has_names[searching-where] - AssertionError: scipy.array_api is missing the searching function where()
FAILED array_api_tests/test_has_names.py::test_has_names[creation-arange] - AssertionError: scipy.array_api is missing the creation function arange()
FAILED array_api_tests/test_has_names.py::test_has_names[creation-from_dlpack] - AssertionError: scipy.array_api is missing the creation function from_dlpack()
FAILED array_api_tests/test_has_names.py::test_has_names[creation-linspace] - AssertionError: scipy.array_api is missing the creation function linspace()
FAILED array_api_tests/test_has_names.py::test_has_names[creation-meshgrid] - AssertionError: scipy.array_api is missing the creation function meshgrid()
FAILED array_api_tests/test_has_names.py::test_has_names[creation-tril] - AssertionError: scipy.array_api is missing the creation function tril()
FAILED array_api_tests/test_has_names.py::test_has_names[creation-triu] - AssertionError: scipy.array_api is missing the creation function triu()
FAILED array_api_tests/test_has_names.py::test_has_names[utility-any] - AssertionError: scipy.array_api is missing the utility function any()
FAILED array_api_tests/test_has_names.py::test_has_names[manipulation-broadcast_arrays] - AssertionError: scipy.array_api is missing the manipulation function broadcast_arrays()
FAILED array_api_tests/test_has_names.py::test_has_names[manipulation-broadcast_to] - AssertionError: scipy.array_api is missing the manipulation function broadcast_to()
FAILED array_api_tests/test_has_names.py::test_has_names[manipulation-concat] - AssertionError: scipy.array_api is missing the manipulation function concat()
FAILED array_api_tests/test_has_names.py::test_has_names[manipulation-expand_dims] - AssertionError: scipy.array_api is missing the manipulation function expand_dims()
FAILED array_api_tests/test_has_names.py::test_has_names[manipulation-flip] - AssertionError: scipy.array_api is missing the manipulation function flip()
FAILED array_api_tests/test_has_names.py::test_has_names[manipulation-permute_dims] - AssertionError: scipy.array_api is missing the manipulation function permute_dims()
FAILED array_api_tests/test_has_names.py::test_has_names[manipulation-roll] - AssertionError: scipy.array_api is missing the manipulation function roll()
FAILED array_api_tests/test_has_names.py::test_has_names[manipulation-squeeze] - AssertionError: scipy.array_api is missing the manipulation function squeeze()
FAILED array_api_tests/test_has_names.py::test_has_names[manipulation-stack] - AssertionError: scipy.array_api is missing the manipulation function stack()
FAILED array_api_tests/test_has_names.py::test_has_names[sorting-argsort] - AssertionError: scipy.array_api is missing the sorting function argsort()
FAILED array_api_tests/test_has_names.py::test_has_names[sorting-sort] - AssertionError: scipy.array_api is missing the sorting function sort()
FAILED array_api_tests/test_has_names.py::test_has_names[data_type-astype] - AssertionError: scipy.array_api is missing the data_type function astype()
FAILED array_api_tests/test_has_names.py::test_has_names[data_type-isdtype] - AssertionError: scipy.array_api is missing the data_type function isdtype()
FAILED array_api_tests/test_has_names.py::test_has_names[elementwise-bitwise_and] - AssertionError: scipy.array_api is missing the elementwise function bitwise_and()
FAILED array_api_tests/test_has_names.py::test_has_names[elementwise-bitwise_left_shift] - AssertionError: scipy.array_api is missing the elementwise function bitwise_left_shift()
FAILED array_api_tests/test_has_names.py::test_has_names[elementwise-bitwise_invert] - AssertionError: scipy.array_api is missing the elementwise function bitwise_invert()
FAILED array_api_tests/test_has_names.py::test_has_names[elementwise-bitwise_or] - AssertionError: scipy.array_api is missing the elementwise function bitwise_or()
FAILED array_api_tests/test_has_names.py::test_has_names[elementwise-bitwise_right_shift] - AssertionError: scipy.array_api is missing the elementwise function bitwise_right_shift()
FAILED array_api_tests/test_has_names.py::test_has_names[elementwise-bitwise_xor] - AssertionError: scipy.array_api is missing the elementwise function bitwise_xor()
FAILED array_api_tests/test_has_names.py::test_has_names[elementwise-floor_divide] - AssertionError: scipy.array_api is missing the elementwise function floor_divide()
FAILED array_api_tests/test_has_names.py::test_has_names[elementwise-greater] - AssertionError: scipy.array_api is missing the elementwise function greater()
FAILED array_api_tests/test_has_names.py::test_has_names[elementwise-greater_equal] - AssertionError: scipy.array_api is missing the elementwise function greater_equal()
FAILED array_api_tests/test_has_names.py::test_has_names[elementwise-less] - AssertionError: scipy.array_api is missing the elementwise function less()
FAILED array_api_tests/test_has_names.py::test_has_names[elementwise-less_equal] - AssertionError: scipy.array_api is missing the elementwise function less_equal()
FAILED array_api_tests/test_has_names.py::test_has_names[elementwise-logical_and] - AssertionError: scipy.array_api is missing the elementwise function logical_and()
FAILED array_api_tests/test_has_names.py::test_has_names[elementwise-logical_not] - AssertionError: scipy.array_api is missing the elementwise function logical_not()
FAILED array_api_tests/test_has_names.py::test_has_names[elementwise-logical_or] - AssertionError: scipy.array_api is missing the elementwise function logical_or()
FAILED array_api_tests/test_has_names.py::test_has_names[elementwise-logical_xor] - AssertionError: scipy.array_api is missing the elementwise function logical_xor()
FAILED array_api_tests/test_has_names.py::test_has_names[elementwise-multiply] - AssertionError: scipy.array_api is missing the elementwise function multiply()
FAILED array_api_tests/test_has_names.py::test_has_names[elementwise-not_equal] - AssertionError: scipy.array_api is missing the elementwise function not_equal()
FAILED array_api_tests/test_has_names.py::test_has_names[elementwise-subtract] - AssertionError: scipy.array_api is missing the elementwise function subtract()
FAILED array_api_tests/test_has_names.py::test_has_names[linear_algebra-matmul] - AssertionError: scipy.array_api is missing the linear_algebra function matmul()
FAILED array_api_tests/test_has_names.py::test_has_names[linear_algebra-matrix_transpose] - AssertionError: scipy.array_api is missing the linear_algebra function matrix_transpose()
FAILED array_api_tests/test_has_names.py::test_has_names[linear_algebra-tensordot] - AssertionError: scipy.array_api is missing the linear_algebra function tensordot()
FAILED array_api_tests/test_has_names.py::test_has_names[linear_algebra-vecdot] - AssertionError: scipy.array_api is missing the linear_algebra function vecdot()
FAILED array_api_tests/test_has_names.py::test_has_names[array_method-__and__] - AssertionError: The scipy.array_api array object is missing the method __and__()
FAILED array_api_tests/test_has_names.py::test_has_names[array_method-__complex__] - AssertionError: The scipy.array_api array object is missing the method __complex__()
FAILED array_api_tests/test_has_names.py::test_has_names[array_method-__dlpack__] - AssertionError: The scipy.array_api array object is missing the method __dlpack__()
FAILED array_api_tests/test_has_names.py::test_has_names[array_method-__dlpack_device__] - AssertionError: The scipy.array_api array object is missing the method __dlpack_device__()
FAILED array_api_tests/test_has_names.py::test_has_names[array_method-__float__] - AssertionError: The scipy.array_api array object is missing the method __float__()
FAILED array_api_tests/test_has_names.py::test_has_names[array_method-__floordiv__] - AssertionError: The scipy.array_api array object is missing the method __floordiv__()
FAILED array_api_tests/test_has_names.py::test_has_names[array_method-__index__] - AssertionError: The scipy.array_api array object is missing the method __index__()
FAILED array_api_tests/test_has_names.py::test_has_names[array_method-__int__] - AssertionError: The scipy.array_api array object is missing the method __int__()
FAILED array_api_tests/test_has_names.py::test_has_names[array_method-__invert__] - AssertionError: The scipy.array_api array object is missing the method __invert__()
FAILED array_api_tests/test_has_names.py::test_has_names[array_method-__lshift__] - AssertionError: The scipy.array_api array object is missing the method __lshift__()
FAILED array_api_tests/test_has_names.py::test_has_names[array_method-__mod__] - AssertionError: The scipy.array_api array object is missing the method __mod__()
FAILED array_api_tests/test_has_names.py::test_has_names[array_method-__or__] - AssertionError: The scipy.array_api array object is missing the method __or__()
FAILED array_api_tests/test_has_names.py::test_has_names[array_method-__pos__] - AssertionError: The scipy.array_api array object is missing the method __pos__()
FAILED array_api_tests/test_has_names.py::test_has_names[array_method-__rshift__] - AssertionError: The scipy.array_api array object is missing the method __rshift__()
FAILED array_api_tests/test_has_names.py::test_has_names[array_method-__xor__] - AssertionError: The scipy.array_api array object is missing the method __xor__()
FAILED array_api_tests/test_has_names.py::test_has_names[array_method-to_device] - AssertionError: The scipy.array_api array object is missing the method to_device()
FAILED array_api_tests/test_has_names.py::test_has_names[array_attribute-device] - AssertionError: The scipy.array_api array object is missing the attribute device
FAILED array_api_tests/test_has_names.py::test_has_names[array_attribute-mT] - AssertionError: The scipy.array_api array object is missing the attribute mT

At the end of the day, I'm not sure if this is worth doing or not. I will remember to come to the working group to discuss in two weeks! That being said, a lot of the missing functions probably could be implemented and this might serve as a nice roadmap for new features that would make QOL better for many. For example, one of the blockers for dask integration I found was the keepdims arg being passed through to csr_matrix.sum but if we could rely on the array api implementation here, we could probably ignore it or handle it better with the advent of 1D arrays.

@ilan-gold ilan-gold changed the title (feat): first pass at array_api (feat): first pass at array_api compat Mar 5, 2024
@github-actions github-actions bot added scipy.sparse Meson Items related to the introduction of Meson as the new build system for SciPy labels Mar 5, 2024
@ilan-gold
Copy link
Contributor Author

I will continue working on this at some point but just wanted to post here since @ivirshup wanted to see as well. There are also a few blockers like 1D and #20128. Sorry for the spam, didn't know it would auto-request a review!

@ilan-gold
Copy link
Contributor Author

A first "fix" would probably be moving the folder under sparse?

@lucascolley
Copy link
Member

lucascolley commented Mar 5, 2024

A first "fix" would probably be moving the folder under sparse?

Indeed 👍. SciPy is largely an array-consumer library, so having this at the top-level would be very confusing. Keeping this as close to the array-producer part of sparse as possible would be good.

@lucascolley lucascolley added enhancement A new feature or improvement array types Items related to array API support and input array validation (see gh-18286) and removed Meson Items related to the introduction of Meson as the new build system for SciPy labels Mar 5, 2024
@ivirshup
Copy link
Contributor

ivirshup commented Mar 5, 2024

@willow-ahrens, I believe you'd mention that you were working on getting finch-tensor passing the array-api test suite as well. Is that work publicly available anywhere? It would be nice to see what you've done and compare what tests are being skipped.

@willow-ahrens
Copy link

We've been building it array api-compliant, but haven't begun testing with the Array API suite. I'll try to remember to update when we get to the test suite.

@ilan-gold
Copy link
Contributor Author

ilan-gold commented Mar 5, 2024

Definitely interested @willow-ahrens - I left out operations that don't already have standard implementations. It seems like certain operations like logical_or (one such op) have simple implementations but it seems out of scope to do that here. I think its output could definitely still be "sparse enough" to warrant being a sparse operation (after all, you probably shouldn't be using sparse unless your data is truly sparse and in that sense an operation on two sparse things should just give sparse as it does with addition).

@j-bowhay j-bowhay changed the title (feat): first pass at array_api compat ENH: sparse: first pass at array_api compat Mar 5, 2024
@ilan-gold
Copy link
Contributor Author

ilan-gold commented Mar 6, 2024

@lucascolley tried to add CI but I don't think it'll run without approval? Not sure it's worth really delving into this ATM unless we really think it'll help move things along, especially given the status of 1D arrays. I also definitely don't think non-trivial implementations should go here since array-api-tests do not test for correctness (and even for things like shape and dtype correctness as properties of the output, they are often marked for failure).

@lucascolley
Copy link
Member

sounds good. As I said, no rush on the CI check. FYI, we are able to skip certain CI workflows with tags in commit messages, see http://scipy.github.io/devdocs/dev/contributor/continuous_integration.html#skipping. That might be useful here, e.g. if you don't need to build the docs and would only like GitHub Actions to run.

@ilan-gold
Copy link
Contributor Author

maybe a next step tomorrow, thanks @lucascolley :)

@ilan-gold
Copy link
Contributor Author

ilan-gold commented Mar 8, 2024

First up: https://data-apis.org/array-api/latest/API_specification/generated/array_api.where.html#where

This appears to be necessary for solving at least part of dask/dask#10980 (comment)

Interestingly, the array api version does not actually cover all the use-cases I'd be interested in (or rather the ones where numpy currently has coverage):

from numpy import array_api as npa
import numpy as np
X = np.random.randn(20, 20)
npa.where(X>0, 10, X) # errors out because 10 is not an array as specified

Copy link
Member

@rgommers rgommers left a comment

Choose a reason for hiding this comment

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

Interesting @ilan-gold, thanks for sharing.

First a comment on the module structure: putting this into scipy/sparse/_array_api/ (private) would avoid exposing a lot of things publicly. At least at the start, using it via __array_namespace__ would make sense. And it's possible (desirable?) that it could live directly in sparse in the future - like in numpy 2.0

npa.where(X>0, 10, X) # errors out because 10 is not an array as specified

Allowing Python scalars is a minor convenience thing that is numpy-specific. Using where(X>0, asarray(10), X) works.

@ilan-gold
Copy link
Contributor Author

where(X>0, asarray(10), X)

Oh very cool!!!

@ilan-gold
Copy link
Contributor Author

CI now running under Linux tests / sparse-array-api (3.10) (pull_request)

@lucascolley lucascolley added the CI Items related to the CI tools such as CircleCI, GitHub Actions or Azure label Aug 7, 2024
@lucascolley
Copy link
Member

thanks for getting this set up @ilan-gold ! I just pushed a few commits to get the CI job over the line and we're now at:

=== 405 failed, 507 passed, 57 skipped, 5305 warnings in 2949.75s (0:49:09) ====

so still a long way to go, but nice to be able to see what is missing! Hopefully #21197 (comment) will help us make some further progress!

@ilan-gold
Copy link
Contributor Author

Thanks @lucascolley !!

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) CI Items related to the CI tools such as CircleCI, GitHub Actions or Azure enhancement A new feature or improvement scipy.sparse
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants