Thanks to visit codestin.com
Credit goes to github.com

Skip to content

MNT Use integer cumulative sum in confusion_matrix_at_thresholds for unweighted inputs - #34817

Merged
OmarManzoor merged 12 commits into
scikit-learn:mainfrom
Kaynup:mnt-cumsum-confusion-matrix-unweighted-34813
Aug 31, 2026
Merged

MNT Use integer cumulative sum in confusion_matrix_at_thresholds for unweighted inputs#34817
OmarManzoor merged 12 commits into
scikit-learn:mainfrom
Kaynup:mnt-cumsum-confusion-matrix-unweighted-34813

Conversation

@Kaynup

@Kaynup Kaynup commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Reference Issues/PRs

Towards #34813. See also #33200.

What does this implement/fix? Explain your changes.

As discussed in #34813, confusion_matrix_at_thresholds currently computes cumulative counts using floating-point cumulative sums. On float32-only devices (e.g. PyTorch MPS or array-api-strict's single-precision mock devices), _max_precision_float_dtype returns float32. Because single-precision floats only have 24 effective bits of mantissa, cumulative sums freeze once counts exceed $2^{24} = 16,777,216$ samples due to floating-point swamping (where adding $+1.0$ rounds down to itself).

This PR implements the unweighted integer accumulation strategy proposed in #34813:

  • When sample_weight is None, y_true is accumulated in integer space (xp.int64 or fallback xp.int32), giving 100% exact counts and completely preventing float32 saturation on large datasets.
  • Derives fps_int = (threshold_idxs + 1) - tps_int in the integer domain, avoiding mixed-integer type promotion errors in strict array API modes.
  • Casts tps and fps to the floating-point dtype of y_score at the output boundary.
  • Added test_confusion_matrix_at_thresholds_unweighted_integer_accumulation in sklearn/metrics/tests/test_ranking.py testing float32 dtypes, ties, all-positive, and all-negative edge cases.
  • Added changelog entry in doc/whats_new/upcoming_changes/sklearn.metrics/34813.enhancement.rst.

Introduce yourself

Hello everyone, I'm contributing to scikit-learn to help improve array API support and numerical stability across accelerated and single-precision array devices.

AI usage disclosure

I used AI assistance for:

  • Test cases generation
  • Understanding

Any other comments?

… unweighted inputs

- Avoids float32 precision swamping/saturation on float32-only array API devices when N > 2^24.
- Ensures type-safe integer arithmetic between threshold indices and true positive counts.
- Preserves y_score floating-point dtype at the output boundary.
- Closes scikit-learn#34813 (part 1).
@github-actions

Copy link
Copy Markdown

Thank you for opening your first pull request to scikit-learn! 🎉

To help get your contribution reviewed, please make sure that:

  • You have filled out the pull request template.

  • The pull request addresses an existing issue that is ready for contribution (e.g. not tagged as 'Needs Triage', 'Needs Decision', ...). If you are proposing a new feature, please open an issue to discuss it first.

  • There are no other open pull requests already targeting the same issue.

  • You have followed the pull request checklist. In particular, linting and tests should pass.

@github-actions github-actions Bot added the CI:Linter failure The linter CI is failing on this PR label Aug 25, 2026
@ogrisel

ogrisel commented Aug 26, 2026

Copy link
Copy Markdown
Member

Thanks for the PR, however the test is too weak as it would not reproduce the numerical stability problem on a float32 only device. I concurrently vibe-coded a fix for this issue and the test in my branch seems stronger. Let me push it to your branch.

Use a float32-only large-n non-regression test that fails when
cumulative sums saturate past 2**24, covering the bug fixed in scikit-learn#34813.

Co-authored-by: Cursor <[email protected]>
@ogrisel

ogrisel commented Aug 26, 2026

Copy link
Copy Markdown
Member

Pushed a follow-up commit replacing test_confusion_matrix_at_thresholds_unweighted_integer_accumulation with test_confusion_matrix_at_thresholds_float32_only_unweighted_large_n.

The previous smoke test only checked small float32 arrays / all-pos / all-neg edge cases and would not catch float32 cumsum saturation. The new test uses array-api-strict's no_float64 device with n_pos > 2**24, which is the actual failure mode from #34813.

@ogrisel ogrisel moved this to In Progress in Array API Aug 26, 2026
Comment thread sklearn/metrics/tests/test_ranking.py Outdated
Comment thread sklearn/metrics/_ranking.py Outdated
Comment thread sklearn/metrics/_ranking.py

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

