Skip to content

fix(weights): normalize challenge weights when sum exceeds 1.0#34

Merged
echobt merged 1 commit intomainfrom
feat/normalize-challenge-weights-1770221621
Feb 4, 2026
Merged

fix(weights): normalize challenge weights when sum exceeds 1.0#34
echobt merged 1 commit intomainfrom
feat/normalize-challenge-weights-1770221621

Conversation

@echobt
Copy link
Contributor

@echobt echobt commented Feb 4, 2026

Summary

This PR adds proportional weight normalization for challenge weights to prevent any single challenge from exceeding its allocated weight share (1.0 maximum).

Changes

  • New function: normalize_hotkey_weights() scales weights proportionally when their sum exceeds 1.0
  • Integration: Applied normalization in collect_challenge_weights() before converting hotkeys to UIDs
  • Tests: Added 7 comprehensive unit tests covering edge cases

Behavior

Scenario Action
Sum > 1.0 Scale all weights by 1.0 / sum
Sum <= 1.0 Leave weights unchanged

Testing

  • All 7 new unit tests pass
  • Code compiles without errors or warnings
  • Cargo clippy passes

Summary by CodeRabbit

  • Bug Fixes

    • Improved handling of challenge weights to ensure proper normalization while maintaining relative proportions between entries.
  • Tests

    • Expanded test coverage for weight normalization behavior across various scenarios.

Add proportional normalization for challenge weights to prevent
any single challenge from exceeding its allocated weight share.
When a challenge returns weights that sum to more than 1.0,
each weight is scaled down proportionally to sum exactly to 1.0.

- Add normalize_hotkey_weights() function
- Integrate normalization in collect_challenge_weights()
- Add comprehensive unit tests for edge cases
@coderabbitai
Copy link

coderabbitai bot commented Feb 4, 2026

📝 Walkthrough

Walkthrough

A new internal weight normalization function normalize_hotkey_weights was added to standardize hotkey-based weights such that their sum does not exceed 1.0 while preserving relative proportions. This function is integrated into fetch_challenge_weights to normalize weights before UID conversion. Comprehensive tests validate normalization behavior across various scenarios.

Changes

Cohort / File(s) Summary
Weight Normalization Logic
crates/bittensor-integration/src/challenge_weight_collector.rs
Introduced internal normalize_hotkey_weights function that scales weights to ensure their sum does not exceed 1.0, integrated into fetch_challenge_weights pipeline, and added extensive test coverage including edge cases (empty input, single entries, sums greater/equal/less than one) and validation of relative proportion preservation.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 Through weights and measures, a rabbit does dance,
Normalizing sums with algorithmic grace,
No overflow here, each proportion preserved,
With tests that ensure perfection is served! 📊✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and concisely describes the main change: adding weight normalization to handle cases where challenge weights exceed 1.0.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/normalize-challenge-weights-1770221621

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.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@crates/bittensor-integration/src/challenge_weight_collector.rs`:
- Around line 87-115: normalize_hotkey_weights currently sums raw weights and
can be skewed by NaN/∞ or negative values; first sanitize the input in
normalize_hotkey_weights by filtering out entries whose weight is not finite
(w.weight.is_finite()) and for finite weights clamp them into a valid range
(e.g., let w = w.weight.clamp(0.0, 1.0)); compute the sum from this sanitized
Vec<HotkeyWeightEntry>, then proceed with the existing logic (if sum>1.0 scale
each sanitized entry by 1.0/sum, otherwise return the sanitized/clamped entries
unchanged) so downstream allocations cannot be distorted by non‑finite or
out‑of‑range weights.

Comment on lines +87 to +115
/// Normalize hotkey weights proportionally if their sum exceeds 1.0
///
/// When a challenge returns weights that sum to more than 1.0, each weight
/// is scaled down proportionally so the total equals 1.0. This ensures
/// no challenge can exceed its allocated weight share.
///
/// - If sum > 1.0: all weights are scaled by (1.0 / sum)
/// - If sum <= 1.0: weights are returned unchanged
fn normalize_hotkey_weights(weights: Vec<HotkeyWeightEntry>) -> Vec<HotkeyWeightEntry> {
if weights.is_empty() {
return weights;
}

let sum: f64 = weights.iter().map(|w| w.weight).sum();

// Only normalize if sum exceeds 1.0
if sum > 1.0 {
tracing::info!(
"Normalizing {} weights: sum={:.4} -> 1.0 (scaling by {:.4})",
weights.len(),
sum,
1.0 / sum
);
weights
.into_iter()
.map(|w| HotkeyWeightEntry {
hotkey: w.hotkey,
weight: w.weight / sum,
})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Sanitize non‑finite/out‑of‑range weights before summing.

Line 100 sums raw weights; NaN/∞ or negative values can bypass normalization and silently distort downstream allocations. Clamp and drop non‑finite values before computing sum.

Proposed fix
-fn normalize_hotkey_weights(weights: Vec<HotkeyWeightEntry>) -> Vec<HotkeyWeightEntry> {
+fn normalize_hotkey_weights(mut weights: Vec<HotkeyWeightEntry>) -> Vec<HotkeyWeightEntry> {
     if weights.is_empty() {
         return weights;
     }
 
+    for w in &mut weights {
+        if !w.weight.is_finite() {
+            warn!(
+                "Non-finite weight for hotkey {}, defaulting to 0.0",
+                w.hotkey
+            );
+            w.weight = 0.0;
+        }
+        w.weight = w.weight.clamp(0.0, 1.0);
+    }
+
     let sum: f64 = weights.iter().map(|w| w.weight).sum();
🤖 Prompt for AI Agents
In `@crates/bittensor-integration/src/challenge_weight_collector.rs` around lines
87 - 115, normalize_hotkey_weights currently sums raw weights and can be skewed
by NaN/∞ or negative values; first sanitize the input in
normalize_hotkey_weights by filtering out entries whose weight is not finite
(w.weight.is_finite()) and for finite weights clamp them into a valid range
(e.g., let w = w.weight.clamp(0.0, 1.0)); compute the sum from this sanitized
Vec<HotkeyWeightEntry>, then proceed with the existing logic (if sum>1.0 scale
each sanitized entry by 1.0/sum, otherwise return the sanitized/clamped entries
unchanged) so downstream allocations cannot be distorted by non‑finite or
out‑of‑range weights.

@echobt echobt merged commit 168f138 into main Feb 4, 2026
7 checks passed
@echobt echobt deleted the feat/normalize-challenge-weights-1770221621 branch February 4, 2026 16:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant