Skip to content

Apply new validation to cuml.svm - #8029

Merged
rapids-bot[bot] merged 5 commits into
NVIDIA:mainfrom
jcrist:validation-svm
May 3, 2026
Merged

Apply new validation to cuml.svm#8029
rapids-bot[bot] merged 5 commits into
NVIDIA:mainfrom
jcrist:validation-svm

Conversation

@jcrist

@jcrist jcrist commented Apr 29, 2026

Copy link
Copy Markdown
Member

This applies the new validation utilities to cuml.svm, affecting SVC, SVR, LinearSVC, and LinearSVR. The most effective way to do this required some code movement to rearrange where validation occurred. Beyond that (and some increased validation checks) there should be no behavior changes.

Fixes #8004.

@jcrist jcrist self-assigned this Apr 29, 2026
@jcrist jcrist added the improvement Improvement / enhancement to an existing function label Apr 29, 2026
@jcrist
jcrist requested a review from a team as a code owner April 29, 2026 18:47
@jcrist jcrist added non-breaking Non-breaking change cuml-accel Issues related to cuml.accel labels Apr 29, 2026
@jcrist
jcrist requested a review from viclafargue April 29, 2026 18:47
@jcrist jcrist added sklearn-api-compat Issues around cuml matching sklearn API conventions/standards algo: svm labels Apr 29, 2026
@github-actions github-actions Bot added the Cython / Python Cython or Python issue label Apr 29, 2026
@coderabbitai

coderabbitai Bot commented Apr 29, 2026

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Summary by CodeRabbit

Release Notes

  • Refactor

    • Streamlined input validation and data handling across SVM estimators (LinearSVC, LinearSVR, SVC, and SVR).
    • Centralized data type conversion and normalization logic for improved consistency and maintainability.
  • Tests

    • Enhanced scikit-learn compatibility; multiple previously expected test failures are now resolved.

Walkthrough

Centralizes input validation via check_inputs/check_y; refactors linear SVM internals to accept an estimator and is_classifier, return classes for classifiers, switch to raw CuPy allocations/pointers, wrap outputs at API boundaries, update reflect reset semantics, and narrow scikit-learn xfail expectations for SVM estimators.

Changes

Cohort / File(s) Summary
Core linear internals
python/cuml/cuml/svm/linear.pyx
Refactored fit to accept estimator, is_classifier, optional class_weight, and convert_dtype; centralized input checks via check_inputs; sample_weight optional; returns classes for classifiers; switched allocations/pointer handling to CuPy (cp.empty, cp.asarray, .data.ptr); compute_probabilities updated similarly.
Linear estimator frontends
python/cuml/cuml/svm/linear_svc.py, python/cuml/cuml/svm/linear_svr.py
Frontends delegate validation to cuml.svm.linear.fit; post-fit parameters wrapped as CumlArray when appropriate; intercept_ handles scalar vs array; CPU/GPU dtype handling uses cp.float64; @reflect decorators changed to reset="type".
Kernel SVM frontends & predict path
python/cuml/cuml/svm/svc.py, python/cuml/cuml/svm/svr.py
Consolidated validation via check_inputs/check_y; removed per-estimator input-to-array branching; explicit rejection of sparse inputs for kernel="precomputed"; simplified predict/proba/decision flow to rely on centralized helpers; reflect resets updated.
SVM model base / unpacking
python/cuml/cuml/svm/svm_base.pyx
_SVMModel.unpack now returns raw CuPy arrays or cupyx CSR matrices; _fit re-wraps into CumlArray/SparseCumlArray; CSR pointer extraction standardized (.indptr.data.ptr, .indices.data.ptr, .data.data.ptr); _predict adds convert_dtype arg, enforces fitted check, uses check_inputs and returns CumlArray.
Tests / xfails
python/cuml/cuml_accel_tests/upstream/scikit-learn/xfail-list.yaml, python/cuml/tests/test_sklearn_compatibility.py
Pruned many prior scikit-learn estimator-check xfails for LinearSVC/LinearSVR/SVC/SVR, narrowing expected failures to a smaller subset (mostly estimator-tag/selected sample-weight cases).

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • betatim
  • csadorf
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Apply new validation to cuml.svm' accurately summarizes the main change: applying new validation utilities to the SVM module affecting multiple classes.
Description check ✅ Passed The description is related to the changeset, explaining that new validation utilities are applied to cuml.svm classes (SVC, SVR, LinearSVC, LinearSVR) with code rearrangement and increased validation checks.
Linked Issues check ✅ Passed The changeset directly addresses the linked issue #8004 by applying new input validation utilities to cuml.svm classes, implementing centralized validation through check_inputs() and process_class_weight().
Out of Scope Changes check ✅ Passed Changes are focused on validation refactoring across SVM module files and test configurations; xfail list updates reflect reduced expected failures after validation improvements.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Review rate limit: 9/10 reviews remaining, refill in 6 minutes.

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
python/cuml/cuml/svm/linear_svr.py (1)

203-203: Consider using reset=True instead of reset="type" for fit method.

According to the context snippets, reset="type" only resets the reflected type (input type tracking via _set_output_type), but does NOT reset feature validation attributes (n_features_in_, feature_names_in_). For fit() methods, typically reset=True should be used to ensure both the output type and feature metadata are reset.

However, looking at the implementation, cuml.svm.linear.fit internally calls check_inputs(..., reset=True), which handles the feature reset. This appears to be intentional to centralize the reset logic in the shared fit function.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@python/cuml/cuml/svm/linear_svr.py` at line 203, Change the decorator on the
fit method from `@reflect`(reset="type") to `@reflect`(reset=True) so fitting resets
both the reflected output type and feature metadata (n_features_in_,
feature_names_in_); locate the decorator in linear_svr.py where fit is defined
and update it to reset=True, and then run tests or ensure the existing
cuml.svm.linear.fit call that uses check_inputs(..., reset=True) remains
compatible (no additional code changes required).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@python/cuml/cuml/svm/linear_svr.py`:
- Line 4: Replace the incorrect "import numpy as cp" with "import cupy as cp" so
that all cp.* usages (e.g., cp.float64 in the type casts and cp.isscalar checks)
refer to CuPy rather than NumPy; update the import at the top of linear_svr.py
to import cupy as cp to match the codebase convention and ensure cp.float64 and
cp.isscalar are the CuPy implementations.

---

Nitpick comments:
In `@python/cuml/cuml/svm/linear_svr.py`:
- Line 203: Change the decorator on the fit method from `@reflect`(reset="type")
to `@reflect`(reset=True) so fitting resets both the reflected output type and
feature metadata (n_features_in_, feature_names_in_); locate the decorator in
linear_svr.py where fit is defined and update it to reset=True, and then run
tests or ensure the existing cuml.svm.linear.fit call that uses
check_inputs(..., reset=True) remains compatible (no additional code changes
required).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 811ead37-9fad-4fe4-b64b-80a9086028b3

📥 Commits

Reviewing files that changed from the base of the PR and between c811e80 and 151734d.

📒 Files selected for processing (8)
  • python/cuml/cuml/svm/linear.pyx
  • python/cuml/cuml/svm/linear_svc.py
  • python/cuml/cuml/svm/linear_svr.py
  • python/cuml/cuml/svm/svc.py
  • python/cuml/cuml/svm/svm_base.pyx
  • python/cuml/cuml/svm/svr.py
  • python/cuml/cuml_accel_tests/upstream/scikit-learn/xfail-list.yaml
  • python/cuml/tests/test_sklearn_compatibility.py
💤 Files with no reviewable changes (1)
  • python/cuml/cuml_accel_tests/upstream/scikit-learn/xfail-list.yaml

Comment thread python/cuml/cuml/svm/linear_svr.py Outdated
@jcrist
jcrist force-pushed the validation-svm branch 2 times, most recently from 1b6f8e6 to 4e8ce89 Compare April 29, 2026 21:33

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
python/cuml/cuml/svm/linear_svr.py (1)

208-224: ⚠️ Potential issue | 🟠 Major

Forward convert_dtype into cuml.svm.linear.fit().

LinearSVR.fit() still exposes convert_dtype, but this call no longer passes it through. As written, convert_dtype=False is ignored and the centralized validator always uses its default coercion behavior. That's a regression in the public fit API.