I think the following lines are redundant.

Comment thread sklearn/metrics/tests/test_ranking.py Outdated
Comment thread sklearn/metrics/tests/test_ranking.py Outdated
@github-actions github-actions Bot added the CI:Linter failure The linter CI is failing on this PR label Aug 27, 2026
Comment thread sklearn/metrics/tests/test_ranking.py Outdated
@github-actions github-actions Bot removed the CI:Linter failure The linter CI is failing on this PR label Aug 27, 2026
@ogrisel ogrisel added CUDA CI and removed CUDA CI labels Aug 27, 2026
@github-actions github-actions Bot removed the CUDA CI label Aug 27, 2026
@Kaynup

Kaynup commented Aug 27, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the fix and cleanup! @ogrisel.
May I also try to work on the weighted case as another draft as you mentioned? #34813

I'll keep in mind the changes you did.

@ogrisel

ogrisel commented Aug 27, 2026

Copy link
Copy Markdown
Member

May I also try to work on the weighted case as another draft as you mentioned? #34813

I already have a local branch that implement a different strategy as what I originally suggested in the issue does not seem to be stable enough.

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

@Kaynup I suppose this is ready for review, hence you can remove the draft status of that PR.

LGTM.

@ogrisel

ogrisel commented Aug 27, 2026

Copy link
Copy Markdown
Member

I ran the Intel GPU tests there and they pass:

https://github.com/probabl-ai/scikit-learn-intel-workflow/actions/runs/33060755214

@Kaynup

Kaynup commented Aug 27, 2026

Copy link
Copy Markdown
Contributor Author

May I also try to work on the weighted case as another draft as you mentioned? #34813

I already have a local branch that implement a different strategy as what I originally suggested in the issue does not seem to be stable enough.

Ok

@Kaynup I suppose this is ready for review, hence you can remove the draft status of that PR.

LGTM.

I have actually hit the 1 open PR limit. Can you change the status from your end if its alright?

@ogrisel

ogrisel commented Aug 27, 2026

Copy link
Copy Markdown
Member

I reviewed your other PR (#34789). Since it seems to need some more work, maybe you can set that one to draft and mark this one ready for review instead.

@ogrisel ogrisel added Quick Review For PRs that are quick to review Numerical Stability labels Aug 28, 2026

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

Thank you for the PR @Kaynup . A few minor comments otherwise looks good

Comment thread sklearn/metrics/tests/test_ranking.py
Comment thread sklearn/metrics/tests/test_ranking.py Outdated
Comment thread sklearn/metrics/tests/test_ranking.py Outdated
Comment thread sklearn/metrics/tests/test_ranking.py Outdated
Co-authored-by: Omar Salman <[email protected]>
Co-authored-by: Olivier Grisel <[email protected]>
Comment thread sklearn/metrics/tests/test_ranking.py Outdated
Comment thread sklearn/metrics/tests/test_ranking.py Outdated

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

LGTM. Thank you @Kaynup and @ogrisel

@OmarManzoor
OmarManzoor enabled auto-merge (squash) August 31, 2026 10:06
@OmarManzoor
OmarManzoor merged commit 0963bff into scikit-learn:main Aug 31, 2026
37 checks passed
@github-project-automation github-project-automation Bot moved this from In Progress to Done in Array API Aug 31, 2026
@Kaynup

Kaynup commented Aug 31, 2026

Copy link
Copy Markdown
Contributor Author

LGTM. Thank you @Kaynup and @ogrisel

Thank you! But it was mostly Olivier driving the implementation forward!

I had a hunch that the unweighted case could serve as a solid foundation for the weighted follow-up (#34827). It's been really exciting to see this develop, and I look forward to contributing more to the Array API initiatives and scikit-learn as I continue learning.

Thank you again for the thorough reviews, guidance, and approvals! @ogrisel and @OmarManzoor

cursor Bot pushed a commit to ogrisel/scikit-learn that referenced this pull request Sep 1, 2026
Resolve conflicts with scikit-learn#34817 by keeping both the unweighted int64
cumsum path and the weighted float32-only fixed-point scale path, and
retaining both corresponding large-n float32-only tests.

Co-authored-by: Olivier Grisel <[email protected]>
prady0t pushed a commit to prady0t/scikit-learn that referenced this pull request Sep 2, 2026
@jeremiedbb jeremiedbb mentioned this pull request Sep 8, 2026
14 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants