Skip to content

Use new input validation infrastructure for cuml.decomposition. - #8006

Merged
rapids-bot[bot] merged 12 commits into
NVIDIA:mainfrom
csadorf:decomposition-new-validation
Apr 24, 2026
Merged

Use new input validation infrastructure for cuml.decomposition.#8006
rapids-bot[bot] merged 12 commits into
NVIDIA:mainfrom
csadorf:decomposition-new-validation

Conversation

@csadorf

@csadorf csadorf commented Apr 23, 2026

Copy link
Copy Markdown
Contributor

This applies the new input validation utilities added in #7973 to cuml.decomposition.

Doing this fixed ~9 failing sklearn compatibility tests for cuml proper, and at least 36 upstream tests for cuml.accel.

Fixes #7990
Part of #7428

@csadorf
csadorf requested a review from a team as a code owner April 23, 2026 01:43
@csadorf
csadorf requested a review from dantegd April 23, 2026 01:43
@github-actions github-actions Bot added the Cython / Python Cython or Python issue label Apr 23, 2026
@csadorf csadorf added improvement Improvement / enhancement to an existing function non-breaking Non-breaking change labels Apr 23, 2026
@coderabbitai

coderabbitai Bot commented Apr 23, 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

Walkthrough

Replace bespoke array conversion and sparse helpers in IncrementalPCA, PCA, and TruncatedSVD with unified validation (check_inputs/check_array), allocate buffers with CuPy and wrap as CumlArray, update reflect decorators to reset="type", and adjust tests/xfails to expect passing sklearn checks.

Changes

Cohort / File(s) Summary
IncrementalPCA
python/cuml/cuml/decomposition/incremental_pca.py
Unifies validation via check_inputs/check_array (accepts csr/csc, float32/64), removes _validate_sparse_input, densifies sparse batches before partial_fit, changes partial_fit sparse validation flow, and updates fit decorator to @cuml.internals.reflect(reset="type").
PCA (Cython)
python/cuml/cuml/decomposition/pca.pyx
Switches to check_inputs/check_array (dtype + order="F"), allocates intermediates with cupy.zeros(..., order="F"), uses .data.ptr for kernels, wraps outputs as CumlArray(data=...), updates dense helpers to accept index=None, replaces output-type context manager with explicit .to_output("cupy"), and changes reflect decorators to reset="type".
TruncatedSVD (Cython)
python/cuml/cuml/decomposition/tsvd.pyx
Replaces input_to_cuml_array with check_inputs/check_array (captures index), allocates outputs with cupy.zeros(..., order="F") and constructs CumlArray(data=..., index=...), uses .data.ptr for kernels, removes check_features from transform, adds inverse-transform column-count guard, and updates reflect decorator to reset="type".
Tests & xfail config
python/cuml/cuml_accel_tests/upstream/scikit-learn/xfail-list.yaml, python/cuml/tests/test_incremental_pca.py, python/cuml/tests/test_sklearn_compatibility.py, python/cuml/tests/test_exceptions.py
Removes several xfail entries and conditional skips for PCA/IncrementalPCA/TruncatedSVD (dtype/object, empty-data messages, NaN/Inf, transform checks), enables csc sparse cases in IncrementalPCA tests, and relaxes a sparse-related regex in an exception test.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • dantegd
  • jcrist
  • viclafargue
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 57.14% 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 'Use new input validation infrastructure for cuml.decomposition' directly and concisely describes the primary change—applying new validation utilities to the decomposition module.
Description check ✅ Passed The description accurately describes the changeset: applying new input validation utilities to cuml.decomposition, references the foundational PR #7973, and mentions both the test fixes and linked issues.
Linked Issues check ✅ Passed The code changes fully address issue #7990 by migrating PCA, IncrementalPCA, and TruncatedSVD to use the new check_inputs/check_array validation utilities, completing the decomposition module's transition to standardized input validation.
Out of Scope Changes check ✅ Passed All changes are directly scoped to the decomposition module's validation infrastructure migration; test updates and xfail removals are necessary consequences of the validation improvements and not extraneous additions.

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

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

Warning

Review ran into problems

🔥 Problems

Timed out fetching pipeline failures after 30000ms


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: 2