Suggested fix
         coef, intercept, n_iter, _, _ = cuml.svm.linear.fit(
             self,
             X,
             y,
             sample_weight=sample_weight,
+            convert_dtype=convert_dtype,
             loss=self.loss,
             penalty=self.penalty,
             fit_intercept=self.fit_intercept,
             penalized_intercept=self.penalized_intercept,
             max_iter=self.max_iter,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@python/cuml/cuml/svm/linear_svr.py` around lines 208 - 224, The call to
cuml.svm.linear.fit inside LinearSVR.fit is not forwarding the public
convert_dtype flag, so convert_dtype set on LinearSVR is ignored; update the
cuml.svm.linear.fit(...) call (the invocation in linear_svr.py that currently
passes loss, penalty, C, tol, epsilon, etc.) to include
convert_dtype=self.convert_dtype (or the local convert_dtype parameter) so the
centralized validator receives the intended value from LinearSVR.fit.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@python/cuml/cuml_accel_tests/upstream/scikit-learn/xfail-list.yaml`:
- Around line 793-796: The current xfail entries for the four SVM validation
checks
("sklearn.tests.test_common::test_estimators[LinearSVC()-check_classifier_data_not_an_array]",
"sklearn.tests.test_common::test_estimators[LinearSVR()-check_regressor_data_not_an_array]",
"sklearn.tests.test_common::test_estimators[SVC()-check_classifier_data_not_an_array]",
"sklearn.tests.test_common::test_estimators[SVR()-check_regressor_data_not_an_array]")
are incorrectly placed in the flaky bucket; remove these four lines from
cuml_accel_flaky and add them to a deterministic xfail bucket (e.g., the
invalid-sklearn-tests or an SVM-specific xfail group) so they are treated as
persistent validation gaps rather than flaky tests. Ensure the new bucket uses
the deterministic xfail configuration (not strict: false) and keep the exact
test IDs as shown so the test runner matches them.

---

Outside diff comments:
In `@python/cuml/cuml/svm/linear_svr.py`:
- Around line 208-224: The call to cuml.svm.linear.fit inside LinearSVR.fit is
not forwarding the public convert_dtype flag, so convert_dtype set on LinearSVR
is ignored; update the cuml.svm.linear.fit(...) call (the invocation in
linear_svr.py that currently passes loss, penalty, C, tol, epsilon, etc.) to
include convert_dtype=self.convert_dtype (or the local convert_dtype parameter)
so the centralized validator receives the intended value from LinearSVR.fit.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 3bca8710-2925-4ccf-876c-d4c09c7ddcea

📥 Commits

Reviewing files that changed from the base of the PR and between 1b6f8e6 and 4e8ce89.

📒 Files selected for processing (8)
  • python/cuml/cuml/svm/linear.pyx
  • python/cuml/cuml/svm/linear_svc.py
  • python/cuml/cuml/svm/linear_svr.py
  • python/cuml/cuml/svm/svc.py
  • python/cuml/cuml/svm/svm_base.pyx
  • python/cuml/cuml/svm/svr.py
  • python/cuml/cuml_accel_tests/upstream/scikit-learn/xfail-list.yaml
  • python/cuml/tests/test_sklearn_compatibility.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • python/cuml/tests/test_sklearn_compatibility.py
  • python/cuml/cuml/svm/svr.py

Comment thread python/cuml/cuml_accel_tests/upstream/scikit-learn/xfail-list.yaml
Comment thread python/cuml/cuml/svm/linear_svr.py
Comment thread python/cuml/cuml/svm/svc.py Outdated
Comment thread python/cuml/cuml/svm/svc.py Outdated
@betatim

betatim commented Apr 30, 2026

Copy link
Copy Markdown
Contributor

I like it.

Do I want to know why the check_classifier_data_not_an_array is flaky? I somehow suspect that I don't want to know :-/

@viclafargue viclafargue left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks! LGTM. Just one minor comment.

Comment thread python/cuml/cuml/svm/svm_base.pyx Outdated
@jcrist

jcrist commented Apr 30, 2026

Copy link
Copy Markdown
Member Author

Do I want to know why the check_classifier_data_not_an_array is flaky? I somehow suspect that I don't want to know :-/

This one is annoying. I've not actually seen the flakiness from the classifier versions (LinearSVC, SVC), but have from the regressors. I proactively marked all as flaky to save a headache later.

The intent of the check_*_data_is_not_an_array tests is to test that an input that isn't a numpy array but something that exposes an __array__ method works on the estimator. The test itself does this by calling fit(...).predict(...) on instances of the estimator - one with a numpy array (float64) and one with a thin wrapper. It then compares the output of predict for consistency (with a tolerance).

In our case, we sometimes see differences in output large enough to fail the test. I suspect this is due to some non-determinism in svm module, as well as differences between float32 and float64 (the non-array-but-array-like-input is coerced to float32 in our case, while the numpy array input remains at float64). This difference in dtype treatment is consistent with how sklearn's check_array handles things too (array-likes are always coerced to the first provided dtype unless they also expose a dtype attribute), but in their case their SVM module only ever runs with float64 values, so everything is always coerced to float64 anyway.

Since the actual point of the test ("does this estimator correctly handle array-like inputs") is now resolved, I'm not worried about marking this test as flaky. We do the right thing as far as this use case is concerned.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
python/cuml/cuml/svm/linear_svc.py (1)

243-273: ⚡ Quick win

Add a fit_intercept=False regression test for the scalar-intercept path.

python/cuml/cuml/svm/linear.pyx:268-279 makes the scalar intercept_ branch reachable here, and this wrapper now flips intercept_ between a scalar and CumlArray. A focused test for fit_intercept=False on both LinearSVC and LinearSVR would lock down the new descriptor/interop path.

As per coding guidelines, "Update unit tests when making code changes".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@python/cuml/cuml/svm/linear_svc.py` around lines 243 - 273, Add regression
tests that exercise the scalar-intercept path by training models with
fit_intercept=False for both LinearSVC and LinearSVR (the code path calling
cuml.svm.linear.fit and assigning self.intercept_ via the scalar check
cp.isscalar(intercept)). For each model, call fit with fit_intercept=False and
assert that intercept_ is a plain scalar (not a CumlArray) and that coef_
remains a CumlArray; include a paired test with fit_intercept=True to confirm
the intercept_ is a CumlArray in that case. Ensure tests are deterministic with
a small synthetic dataset and cover both classifier and regressor to lock down
the descriptor/interop path.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@python/cuml/cuml/svm/linear_svc.py`:
- Around line 243-273: Add regression tests that exercise the scalar-intercept
path by training models with fit_intercept=False for both LinearSVC and
LinearSVR (the code path calling cuml.svm.linear.fit and assigning
self.intercept_ via the scalar check cp.isscalar(intercept)). For each model,
call fit with fit_intercept=False and assert that intercept_ is a plain scalar
(not a CumlArray) and that coef_ remains a CumlArray; include a paired test with
fit_intercept=True to confirm the intercept_ is a CumlArray in that case. Ensure
tests are deterministic with a small synthetic dataset and cover both classifier
and regressor to lock down the descriptor/interop path.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 05489ca7-334a-441b-b7f0-a7ec280e8197

📥 Commits

Reviewing files that changed from the base of the PR and between 4e8ce89 and ddb3102.

📒 Files selected for processing (8)
  • python/cuml/cuml/svm/linear.pyx
  • python/cuml/cuml/svm/linear_svc.py
  • python/cuml/cuml/svm/linear_svr.py
  • python/cuml/cuml/svm/svc.py
  • python/cuml/cuml/svm/svm_base.pyx
  • python/cuml/cuml/svm/svr.py
  • python/cuml/cuml_accel_tests/upstream/scikit-learn/xfail-list.yaml
  • python/cuml/tests/test_sklearn_compatibility.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • python/cuml/tests/test_sklearn_compatibility.py
  • python/cuml/cuml/svm/linear.pyx

@jcrist

jcrist commented May 3, 2026

Copy link
Copy Markdown
Member Author

/merge

@rapids-bot
rapids-bot Bot merged commit 0ad2d48 into NVIDIA:main May 3, 2026
324 of 337 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

algo: svm cuml-accel Issues related to cuml.accel Cython / Python Cython or Python issue improvement Improvement / enhancement to an existing function non-breaking Non-breaking change sklearn-api-compat Issues around cuml matching sklearn API conventions/standards

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Update cuml.svm to new input validation

4 participants