11//! Weight Calculator
22//!
33//! Converts challenge leaderboard scores to Bittensor weights.
4- //! Handles normalization, edge cases, and commit-reveal protocol.
4+ //! Pure pass-through of challenge weights - no manipulation.
5+ //! Unused weight automatically goes to UID 0 (burn address).
56
67use serde:: { Deserialize , Serialize } ;
78use tracing:: { info, warn} ;
89
910/// Maximum weight value for Bittensor (u16 max)
1011pub const MAX_WEIGHT : u16 = 65535 ;
1112
13+ /// UID 0 is the burn address - receives all unused weight
14+ pub const BURN_UID : u16 = 0 ;
15+
1216/// Weight calculator configuration
1317#[ derive( Debug , Clone , Serialize , Deserialize ) ]
1418pub struct WeightCalculatorConfig {
15- /// Minimum score to receive any weight
19+ /// Minimum score to receive any weight (default: 0.0 = no threshold)
1620 pub min_score_threshold : f64 ,
1721 /// Temperature for softmax (higher = more distributed weights)
1822 pub temperature : f64 ,
1923 /// Whether to use softmax or linear normalization
2024 pub use_softmax : bool ,
21- /// Maximum weight any single UID can receive (as fraction)
25+ /// Maximum weight any single UID can receive (as fraction) - DISABLED (set to 1.0)
26+ /// NOTE: Weight cap removed - challenges receive pure weights based on emission %
2227 pub max_weight_fraction : f64 ,
2328 /// Mechanism ID for this challenge
2429 pub mechanism_id : u8 ,
@@ -29,8 +34,8 @@ impl Default for WeightCalculatorConfig {
2934 Self {
3035 min_score_threshold : 0.0 ,
3136 temperature : 1.0 ,
32- use_softmax : true ,
33- max_weight_fraction : 0.5 , // No single UID gets > 50%
37+ use_softmax : false , // Use simple linear normalization
38+ max_weight_fraction : 1.0 , // No cap - pure weights
3439 mechanism_id : 0 ,
3540 }
3641 }
@@ -185,52 +190,14 @@ impl WeightCalculator {
185190 . collect ( )
186191 }
187192
188- /// Apply max weight cap and redistribute
189- fn apply_weight_cap ( & self , mut weights : Vec < ( u16 , f64 , f64 ) > ) -> Vec < ( u16 , f64 , f64 ) > {
190- let max = self . config . max_weight_fraction ;
191-
192- // Sort by weight descending
193- weights. sort_by ( |a, b| b. 1 . partial_cmp ( & a. 1 ) . unwrap_or ( std:: cmp:: Ordering :: Equal ) ) ;
194-
195- let mut excess = 0.0 ;
196- let mut capped_count = 0 ;
197-
198- // Cap weights that exceed max
199- for ( _, weight, _) in weights. iter_mut ( ) {
200- if * weight > max {
201- excess += * weight - max;
202- * weight = max;
203- capped_count += 1 ;
204- }
205- }
206-
207- if excess > 0.0 && capped_count < weights. len ( ) {
208- // Redistribute excess to non-capped weights
209- let uncapped: Vec < usize > = weights
210- . iter ( )
211- . enumerate ( )
212- . filter ( |( _, ( _, w, _) ) | * w < max)
213- . map ( |( i, _) | i)
214- . collect ( ) ;
215-
216- let extra_per = excess / uncapped. len ( ) as f64 ;
217- for i in uncapped {
218- weights[ i] . 1 += extra_per;
219- // Re-cap if needed
220- if weights[ i] . 1 > max {
221- weights[ i] . 1 = max;
222- }
223- }
224- }
225-
226- // Normalize to ensure sum = 1.0
227- let sum: f64 = weights. iter ( ) . map ( |( _, w, _) | w) . sum ( ) ;
228- if sum > 0.0 {
229- for ( _, w, _) in weights. iter_mut ( ) {
230- * w /= sum;
231- }
232- }
233-
193+ /// Apply weight cap (DISABLED - pure pass-through)
194+ ///
195+ /// NOTE: Weight caps have been removed for simpler, more transparent weight distribution.
196+ /// Challenges receive weights purely based on their emission percentage.
197+ /// Unused weight is sent to UID 0 (burn address).
198+ fn apply_weight_cap ( & self , weights : Vec < ( u16 , f64 , f64 ) > ) -> Vec < ( u16 , f64 , f64 ) > {
199+ // No cap applied - return weights as-is
200+ // Normalization already done in normalize_scores()
234201 weights
235202 }
236203
@@ -385,15 +352,16 @@ mod tests {
385352 }
386353
387354 #[ test]
388- fn test_weight_cap ( ) {
355+ fn test_no_weight_cap ( ) {
356+ // Weight caps are DISABLED - pure pass-through
389357 let config = WeightCalculatorConfig {
390358 use_softmax : false ,
391- max_weight_fraction : 0.5 , // 50% max
359+ max_weight_fraction : 1.0 , // No cap (default)
392360 ..Default :: default ( )
393361 } ;
394362 let calc = WeightCalculator :: new ( config) ;
395363
396- // One score dominates
364+ // One score dominates - should get proportional weight
397365 let scores = vec ! [
398366 MinerScore {
399367 uid: 1 ,
@@ -415,10 +383,13 @@ mod tests {
415383
416384 let result = calc. calculate ( & scores, 1 ) ;
417385
418- // Verify cap is applied - no weight should exceed 50%
419- for w in & result. weights {
420- assert ! ( w. normalized <= 0.51 , "Weight {} exceeds cap" , w. normalized) ;
421- }
386+ // With pure pass-through, weights should be proportional to scores
387+ // Score 0.9 / 1.0 = 90% weight, Score 0.1 / 1.0 = 10% weight
388+ let uid1_weight = result. weights . iter ( ) . find ( |w| w. uid == 1 ) . unwrap ( ) ;
389+ let uid2_weight = result. weights . iter ( ) . find ( |w| w. uid == 2 ) . unwrap ( ) ;
390+
391+ assert ! ( uid1_weight. normalized > 0.85 , "UID 1 should get ~90% weight: {}" , uid1_weight. normalized) ;
392+ assert ! ( uid2_weight. normalized < 0.15 , "UID 2 should get ~10% weight: {}" , uid2_weight. normalized) ;
422393
423394 // Verify weights still sum to 1.0
424395 let sum: f64 = result. weights . iter ( ) . map ( |w| w. normalized ) . sum ( ) ;
0 commit comments