Fix davies_bouldin_score returning a wrong value for off-origin float64 data - #3468
Open
VenishPaneliya wants to merge 2 commits into
Open
Fix davies_bouldin_score returning a wrong value for off-origin float64 data#3468VenishPaneliya wants to merge 2 commits into
VenishPaneliya wants to merge 2 commits into
Conversation
VenishPaneliya
requested review from
SkafteNicki and
justusschock
as code owners
August 19, 2026 04:43
VenishPaneliya
added a commit
to VenishPaneliya/torchmetrics
that referenced
this pull request
Aug 19, 2026
`davies_bouldin_score` and `calinski_harabasz_score` allocated their
accumulators without a `dtype`, so the buffers were float32 whatever the
input was. `dunn_index`, same family and same signature, preserves the
input dtype, so the clustering metrics disagreed with each other.
For Davies-Bouldin this was not only a dtype-label problem. Writing a
float64 centroid into a float32 buffer rounds it to roughly seven
significant digits, so `cluster_k - centroids[k]` is dominated by that
rounding once the data sits away from the origin. The score is then
wrong, not merely imprecise:
offset returned correct relative error
0 27.011803 27.011805 1.0e-07
1e3 27.014309 27.011805 9.3e-05
1e5 29.370480 27.011805 8.7e-02
1e7 1.726807 27.011806 9.4e-01
Off-origin coordinates are ordinary -- geographic positions, epoch
timestamps, unnormalised sensor readings -- and a user who reaches for
float64 because of a large dynamic range had it silently discarded.
Calinski-Harabasz narrows only the running accumulation, so its error was
bounded near float32 epsilon, but it was still a silent float32 ceiling
on a float64 input.
Both degenerate branches also returned an explicit float32 scalar; those
now follow the input dtype too.
Values are unchanged for float32 inputs, which already allocated float32
buffers.
Fixes Lightning-AI#3467
VenishPaneliya
force-pushed
the
fix/3467-clustering-buffer-dtype
branch
from
August 24, 2026 10:16
cc2256f to
657146c
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
Fixes #3467
davies_bouldin_scoreandcalinski_harabasz_scoreallocated their accumulators without adtype, so the buffers werefloat32whatever the input was.dunn_index— same family, same signature — preserves the input dtype, so the clustering metrics disagreed with each other.For Davies-Bouldin this is a correctness bug, not a dtype-label one. Writing a
float64centroid into afloat32buffer rounds it to about seven significant digits, socluster_k - centroids[k]ends up dominated by that rounding as soon as the data sits away from the origin:01e31e51e7The clustering structure is identical in every row — only the origin moved. At
offset=1e7the metric returns1.73where the answer is27.01.Off-origin coordinates are ordinary: geographic positions, epoch timestamps, unnormalised sensor readings. A user reaching for
float64because their data has a large dynamic range had it silently discarded.Calinski-Harabasz narrows only the running accumulation — the per-cluster terms are still computed in the input dtype — so its error stayed near
float32epsilon (~1e-7 relative). Still a silentfloat32ceiling on afloat64input, but not catastrophic.Changes
Both degenerate branches also returned an explicit
dtype=torch.float32scalar; those now follow the input dtype too, so the return type is consistent on every path.float32inputs are unaffected — they already allocatedfloat32buffers, so no value moves.Tests
New
tests/unittests/clustering/test_clustering_dtype.py:float64implementation of the same formula;float32behaviour explicitly unchanged.On the tolerances: subtracting a centroid from off-origin data cancels leading digits, so exact translation invariance is not achievable even in
float64. Measured limits are2.6e-12atoffset=1e3,2.9e-10at1e5and5.2e-08at1e7, so the tolerance scales with the offset rather than pretending the error is zero. The bug produced a 94% error at1e7, orders of magnitude outside those bounds, so the test still catches it decisively.Verified the tests actually catch the bug — against unpatched
src/:With the fix:
Full clustering suite, compared against
masterin the same environment:The 10 are exactly this PR's own tests. Nothing else in the suite moves.
Lint, types and doctests:
Related
Same class of problem as #3465 / #3466 (
spearman_corrcoefandkendall_rank_corrcoef), but a different mechanism — that one is integer division falling back to the default dtype, this one is untyped buffer allocation. Kept separate because the fix and the blast radius differ.Before submitting