Skip to content

gpl: Fix overflow, sampling bias, and edge cases in MBFF KMeans, GetS… - #11645

Open
debayanbandyopadhyay wants to merge 1 commit into
The-OpenROAD-Project:masterfrom
debayanbandyopadhyay:fix-mbff-kmeans-overflow-and-silh
Open

debayanbandyopadhyay wants to merge 1 commit into
The-OpenROAD-Project:masterfrom
debayanbandyopadhyay:fix-mbff-kmeans-overflow-and-silh

Conversation

@debayanbandyopadhyay

@debayanbandyopadhyay debayanbandyopadhyay commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  1. Fix 32-bit float precision absorption, float-to-int cast overflow UB, and K-Means++ sampling bias in MBFF::KMeans:
    • Accumulate squared distances (tot_sum, cum_sum) and prob in double rather than 32-bit float to prevent mantissa absorption (> 1.67e7) on large clock domains (100k+ flops) and eliminate float-to-integer cast overflow UB even for extreme coordinates (tot_sum * 100.0 > INT64_MAX, or near the INT_MAX boundary where static_cast<float>(INT_MAX) rounds up to 2147483648.0f).
    • Use named constants (kLegacyProbScale = 100.0, kLargeDesignProbResolution = 1000000): preserve exact RNG modulo scaling for small designs (1.0 <= raw_scaled_sum <= INT_MAX), and combine two 15+ bit draws from next_rand() ((rand_hi << 15) ^ rand_lo) for uniform 6-digit fractional scaling (tot_sum * fraction) when raw_scaled_sum > INT_MAX so prob is not hard-capped at RAND_MAX / 100.0 (even when RAND_MAX == 32767).
    • Add a bounds-safe next_rand() helper that wraps rand_nums modulo rand_nums.size(), preserves the lower 31 bits of entropy (val & 0x7FFFFFFF) for negative 32-bit integers, guards against empty rand_nums, and keeps the RNG stream aligned when raw_scaled_sum < 1.0.
    • Update d[i] incrementally against centers.back().pt during K-Means++ center selection, reducing center initialization complexity from O(K^2 * N) to O(K * N).
  2. Fix latent infinite loop when knn > num_flops:
    • Clamp actual_knn = std::min(knn, num_flops) in MBFF::KMeans (and return early if num_flops == 0 || knn <= 0).
    • Bound max_k = std::min(8, num_flops) and best_k = std::min(4, max_k) in MBFF::KMeansDecomp (and return early if num_flops < 2).
  3. Guard silhouette score calculation against NaN and float::max() overflow:
    • Guard against 0.0f / 0.0f (NaN) and std::numeric_limits<float>::max() overflow in MBFF::GetSilh and MBFF::GetKSilh when flops or tray slots are co-located or when only a single tray/center exists.
  4. Add unit tests and struct documentation:
    • Document Point, Tray, and Flop in src/gpl/src/mbff.h and add unit tests in src/gpl/test/mbff_test.cpp covering large/extreme distances, negative 32-bit rand_nums, co-located flops/slots, knn > num_flops, short/empty rand_nums, GetSilh, and GetKSilh.

Type of Change

  • Bug fix

Impact

  • Preserves exact RNG trajectory and clustering behavior on existing small designs where 1.0 <= tot_sum * 100.0 <= INT_MAX.
  • Eliminates K-Means++ center-selection truncation bias and float mantissa absorption on large designs (tot_sum * 100.0 > INT_MAX) and reduces K-Means++ initialization from O(K^2 * N) to O(K * N).
  • Prevents infinite loops when MBFF::KMeans or MBFF::KMeansDecomp is invoked on small flop partitions (num_flops < knn or num_flops < 8).
  • Prevents NaN or float::max() silhouette scores in MBFF::GetSilh and MBFF::GetKSilh on co-located flops/slots or single-tray/single-center cases.

Verification

  • I have verified that the local build succeeds (./etc/Build.sh / bazelisk).
  • I have run the relevant tests and they pass (bazelisk test //src/gpl/test/... — 109 passed).
  • My code follows the repository's formatting guidelines (git clang-format upstream/master).
  • I have included tests to prevent regressions.
  • I have signed my commits (DCO).

Related Issues

Follow-up to #11426

@github-actions github-actions Bot added the size/M label Oct 6, 2026

@gemini-code-assist gemini-code-assist Bot 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.

Code Review

This pull request introduces several robustness improvements to the multi-bit flip-flop (MBFF) clustering algorithms, including guarding against division-by-zero and NaN cases in silhouette calculations, preventing infinite loops and integer overflows in K-Means++ when handling large designs or co-located flops, and using double-precision floats for distance accumulation. The reviewer suggested a valuable performance optimization in the K-Means center selection phase to update distances using only the latest center, which reduces the complexity from O(K^2 * N) to O(K * N).

Comment thread src/gpl/src/mbff.cpp
@debayanbandyopadhyay
debayanbandyopadhyay force-pushed the fix-mbff-kmeans-overflow-and-silh branch 4 times, most recently from b153863 to f5b946a Compare October 7, 2026 12:09
1. Guard empty inputs and small flop subsets:
   - Return early in `MBFF::KMeans` when `num_flops == 0`.
   - Cap `knn = std::min(knn, num_flops)` in `MBFF::KMeans` and `MBFF::KMeansDecomp` when `max_sz < num_flops < 8`, avoiding empty clusters (`cur_sz == 0`) and division-by-zero (`0 / 0 = NaN` in `GetKSilh` and `% 0` in `KMeans`).
   - Guard single-element (`cur_sz <= 1`), co-located (`max_ab == 0`), and single-center (`b_j == float::max()`) clusters in `MBFF::GetKSilh`.

2. Prevent integer overflow and sampling bias in `KMeans++` initialization:
   - Accumulate `d[i]` in `double` (`tot_sum`) instead of `float` and normalize probabilities to `kLargeDesignProbResolution = 1,000,000` using two combined random draws (`(rand_hi << 15) ^ rand_lo`, ensuring >=30 bits of entropy even when `RAND_MAX == 32767`) only when `raw_scaled_sum > std::numeric_limits<int>::max()`, preserving exact legacy behavior on small/medium designs.
   - Guard co-located flops (`raw_scaled_sum < 1.0`) to prevent modulo-by-zero (`% 0` UB).
   - Mask the sign bit (`val & 0x7FFFFFFF`) in `next_rand()` so negative entries in `rand_nums` (including `INT_MIN`) cannot trigger `std::abs(INT_MIN)` UB or produce negative modulo results.
   - Update minimum distances incrementally against `centers.back().pt` in `O(K * N)` instead of recomputing all center distances in `O(K^2 * N)`.

3. Unit tests:
   - Added unit tests in `src/gpl/test/mbff_test.cpp` covering empty input, small flop counts (`1 < num_flops < 8`), single-cluster `GetKSilh`, large-coordinate overflow, and negative `rand_nums` (including `INT_MIN`).

Signed-off-by: Debayan Bandyopadhyay <dbandyopadhyay@google.com>
@debayanbandyopadhyay
debayanbandyopadhyay force-pushed the fix-mbff-kmeans-overflow-and-silh branch from f5b946a to 699ab68 Compare October 7, 2026 12:13
@debayanbandyopadhyay
debayanbandyopadhyay marked this pull request as ready for review October 7, 2026 12:30
@debayanbandyopadhyay
debayanbandyopadhyay requested a review from a team as a code owner October 7, 2026 12:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants