Repository navigation
Conversation
| # MoorePenrose Pseudo-Inverse, otherwise use ridge regularized form. | ||
| if self.reg_alpha is None: | ||
| self.coeff_ = np.linalg.pinv(D, rtol=self.rtol) @ Y | ||
| self.coeff_ = xp.linalg.pinv(D, rtol=self.rtol) @ Y |
There was a problem hiding this comment.
This particular invocation has issues with integer arrays. I believe xp.linalg.pinv in some or all namespaces is only implemented for svd, which has an finfo() invocation. The finfo() invocation fails when given an integer array.
There was a problem hiding this comment.
It would be helpful to refer to the standard itself when discussing issues. For example, if we look at: https://data-apis.org/array-api/latest/extensions/generated/array_api.linalg.pinv.html#pinv
It indicates:
x (array) – input array having shape (..., M, N) and whose innermost two dimensions form MxN matrices. Should have a floating-point data type.
So, yes, the input must be of a floating point dtype. git grep -E -i "xp.linalg.pinv" does show some example usages that might be helpful inside the sklearn source. They do seem to occasionally diverge when NumPy is used vs. not.
There was a problem hiding this comment.
There is also a related issue in the regularized path. With solver="auto", Ridge selects Cholesky for NumPy input but SVD for Array API input, which makes equivalence testing difficult.
See my modified tests (based heavily on https://github.com/scikit-learn/scikit-learn/blob/main/sklearn/linear_model/tests/test_logistic.py#L2858) below the fold:
Details
@pytest.mark.parametrize(
"reg_alpha, n_features, hidden_layer_sizes",
[
(1e-1, 40, (100,)),
(1e-1, 400, (100,)),
(2, 40, (100,)),
(2, 400, (100,)),
(None, 40, (100,)),
(None, 400, (100,)),
(1e-1, 40, (100, 100,)),
(1e-1, 400, (100, 100,)),
(2, 40, (100, 100,)),
(2, 400, (100, 100,)),
(None, 40, (100, 100,)),
(None, 400, (100, 100,)),
]
)
@pytest.mark.parametrize(
"array_namespace, device_name, dtype_name",
yield_namespace_device_dtype_combinations(),
)
def test_gfdl_array_api_compliance(
reg_alpha,
n_features,
hidden_layer_sizes,
array_namespace,
device_name,
dtype_name,
):
xp, device = _array_api_for_tests(array_namespace, device_name, dtype_name)
X_np, y_np = make_regression(
n_samples=10_000,
n_features=n_features,
n_informative=int(n_features / 10),
n_targets=1,
random_state=0,
)
y_np /= np.std(y_np) # normalize the regression targets
X_np = X_np.astype(dtype_name, copy=False)
y_np = y_np.astype(dtype_name, copy=False)
X_xp = xp.asarray(X_np, device=device)
y_xp_or_np = np.asarray(y_np, copy=True)
params = dict(reg_alpha = reg_alpha, hidden_layer_sizes = hidden_layer_sizes, seed=0)
with config_context(array_api_dispatch=False):
gfdl_np = GFDLRegressor(**params).fit(
X_np, y_np
)
prediction_np = gfdl_np.predict(X_np)
atol = _atol_for_type(dtype_name)*4
rtol = 1e-4 if dtype_name == "float32" else 1e-8
with config_context(array_api_dispatch=True):
gfdl_xp = GFDLRegressor(**params).fit(
X_xp, y_xp_or_np
)
attr_xp = getattr(gfdl_xp, "coeff_")
attr_np = getattr(gfdl_np, "coeff_")
assert_allclose(
move_to(attr_xp, xp=np, device="cpu"), attr_np, rtol=rtol, atol=atol
)
assert attr_xp.dtype == X_xp.dtype
assert array_device(attr_xp) == array_device(X_xp)
prediction_xp = gfdl_xp.predict(X_xp)
prediction_xp = move_to(prediction_xp, xp=np, device="cpu")
assert_allclose(prediction_xp, prediction_np, rtol=rtol, atol=atol)UserWarning: Using Array API dispatch to namespace array_api_strict with `solver='auto'` will result in using the solver 'svd'. The results may differ from those when using a Numpy array, because in that case the preferred solver would be cholesky. Set `solver='svd'` to suppress this warning.
There was a problem hiding this comment.
Emma and I talked about this in-person. We are both comfortable setting the solver as svd. On their page, sklearn explicitly says that svd is the most stable solver, especially for nearly singular matrices. I think svd may be the default (what auto would get you) for many namespaces, but numpy uses cholesky. We have had some instances of concern for very large singular matrices, so stability could be a good call. However, the tradeoff is speed.
https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.Ridge.html
In scope, I believe, is to do a small speed benchmark on svd versus cholesky.
There was a problem hiding this comment.
I did do a small speed test for numpy (with numbers similar to #129 (comment))
The speed difference is negligible.
I would thus propose we accept the change to use svd:
- No major speed difference
- More stable, especially for different backends doing matrix inversion on large matrices
There was a problem hiding this comment.
And we convert integer arrays to float arrays elsewhere in the code.
This comment could probably be reviewed and resolved?
|
The tests (both old and new) worked on the two clusters for me as well (linux-based). |
tylerjereddy
left a comment
There was a problem hiding this comment.
I added some initial feedback--I'll expect that a lot of back and forth should happen between Emma/you/Ankan here before I'm pinged for a "real" review, but I've given you some initial feedback. The overarching message of my feedback is to avoid reinventing the wheel and match what sklearn does, falling back to what i.e., SciPy does if they are missing something.
As mentioned in person, arguably the most important component of the assigned task, as pasted below, is to determine how this is all done upstream in sklearn (preferably, since that's our target for Emma's PR) and maybe SciPy (if sklearn is missing a utility for now), so that we don't have to spend hours rehashing battle tested ways to support and test the array API from upstream.
... the source code of sklearn for how they write these tests/run them
in CI, etc.
Typically, we shouldn't really need many new tests--it should often suffice to just be able to add some decorators to existing tests to parametrize the namespaces used for their checks, and to adjust to assertions that enforce namespace/dtype preservation. That's probably a lot easier to read/review as well--take the existing tests and add a few decorators to them vs. an entire new category of tests.
| "scikit-learn>=1.5.0", | ||
| "scipy>=1.13.0", | ||
| "array-api-compat", | ||
| "torch>=2.12.0,<=2.14.0", |
There was a problem hiding this comment.
I don't think any upstream example projects that I know of add these as runtime dependencies in this way. Probably best to remove and install them in the ways that match the approaches used upstream (i.e., sklearn/scipy, etc.). array-api-compat might be "ok" to leave, since we're a small project.
We can't officially turn the functionality on anyway until SciPy/sklearn turn their array API functionality on, so some manual installing on the part of the user is likely reasonable for now, but torch is something the user would just normally have to install on their own if they want to use it. And the torch binary is massive so pulling that in by default would be wasteful except in CI jobs that genuinely need it. Perhaps torch as a dependency group entry for developers for now. SciPy does have the test-array-types dependency group (https://github.com/scipy/scipy/blob/main/pyproject.toml#L131)--maybe we could use something similar for now.
I also know that SciPy uses git submodules for some of this:
e1d4eed1389f1d93318ec855730488db48475320 subprojects/array_api_compat (1.14-36-ge1d4eed)
dc9e6b59061d8a116f90dcaf83c6d1878496e5ae subprojects/array_api_extra (v0.11.1)
we could consider something like that I suppose. Not sure what is best for us yet--we're a bit smaller so may have less constraints, but still pulling in torch automatically seem undesirable. array-api-compat may be "ok" to leave as a normal dep perhaps, since we're "small."
There was a problem hiding this comment.
Looks like sklearn vendors some of the deps -- see their infrastructure at: sklearn/externals/array_api_compat. That's probably because adding dependencies is prohibitively expensive for them/their users. We probably don't want to do that, but we may want to do one thing that is similar--pinning our version of that lib and only updating the compatibility layer as needed, because when the array API standard changes, things may change/start to fail, so we probably want to be explicit about changes that adopt newer versions of the array API standard.
There was a problem hiding this comment.
I have been able to lean on the existing sklearn utilities to achieve what the private utilities I wrote in the initial commit did. Following these changes, I do not even use array_api_compat anymore, as the utility functions in sklearn depend on their vendoring sklearn/externals/array_api_compat.
We probably don't want to do that, but we may want to do one thing that is similar--pinning our version of that lib and only updating the compatibility layer as needed, because when the array API standard changes, things may change/start to fail, so we probably want to be explicit about changes that adopt newer versions of the array API standard
I am now conflicted between using the sklearn utilities versus using the non-vendored array_api_compat. Aren't these ideas in conflict with each other?
There was a problem hiding this comment.
There are indeed some messy problems here, since depending on private sklearn utilities (usually "private" means names/modules/namespaces starting with underscores, like _name, not made for public consumption) that may disappear at any time is an inherently bad idea because of the risk of breaking.
For now, this WIP PR is a template that Emma might use to transplant over to her PR and help us get merged in to sklearn proper, and there that risk goes away because they can't remove the private utilities without also fixing the accompanying sources that depend on them in sklearn proper.
To move forward here, this may be one of the last decisions we make on the GFDL side before merging. If Emma's PR gets merged first, then the pressure here to leverage sklearn utilities drops quite a bit, but I didn't want two people pursuing different approaches to the same goal, at least not yet, because then the synergy of effort is lost. If her PR does get merged first though, then we can probably do less painful things here. For example, we'd have the option of using array_api_compact directly in our library/source code so it doesn't break for consumers, but we could still keep the vulnerability of using sklearn private utils in testing code--since user/consumer breaks that only affect the testing code are far less serious.
But maybe for now make this PR as useful as possible for Emma's transplantation of array API capabilities is the right target to achieve the goal of getting the sklearn PR merged, which would be huge.
There was a problem hiding this comment.
We could also try "dumping" the necessary private utilities into array-api-extra as noted at #129 (comment) two weeks ago. The community might appreciate that since others will have this problem too, but would need to determine how much work that would be. Then we wouldn't have any "private" deps and yet we'd still use an approach that is basically harmonious with sklearn/what Emma needs.
There was a problem hiding this comment.
So these are the core private utilities that we use:
from sklearn.utils._array_api import (
_max_precision_float_dtype,
check_same_namespace,
get_namespace_and_device,
move_to,
)I suspect these would be generally useful in something like array-api-extra.
There was a problem hiding this comment.
@eiviani-lanl In that case, I think the current status of this PR that exclusively uses the sklearn private utilities
from sklearn.utils._array_api import (
_max_precision_float_dtype,
check_same_namespace,
get_namespace_and_device,
move_to,
)and the testing utilities
from sklearn.utils._testing import (
skip_if_array_api_compat_not_configured,
)
from sklearn.utils.estimator_checks import (
check_array_api_input_and_values, ### THIS ONE IS THE MOST IMPORTANT
parametrize_with_checks,
)would be the best approach to take for your sklearn PR. As Tyler mentioned, they'd have to fix many downstream issues if removing those sklearn.utils._array_api functions, and those are used all over the place.
| def fit(self, X, Y): | ||
| xp = array_api_compat.get_namespace(X) | ||
| X = _ensure_float_X(xp, X) | ||
| Y = _check_and_convert_array(X, Y) |
There was a problem hiding this comment.
Have you actually checked how sklearn does all this stuff? That was the main task assigned really--avoid reinventing the wheel and adopt their approach, especially for simplification of the process of transplanting over to Emma's sklearn PR. Looking through their PR list with a search for "array API" one can learn quite a lot about how they are doing things.
If I look at their LogisticRegression class for example, the fit() method uses this sequence:
xp, _, device = get_namespace_and_device(X)
sample_weight = move_to(sample_weight, xp=xp, device=device)
xp_y, _ = get_namespace(y)see: https://github.com/scikit-learn/scikit-learn/blob/main/sklearn/linear_model/_logistic.py#L1492-L1494
I can't think of a reason to "roll our own" approach when a large team of folks, including people on the actual standards committee, have vetted upstream approaches.
There was a problem hiding this comment.
I think this can be resolved. We are using sklearn private utilities now.
| # MoorePenrose Pseudo-Inverse, otherwise use ridge regularized form. | ||
| if self.reg_alpha is None: | ||
| self.coeff_ = np.linalg.pinv(D, rtol=self.rtol) @ Y | ||
| self.coeff_ = xp.linalg.pinv(D, rtol=self.rtol) @ Y |
There was a problem hiding this comment.
It would be helpful to refer to the standard itself when discussing issues. For example, if we look at: https://data-apis.org/array-api/latest/extensions/generated/array_api.linalg.pinv.html#pinv
It indicates:
x (array) – input array having shape (..., M, N) and whose innermost two dimensions form MxN matrices. Should have a floating-point data type.
So, yes, the input must be of a floating point dtype. git grep -E -i "xp.linalg.pinv" does show some example usages that might be helpful inside the sklearn source. They do seem to occasionally diverge when NumPy is used vs. not.
|
|
||
| def predict(self, X): | ||
| check_is_fitted(self) | ||
| xp = array_api_compat.get_namespace(X) |
There was a problem hiding this comment.
Here again, check sklearn source may be helpful.
The predict_proba of their LogisticRegression uses check_same_namespace(X, self, attribute="coef_", method="predict_proba") for example.
There was a problem hiding this comment.
I am now using check_same_namespace
There was a problem hiding this comment.
I think this old comment can be resolved.
| ) | ||
| if predict_dtype != y_pred.dtype: | ||
| raise ValueError( | ||
| ".predict output and input are not the same dtype" |
There was a problem hiding this comment.
we don't typically raise ValueError in tests; a lot of this looks kind of weird--you'd typically just do a plain assertion, but actually dtype passthrough/preservation is already built-in to upstream utilities so we really shouldn't spend much time reviewing or discussing things that have already been battle tested upstream for array API testing/support
There was a problem hiding this comment.
This custom test has been removed in favor of sklearn utilities
There was a problem hiding this comment.
I think this old comment can be resolved.
| random_state=42, | ||
| ) | ||
| X = namespace.asarray(X, dtype=X_dtype, device=device) | ||
| y = namespace.asarray(y, dtype=y_dtype, device=device) |
There was a problem hiding this comment.
note that CuPy asarray() doesn't suport device=; this stuff has all been thought through upstream though.. see my other comments
There was a problem hiding this comment.
I removed this test in favor of sklearn tests.
There was a problem hiding this comment.
I think this old comment can be resolved. That custom test was removed, and we use xp now instead of custom imports of namespaces.
| y_dtype, | ||
| ): | ||
| """Fitted attributes shall be same as design matrix""" | ||
| os.environ["SCIPY_ARRAY_API"] = "1" |
There was a problem hiding this comment.
This is obviously not correct--it would bleed into other tests in our testsuite, which we don't want.
We'll need a way to turn this on/off globally for the testsuite, along with a way to select which array backends we want to test at any given time. The user/developer/CI testing could just set this and other flags externally depending on what they are trying to test.
This stuff has been hashed out upstream, so let's not reinvent the wheel.
There was a problem hiding this comment.
The toggle I am working with is
SCIPY_ARRAY_API=1 python -m pytestThis seems to be the way that sklearn performs some of their testing:
@pytest.mark.skipif(
os.environ.get("SCIPY_ARRAY_API") != "1", reason="SCIPY_ARRAY_API not set to 1."
)I am implementing that decorator in the array API-related tests in alignment with the sklearn approach.
There was a problem hiding this comment.
I think this old comment can be resolved. We removed
os.environ["SCIPY_ARRAY_API"] = "1"from code in the tests.
|
Note also that many of the tensions here with respect to gracefully extracting the In particular, here: https://hackmd.io/zn5bvdZTQIeJmb3RW1B-8g#Extracting-common-testing-infrastructure-Lucas
|
|
@sdtemple In my own testing it's occurred to me that we'll need to make all activation functions namespace aware, i.e. --- a/src/gfdl/activations.py
+++ b/src/gfdl/activations.py
@@ -20,7 +20,9 @@ def relu(z):
numpy.ndarray
The output array with only positive values.
"""
- return np.maximum(0, z)
+ xp, _, device = get_namespace_and_device(z)
+ zero = xp.asarray(0, dtype=z.dtype, device=device)
+ return xp.maximum(z, zero) |
|
Thank you for answering that item from the checklist items! I'll be sure to make that change. I know we could also do array API support for weight generation, but those can be converted anyway by move_to. So, for that, it'd be more about speed testing. |
Ignore this comment. It looks like the community discussed array API standards for random number generation at length and couldn't come to a consensus on implementation because of irreconcilable differences between packages like NumPy versus those like JAX. |
|
Yeah there is https://github.com/NeilGirdhar/array-api-rng, probably not going to be solved anytime soon. Best bet is, as usual, to look what |
c398a0f to
6f0a8f6
Compare
|
@sdtemple had asked in person how we can check which SciPy functions have array API support. Taking one of the ones we use for activation as an example, Many functions like that one will have an array API standard support table/summary, which is quite nice. I'll reproduce it below. An overview of array API support coverage across the entire library is available in a larger table at https://scipy.github.io/devdocs/dev/api-dev/array_api.html#api-coverage.
|
- xp.maximum requires both to be Tensor - 0 as scalar only works for np - numpy not needed in activations.py - use sklearn way to get namespace
- Control the dtypes with `xp.astype` - `xp.astype` is used >200 times in sklearn - `move_to` is used over >200 times in sklearn - I.e., sklearn learn uses these for device, dtype - `conftest.py` sets global behavior for array API fixture - sklearn tag tells check estimator that array API is supported - All `test_regression.py` functions pass on local Mac OS - Custom tests are yet to be removed - Seeking alternative existing tests from sklearn to replace
* Added `check_array_api_input_and_values` function. This is a public utility that is not covered by `check(estimator)` and will verify if public attributes match to NumPy runs given flexible choice of array namespace. * I checked many combinations over `GFDLRegressor` inputs except for Torch and NumPy seem to handle backends of bounded weight schemes with slight numerical differences. * `check_array_api_input_and_values` does not have enough flexibility like `rtol` or `atol` to control slight backend differences * Modified `test_sklearn_api_conformance` to check both array API and non-array API version. Made a pytest.xfail for array API when _NotAnArray is put in. This is an sklearn specific object that is incompatible with the array API tests. * Two of my custom tests were already covered by `test_sklearn_api_conformance` with the `array_api_dispatch=True` flag * sklearn.linear_model favors `check_same_namespace` as function to ensure `.predict` input matches the `.fit` input * I liked the idea of converting to what the user puts into `.predict` but I will revert to the standard that sklearn has set * This removes `move_to` instances in `GFDL().predict` * For the time being, I commented out a custom test about integer arrays. sklearn does not address this case.
* Removed conftest.py file which globally set some array API settings * Reverted back to original check_estimator test * Made separate check_estimator test for array API Includes skips if array API not set up * Use pytest.importorskip for "torch" test * array_api_strict bawks at hidden_layer_sizes.min() * Removed array_api_compat dependency
* `check_array_api_input_and_values` uses `make_classification` which is unstable for some weight schemes between torch, np * Shrunk number of cases for that test which calls `check_array_api_input_and_values` * Added custom `make_regression`-based test that checks public attributes and predictions
* Applied to `hidden_layer_sizes`
* Implemented argument array_namespace instead of torch only * Used `clone()` in custom test to cut redundancy. This is what `sklearn` does in the related test `_check_array_api_core`. * Removed the 3x for loop of different random states. * The custom `xp_make_regression` test remains but ideally it could be removed if we could get an MR change through `sklearn` that allows for control of the simulating data.
* In `test_xp_make_regression` we need to use `with config_context()` otherwise `sklearn` utilities revert to NumPy. This was a bug. * No handling of dtype for the time being. All tests are xp.float64. * Needed to control `atol` and `rtol` * `move_to` to `assert_allclose` on same namespace (NumPy) * Small code change to use the array_api_compat vendor of the other namespaces (PyTorch)
* `check_array_api_input` only
* Values are evaluated with custom test that is
based on `check_array_api_core` but
gives control over simulating data,
`atol` and `rtol`
* Removed `pytest.importorskip("torch")`
* Instead, use the utility from sklearn
`yield_namespace_device_dtype_combinations()`
* `if` block to scope to torch, cpu, float64
* Don't force to max dtype precision
* All `WEIGHTS.keys()` and `ACTIVATIONS.keys()`
* AI disclosure: I consulted AI but wrote the small edits myself * ridge.coef_.T not strict compliant for 1-d arrays. Revised to not call .T if the array is 1-d * Revert from xp.asarray to np.asarray for hidden_layer_sizes. GaussianNB and others in sklearn have precedent for not all arrays being in xp. This just loops over layer sizes. In experimentation, I find that if this is xp.asarray it creates issues for weight sampling in cupy. No diff from main. * Shortened and revised some comments * Shrunk num of pytest marks by 2 * Added dtype_name to sklearn utility invocation
* AI disclosure: I did not use AI for this * Skip on `check_array_api` for classifier tests * We will remove those skips once we address the classifier in PR lanl#130
* SI disclosure: I did not use SI for this. This is my first set up of a CI, so hopefully it works smoothly. * Added new YML file with SCIPY_ARRAY_API=1 as the environment file * Only macos and ubuntu and python 3.14 * Only torch and array-api-strict installed
* Replace SciPy version of `log_sigmoid` and `sigmoid` with an `xp` version * `log_sigmoid` and `sigmoid` fail some array-api-strict regression tests. It is known from SciPy that `log_expit` is not yet supported. * Update `atol` and `rtol` values for `float32` tests. This is a pain identifying `atol` and `rtol` values suitable for 1e4 tests across platforms, backends, devices, and dtypes. The vast majority of tests pass with stricter than `atol=1e-0` and `rtol=1e-2` for `float32`. For tests that wouldn't pass a stricter threshold, I have verified that the coeffs are qualitatively the same (often a big coefficient 1e2 that may be off by a little over 1e-1). You can also inspect the singular values (manually) to see that they are also qualitatively the same.
* SI disclosure: I did not use SI * Implemented `xp` for base estimator's `partial_fit` method * Be wary of checking namespace without running `.fit()`, hence why you do not put this in `partial_fit` * First draft at adding `partial_fit` boolean to the custom test function * `xp.linalg.lstsq` does SVD as in `Ridge().fit()` but is not supported currently
* SI disclosure: I did not use SI * Replaced `scipy.special` versions of softmax functions with `xp` versions based on Emma's `sklearn` pull request * Updated `atol` and `rtol` bounds for tighter control in `xp_make_regression` test * Added quantitative comments about why we can't tighten further without failing the test (very few cases out of 720 tests over configurations) * We expect some classifier tests to fail, as the classifier will be addressed in lanl#130 which is a descendant of this PR. The vast majority that fail right now concern `partial_fit` in the `GFDLClassifier`
* Removed the `_NotAnArray` try/except block. The `sklearn` compliance function already handles which tests to run if SCIPY_ARRAY_API=1 * Removed the related test
* SI disclosure: I did not use SI, except I did use GPT 6.1 Sol when reviewing Emma's work * There were some Array API styles that Emma used in her sklearn PR that I preferred over mine. I also identified some redundant tests in my sklearn PR review. Here I am porting over those suggested changes. * eiviani-lanl/scikit-learn#2 * Use `xp.asarray` instead of `move_to` and `xp.astype` * Use `dtype=[xp.float64, xp.float32]` in `validate_data` * Only `xp.squeeze` when needed. Fixes a bug with classifier which will go upstream when I rebase lanl#130 * Removed redundant tests which were identified during review
* SI disclosure: I used SI to write a simple `partial_fit` test function * Replaced `numpy.testing.assert_allclose` with `xpx.testing.assert_close` for array API tests * Decreased number of Array API test cases
* SI disclosure: I did not use SI in this commit * Copy and pasted the `xp.asarray` set up for the `self.W_` and `self.b_` initialization to `partial_fit` * Simplified some lines to decrease line number
1e84cdf to
cc2847b
Compare
* SI disclosure: I consulted Gemini for the one line change to the CI YML file * Use -k "not (cuda or xpu or mps)"
|
@tylerjereddy This PR is ready for review. I don't know if it would make sense to:
|
|
We can always squash the commits before merging, so not too worried about the commit history. Cleaning up the PR title and original comment/description for clarity can be helpful for the reviewer if they are out of date. Having CI passing is also nice if something is genuinely ready to go. That depends on some other things like gh-117 and gh-98 though. Depending on private |
Yes, those are the unrelated issues that cause the CI failures. From my repeated testing on Mac OS and the Linux box, #98 is not a failure locally.
What would be the course of action then? Hold off for a bit? The code right now with |
GFDLRegressor
It depends on the status of the servers/ecosystem at any given time.
Well, if we don't vendor I bet we'll be waiting weeks-months for |
|
Also, I'm seeing new CI failures here that don't have issues open or explanations for them yet--try to clear those up before pinging for review. The most important CI job here, "Array API CI," shows You also have unrelated CI failures that you should try to help the team triage. I just opened gh-145 to describe some of those new failures that you can see in the Python It would also be good to have another team member confirm that they are fully happy with all the changes here before pinging me as noted last month at #129 (review). I'll try to get you some feedback pretty soon either way though, but for sustainability that's probably useful to do. |

This PR modifies
.fit(),partial_fit()and.predict()in the baseGFDLto use Array API functionality/enhancements. TheGFDLRegressoris not directly touched, as the array API modifications happen in baseGFDLand invoked viasuper().fit()andsuper().predict(). From initial experiments, using accelerator devices with PyTorch or CuPy noticeably speeds upfitcalls.Test functions in
test_regression.pyare written to evaluate behavior with namespaces, devices, and dtypes that are accessed viasklearn.utils._array_api.yield_namespace_device_dtype_combinations(). We have tested these changes on NumPy, Array API Strict, PyTorch, and CuPy with CPU, MPS, and CUDA devices and float32 and float64 dtypes.To test, you would use an incantation like:
python -m pip install -e . python -m pip install array-api-strict array-api-compat torch cupy-cuda13x SCIPY_ARRAY_API=1 PYTORCH_ENABLE_MPS_FALLBACK=1 python -m pytestThe changes use private utilities from
sklearn.Checklist:
[x] Benchmark speed against NumPy
[x] How important is
solver=svd?- It's more robust in matching values between NumPy and Torch backends. Runtime differences appear to be negligible. Documentation says SVD is more stable, which could be good when inverting very large p > n matrices.
[x] Are we able at all to check coefficients and predictions for PyTorch with bounded weight schemes?
- Yes, but we have to gain control of value checking in
check_array_api_input_and_valuesfrom scikit-learn via a custom test function.[x] Is there a more elegant solution than the private utility functions?
-
move_to,_max_precision_float_dtype[x] Is there an sklearn decorator we could use in the test suite?
- You add tags to the estimator class and use
array_api_dispatch[x] Would some of the test functions already be captured by an sklearn decorator?
- Yes,
check(estimator)witharray_api_dispatchalready does a lot input and output validation[x] Do the activation and weight generation functions need to be in
xp?- Weights come from NumPy and are then converted
- Activations should be in
xp, except forscipy.specialwhich already handles that[x] Can we lower the
atolin thetest_torch_matches_numpyfunction?- Removed this function for
check_array_api_input_and_valuesSI disclosure: Provided advice and small code snippets for which
sklearnutilities we could leverage formodel.py