🤖 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/decomposition/incremental_pca.py`:
- Around line 277-283: The code resets input metadata unconditionally using
n_samples_seen_, _set_output_type and check_features even when check_input is
False; wrap the initial metadata setup so it only runs if check_input is True:
compute first_call and call self._set_output_type(X) and check_features(self, X,
reset=first_call) inside an if check_input: block (or ensure check_features is
invoked with reset=False when check_input is False). Update the partial_fit/fit
flow to preserve the original reflected input type and feature_names_in_ by
avoiding these resets for internal CuPy batches.
- Around line 222-228: In fit() the call to check_array strips pandas/cuDF
feature names before check_features(..., reset=True) runs, so feature_names_in_
and n_features_in_ never get set; fix by capturing feature names and resetting
feature metadata before coercion—either call check_features(self, X, reset=True)
before check_array or switch to check_inputs(X, accept_sparse=..., dtype=...,
convert_dtype=..., reset=True) so names are preserved, and ensure fit() assigns
n_features_in_ and feature_names_in_ (the learned attributes) after validation;
update references to check_array, check_features, check_inputs,
feature_names_in_, and n_features_in_ accordingly.
🪄 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: Pro Plus

Run ID: f36de441-99a0-4085-8c87-f74bfad41870

📥 Commits

Reviewing files that changed from the base of the PR and between c7b104c and a2b019b.

📒 Files selected for processing (6)
  • python/cuml/cuml/decomposition/incremental_pca.py
  • python/cuml/cuml/decomposition/pca.pyx
  • python/cuml/cuml/decomposition/tsvd.pyx
  • python/cuml/cuml_accel_tests/upstream/scikit-learn/xfail-list.yaml
  • python/cuml/tests/test_incremental_pca.py
  • python/cuml/tests/test_sklearn_compatibility.py
💤 Files with no reviewable changes (3)
  • python/cuml/tests/test_incremental_pca.py
  • python/cuml/tests/test_sklearn_compatibility.py
  • python/cuml/cuml_accel_tests/upstream/scikit-learn/xfail-list.yaml

Comment thread python/cuml/cuml/decomposition/incremental_pca.py Outdated
Comment thread python/cuml/cuml/decomposition/incremental_pca.py
Comment thread python/cuml/cuml/decomposition/incremental_pca.py Outdated

@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.

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/decomposition/incremental_pca.py (1)

431-439: ⚠️ Potential issue | 🟡 Minor

Potential AttributeError when transform is called on sparse data after partial_fit-only usage.

batch_size_ is set only in fit() (line 236-238), not in partial_fit(). If a user trains exclusively via partial_fit() calls and then calls transform() on sparse input, line 435 will raise AttributeError: 'IncrementalPCA' object has no attribute 'batch_size_'.

Consider falling back to a default batch size when batch_size_ is not set:

Suggested fix
+            batch_size = getattr(self, "batch_size_", None)
+            if batch_size is None:
+                batch_size = 5 * X.shape[1]
             for batch in _gen_batches(
                 n_samples,
-                self.batch_size_,
+                batch_size,
                 min_batch_size=self.n_components or 0,
             ):
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@python/cuml/cuml/decomposition/incremental_pca.py` around lines 431 - 439,
transform()'s sparse-path uses self.batch_size_ which is only set in fit(),
causing AttributeError when the user trained only via partial_fit(); update
transform (sparse branch) to fall back to a default batch size when batch_size_
is missing (e.g., use getattr(self, "batch_size_", some_default) or compute a
safe default from n_samples/n_components) before calling _gen_batches so
_transform_sparse and _gen_batches receive a valid batch range; touch the
transform method and references to batch_size_ (and possibly __init__ or
partial_fit) to ensure consistency with partial_fit-only workflows and preserve
min_batch_size=self.n_components or 0 behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@python/cuml/cuml/decomposition/incremental_pca.py`:
- Around line 431-439: transform()'s sparse-path uses self.batch_size_ which is
only set in fit(), causing AttributeError when the user trained only via
partial_fit(); update transform (sparse branch) to fall back to a default batch
size when batch_size_ is missing (e.g., use getattr(self, "batch_size_",
some_default) or compute a safe default from n_samples/n_components) before
calling _gen_batches so _transform_sparse and _gen_batches receive a valid batch
range; touch the transform method and references to batch_size_ (and possibly
__init__ or partial_fit) to ensure consistency with partial_fit-only workflows
and preserve min_batch_size=self.n_components or 0 behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 9fccc59e-4423-4549-aaec-8d4fd2e32323

📥 Commits

Reviewing files that changed from the base of the PR and between 527798c and 3063ec6.

📒 Files selected for processing (2)
  • python/cuml/cuml/decomposition/incremental_pca.py
  • python/cuml/tests/test_exceptions.py

@csadorf
csadorf requested a review from jcrist April 23, 2026 22:37

@jcrist jcrist left a comment

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.

Looks good, thanks! Looks like a small merge conflict, but otherwise :shipit:!

check_features(self, X, reset=first_call)

if check_input:
X = check_array(X, dtype=("float32", "float64"))

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.

This check_input kwarg is odd, but it's what sklearn does too so 🤷. I do think there's a way to make this work with check_inputs instead of check_features + check_array, but what you have here is fine too.

@csadorf

csadorf commented Apr 24, 2026

Copy link
Copy Markdown
Contributor Author

/merge

@rapids-bot
rapids-bot Bot merged commit bfce194 into NVIDIA:main Apr 24, 2026
93 checks passed
@csadorf
csadorf deleted the decomposition-new-validation branch April 24, 2026 20:39
rapids-bot Bot pushed a commit that referenced this pull request Apr 28, 2026
…ter partial_fit (#8010)

Previously `transform` on sparse inputs accessed `self.batch_size_` directly, which is only set by `fit`, not `partial_fit`. Calling `transform` after `partial_fit` raised an `AttributeError`.

Fall back to `self.batch_size` (or `5 * n_features` if unset) when `batch_size_` is not present, matching the behavior used in `fit`.

Follow-up to #8006

Authors:
  - Simon Adorf (https://github.com/csadorf)

Approvers:
  - Jim Crist-Harif (https://github.com/jcrist)

URL: #8010
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Cython / Python Cython or Python issue improvement Improvement / enhancement to an existing function non-breaking Non-breaking change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Update cuml.decomposition to new input validation

3 participants