Skip to content

FIX Raise explicit exception for input with complex dtype - #7729

Merged
rapids-bot[bot] merged 10 commits into
NVIDIA:mainfrom
betatim:fix-raise-exception-for-complex
Feb 11, 2026
Merged

FIX Raise explicit exception for input with complex dtype#7729
rapids-bot[bot] merged 10 commits into
NVIDIA:mainfrom
betatim:fix-raise-exception-for-complex

Conversation

@betatim

@betatim betatim commented Jan 28, 2026

Copy link
Copy Markdown
Contributor

Increases the number of common checks we pass and prevents silent conversion to real. So far estimators in cuml would silently drop the imaginary part and fit on just the real part. Probably not what users were expecting.

Increases the number of common checks we pass and prevent silent
conversion to real.
@betatim
betatim requested a review from a team as a code owner January 28, 2026 13:45
@betatim betatim added the improvement Improvement / enhancement to an existing function label Jan 28, 2026
@betatim
betatim requested a review from dantegd January 28, 2026 13:45
@betatim betatim added the non-breaking Non-breaking change label Jan 28, 2026
@github-actions github-actions Bot added the Cython / Python Cython or Python issue label Jan 28, 2026
@coderabbitai

coderabbitai Bot commented Feb 4, 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

Added runtime checks to reject complex-valued inputs in CumlArray.from_input, added a unit test asserting KMeans raises ValueError on complex data for numpy and cupy, removed many per-estimator complex-data xfail expectations, and pruned corresponding upstream xfail entries.

Changes

Cohort / File(s) Summary
Core validation logic
python/cuml/cuml/internals/array.py
Added runtime dtype checks in CumlArray.from_input to reject complex dtypes (raises ValueError with message "Complex data not supported\n{X}") after DataFrame/Series conversions and after list/tuple handling in accel mode.
Unit tests
python/cuml/tests/test_exceptions.py
Imported cupy and added parameterized test_complex_data_rejected(array_type) that asserts KMeans.fit raises ValueError when given complex-valued inputs for both numpy and cupy.
Sklearn compatibility tests
python/cuml/tests/test_sklearn_compatibility.py
Removed many check_complex_data entries from PER_ESTIMATOR_XFAIL_CHECKS; updated messages for GaussianRandomProjection and SparseRandomProjection to reflect ValueError behavior on small datasets.
Upstream xfail list
python/cuml/cuml_accel_tests/upstream/scikit-learn/xfail-list.yaml
Deleted numerous xfail entries related to complex-data and other expected failures; no new xfail entries added.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • CI Update xfail list #7768: Modifies PER_ESTIMATOR_XFAIL_CHECKS in python/cuml/tests/test_sklearn_compatibility.py, related to removal/adjustment of per-estimator xfail entries.

Suggested labels

bug

Suggested reviewers

  • csadorf
  • jcrist
🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: adding an explicit exception for complex dtype inputs, which is the primary focus of the changeset across all modified files.
Description check ✅ Passed The description is directly related to the changeset, explaining the motivation and benefit of the changes: preventing silent conversion of complex data to real values by raising an explicit exception.

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

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@betatim

betatim commented Feb 4, 2026

Copy link
Copy Markdown
Contributor Author

Needs #7762

@betatim betatim mentioned this pull request Feb 4, 2026

@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/internals/array.py (1)

999-1013: ⚠️ Potential issue | 🟠 Major

CumlArray fast-path bypasses the new complex-dtype check.

If a CumlArray holding complex data is passed to from_input and all fast-path conditions are met (lines 999–1009), it is returned (or deep-copied) without ever hitting the complex-dtype guard at line 1060. This leaves a gap where complex data can silently flow through.

Consider adding the complex check before the fast-path return, e.g.:

Proposed fix
         if (
             isinstance(X, CumlArray)
             and (not convert_to_mem_type or convert_to_mem_type == X.mem_type)
             and (not convert_to_dtype or convert_to_dtype == X.dtype)
             and (not force_contiguous or X.is_contiguous)
             and (order in ("K", None) or X.order == order)
             and not check_dtype
             and not check_mem_type
             and not check_cols
             and not check_rows
         ):
+            if np.issubdtype(X.dtype, np.complexfloating):
+                raise ValueError(f"Complex data not supported\n{X}")
             if deepcopy:
                 return copy.deepcopy(X)
             else:
                 return X

Based on learnings: "Silent data corruption from type coercion, incorrect handling of cuDF vs pandas vs NumPy inputs, or missing validation causing crashes on invalid input must be addressed."

@betatim

betatim commented Feb 11, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai I think via the public API you can't pass complex data to the fast path. So maybe this is out of scope. But the fix is easy and made me think about adding a test so implemented it

@coderabbitai

coderabbitai Bot commented Feb 11, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

@betatim, I appreciate your thoughtfulness about keeping the scope tight! You raise a good point about the accessibility of the fast path via the public API.

That said, I think keeping this fix is valuable for a few reasons:

  1. Defensive programming: Even if complex data is hard to pass through the public API currently, having explicit validation prevents future refactoring or API changes from accidentally creating a path where complex data could slip through.

  2. Explicit error messages: If complex data somehow does reach this code (perhaps through internal APIs or edge cases), the explicit ValueError with "Complex data not supported" is much clearer than whatever obscure error or silent corruption might occur otherwise.

  3. Aligns with existing practices: Based on the learnings I have about this codebase, preventing silent data corruption from type coercion is a priority. This check helps ensure complex dtypes don't get silently coerced to real types by dropping the imaginary component.

  4. Low cost, high clarity: Since the fix is simple and you've already added test coverage, the maintenance burden is minimal while improving code robustness.

Given that it's already implemented, tested, and working (enabling removal of many xfail entries), I'd lean toward keeping it. But if you feel strongly that it's truly unreachable and adds unnecessary complexity, removing it is also reasonable.

What are your thoughts?


🧠 Learnings used
Learnt from: CR
Repo: rapidsai/cuml PR: 0
File: python/agents.md:0-0
Timestamp: 2026-02-04T17:25:12.566Z
Learning: Applies to python/**/**/cuml/**/*.py : Silent data corruption from type coercion, incorrect handling of cuDF vs pandas vs NumPy inputs, or missing validation causing crashes on invalid input must be addressed

Learnt from: CR
Repo: rapidsai/cuml PR: 0
File: python/agents.md:0-0
Timestamp: 2026-02-04T17:25:12.566Z
Learning: Applies to python/**/**/cuml/**/*.py : Missing input dimension checks (n_samples, n_features) and not handling edge cases (empty datasets, single sample) must be addressed in Python estimators

Learnt from: CR
Repo: rapidsai/cuml PR: 0
File: python/agents.md:0-0
Timestamp: 2026-02-04T17:25:12.566Z
Learning: Applies to python/**/**/cuml/**/*.py : Correctly handle cuDF, pandas, and NumPy inputs using input_to_cuml_array() for consistent conversion; preserve input type in output where sensible; handle both row-major (C) and column-major (F) memory order

@jcrist

jcrist commented Feb 11, 2026

Copy link
Copy Markdown
Member

/merge

@rapids-bot
rapids-bot Bot merged commit 09d96c7 into NVIDIA:main Feb 11, 2026
91 checks passed
@betatim
betatim deleted the fix-raise-exception-for-complex branch February 11, 2026 14:32
dantegd added a commit to dantegd/cuml that referenced this pull request Feb 17, 2026
Increases the number of common checks we pass and prevents silent conversion to real. So far estimators in cuml would silently drop the imaginary part and fit on just the real part. Probably not what users were expecting.

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

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

URL: NVIDIA#7729
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.

5 participants