diff --git a/pallets/subtensor/src/benchmarks.rs b/pallets/subtensor/src/benchmarks.rs index bd4348acbb..2e22e3c81c 100644 --- a/pallets/subtensor/src/benchmarks.rs +++ b/pallets/subtensor/src/benchmarks.rs @@ -365,6 +365,16 @@ benchmarks! { }: sudo_set_bonds_moving_average(RawOrigin::>::Root, netuid, bonds_moving_average) + benchmark_sudo_set_bonds_penalty { + let netuid: u16 = 1; + let bonds_penalty: u16 = 100; + let tempo: u16 = 1; + let modality: u16 = 0; + + assert_ok!( Subtensor::::do_add_network( RawOrigin::Root.into(), netuid.try_into().unwrap(), tempo.into(), modality.into())); + + }: sudo_set_bonds_penalty(RawOrigin::>::Root, netuid, bonds_penalty) + benchmark_sudo_set_max_allowed_validators { let netuid: u16 = 1; let tempo: u16 = 1; diff --git a/pallets/subtensor/src/epoch.rs b/pallets/subtensor/src/epoch.rs index 55444b0035..417d2bf75c 100644 --- a/pallets/subtensor/src/epoch.rs +++ b/pallets/subtensor/src/epoch.rs @@ -131,18 +131,22 @@ impl Pallet { // Compute preranks: r_j = SUM(i) w_ij * s_i let preranks: Vec = matmul( &weights, &active_stake ); - // Clip weights at majority consensus - let kappa: I32F32 = Self::get_float_kappa( netuid ); // consensus majority ratio, e.g. 51%. + // Consensus majority ratio, e.g. 51%. + let kappa: I32F32 = Self::get_float_kappa( netuid ); + // Calculate consensus as stake-weighted median of weights. let consensus: Vec = weighted_median_col( &active_stake, &weights, kappa ); - inplace_col_clip( &mut weights, &consensus ); - let validator_trust: Vec = row_sum( &weights ); + // Clip weights at majority consensus. + let mut clipped_weights: Vec> = weights.clone(); + inplace_col_clip( &mut clipped_weights, &consensus ); + // Calculate validator trust as sum of clipped weights set by validator. + let validator_trust: Vec = row_sum( &clipped_weights ); // ==================================== // == Ranks, Server Trust, Incentive == // ==================================== // Compute ranks: r_j = SUM(i) w_ij * s_i - let mut ranks: Vec = matmul( &weights, &active_stake ); + let mut ranks: Vec = matmul( &clipped_weights, &active_stake ); // Compute server trust: ratio of rank after vs. rank before. let trust: Vec = vecdiv( &ranks, &preranks ); @@ -155,6 +159,13 @@ impl Pallet { // == Bonds and Dividends == // ========================= + // Get validator bonds penalty in [0, 1]. + let bonds_penalty: I32F32 = Self::get_float_bonds_penalty( netuid ); + // Calculate weights for bonds, apply bonds penalty to weights. + // bonds_penalty = 0: weights_for_bonds = weights.clone() + // bonds_penalty = 1: weights_for_bonds = clipped_weights.clone() + let weights_for_bonds: Vec> = interpolate( &weights, &clipped_weights, bonds_penalty); + // Access network bonds. let mut bonds: Vec> = Self::get_bonds( netuid ); inplace_mask_matrix( &outdated, &mut bonds ); // mask outdated bonds @@ -162,7 +173,7 @@ impl Pallet { // log::trace!( "B:\n{:?}\n", &bonds ); // Compute bonds delta column normalized. - let mut bonds_delta: Vec> = row_hadamard( &weights, &active_stake ); // ΔB = W◦S + let mut bonds_delta: Vec> = row_hadamard( &weights_for_bonds, &active_stake ); // ΔB = W◦S inplace_col_normalize( &mut bonds_delta ); // sum_i b_ij = 1 // log::trace!( "ΔB:\n{:?}\n", &bonds_delta ); @@ -411,15 +422,18 @@ impl Pallet { let preranks: Vec = matmul_sparse( &weights, &active_stake, n ); // log::trace!( "R (before): {:?}", &preranks ); - // Clip weights at majority consensus - let kappa: I32F32 = Self::get_float_kappa( netuid ); // consensus majority ratio, e.g. 51%. + // Consensus majority ratio, e.g. 51%. + let kappa: I32F32 = Self::get_float_kappa( netuid ); + // Calculate consensus as stake-weighted median of weights. let consensus: Vec = weighted_median_col_sparse( &active_stake, &weights, n, kappa ); log::trace!( "C: {:?}", &consensus ); - weights = col_clip_sparse( &weights, &consensus ); + // Clip weights at majority consensus. + let clipped_weights: Vec> = col_clip_sparse( &weights, &consensus ); // log::trace!( "W: {:?}", &weights ); - let validator_trust: Vec = row_sum_sparse( &weights ); + // Calculate validator trust as sum of clipped weights set by validator. + let validator_trust: Vec = row_sum_sparse( &clipped_weights ); log::trace!( "Tv: {:?}", &validator_trust ); // ============================= @@ -427,7 +441,7 @@ impl Pallet { // ============================= // Compute ranks: r_j = SUM(i) w_ij * s_i. - let mut ranks: Vec = matmul_sparse( &weights, &active_stake, n ); + let mut ranks: Vec = matmul_sparse( &clipped_weights, &active_stake, n ); // log::trace!( "R (after): {:?}", &ranks ); // Compute server trust: ratio of rank after vs. rank before. @@ -442,6 +456,13 @@ impl Pallet { // == Bonds and Dividends == // ========================= + // Get validator bonds penalty in [0, 1]. + let bonds_penalty: I32F32 = Self::get_float_bonds_penalty( netuid ); + // Calculate weights for bonds, apply bonds penalty to weights. + // bonds_penalty = 0: weights_for_bonds = weights.clone() + // bonds_penalty = 1: weights_for_bonds = clipped_weights.clone() + let weights_for_bonds: Vec> = interpolate_sparse( &weights, &clipped_weights, n, bonds_penalty); + // Access network bonds. let mut bonds: Vec> = Self::get_bonds_sparse( netuid ); // log::trace!( "B: {:?}", &bonds ); @@ -455,7 +476,7 @@ impl Pallet { // log::trace!( "B (mask+norm): {:?}", &bonds ); // Compute bonds delta column normalized. - let mut bonds_delta: Vec> = row_hadamard_sparse( &weights, &active_stake ); // ΔB = W◦S (outdated W masked) + let mut bonds_delta: Vec> = row_hadamard_sparse( &weights_for_bonds, &active_stake ); // ΔB = W◦S (outdated W masked) // log::trace!( "ΔB: {:?}", &bonds_delta ); // Normalize bonds delta. @@ -577,6 +598,7 @@ impl Pallet { pub fn get_float_rho( netuid:u16 ) -> I32F32 { I32F32::from_num( Self::get_rho( netuid ) ) } pub fn get_float_kappa( netuid:u16 ) -> I32F32 { I32F32::from_num( Self::get_kappa( netuid ) ) / I32F32::from_num( u16::MAX ) } + pub fn get_float_bonds_penalty( netuid:u16 ) -> I32F32 { I32F32::from_num( Self::get_bonds_penalty( netuid ) ) / I32F32::from_num( u16::MAX ) } pub fn get_normalized_stake( netuid:u16 ) -> Vec { let n: usize = Self::get_subnetwork_n( netuid ) as usize; diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index d614781605..fcf6ef00b8 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -131,6 +131,8 @@ pub mod pallet { type InitialAdjustmentInterval: Get; #[pallet::constant] // Initial bonds moving average. type InitialBondsMovingAverage: Get; + #[pallet::constant] // Initial bonds penalty. + type InitialBondsPenalty: Get; #[pallet::constant] // Initial target registrations per interval. type InitialTargetRegistrationsPerInterval: Get; #[pallet::constant] // Rho constant. @@ -530,6 +532,10 @@ pub mod pallet { T::InitialBondsMovingAverage::get() } #[pallet::type_value] + pub fn DefaultBondsPenalty() -> u16 { + T::InitialBondsPenalty::get() + } + #[pallet::type_value] pub fn DefaultValidatorPruneLen() -> u64 { T::InitialValidatorPruneLen::get() } @@ -587,6 +593,9 @@ pub mod pallet { #[pallet::storage] // --- MAP ( netuid ) --> bonds_moving_average pub type BondsMovingAverage = StorageMap<_, Identity, u16, u64, ValueQuery, DefaultBondsMovingAverage>; + #[pallet::storage] // --- MAP ( netuid ) --> bonds_penalty + pub type BondsPenalty = + StorageMap<_, Identity, u16, u16, ValueQuery, DefaultBondsPenalty>; #[pallet::storage] // --- MAP ( netuid ) --> weights_set_rate_limit pub type WeightsSetRateLimit = StorageMap<_, Identity, u16, u64, ValueQuery, DefaultWeightsSetRateLimit>; @@ -739,6 +748,7 @@ pub mod pallet { WeightsSetRateLimitSet(u16, u64), // --- Event created when weights set rate limit has been set for a subnet. ImmunityPeriodSet(u16, u16), // --- Event created when immunity period is set for a subnet. BondsMovingAverageSet(u16, u64), // --- Event created when bonds moving average is set for a subnet. + BondsPenaltySet(u16, u16), // --- Event created when bonds penalty is set for a subnet. MaxAllowedValidatorsSet(u16, u16), // --- Event created when setting the max number of allowed validators on a subnet. AxonServed(u16, T::AccountId), // --- Event created when the axon server information is added to the network. PrometheusServed(u16, T::AccountId), // --- Event created when the prometheus server information is added to the network. @@ -1959,6 +1969,18 @@ pub mod pallet { ) -> DispatchResult { Self::do_sudo_set_adjustment_alpha(origin, netuid, adjustment_alpha) } + + #[pallet::call_index(59)] + #[pallet::weight((Weight::from_ref_time(14_000_000) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)), DispatchClass::Operational, Pays::No))] + pub fn sudo_set_bonds_penalty( + origin: OriginFor, + netuid: u16, + bonds_penalty: u16, + ) -> DispatchResult { + Self::do_sudo_set_bonds_penalty(origin, netuid, bonds_penalty) + } } // ---- Subtensor helper functions. diff --git a/pallets/subtensor/src/math.rs b/pallets/subtensor/src/math.rs index 4da8014bb5..a7b7bd9733 100644 --- a/pallets/subtensor/src/math.rs +++ b/pallets/subtensor/src/math.rs @@ -769,6 +769,58 @@ pub fn weighted_median_col_sparse( stake: &Vec, score: &Vec>, mat2: &Vec>, ratio: I32F32 ) -> Vec> { + if ratio == I32F32::from_num(0) { return mat1.clone(); } + if ratio == I32F32::from_num(1) { return mat2.clone(); } + assert!( mat1.len() == mat2.len() ); + if mat1.len() == 0 { return vec![vec![];1] } + if mat1[0].len() == 0 { return vec![vec![];1] } + let mut result: Vec> = vec![ vec![ I32F32::from_num( 0 ); mat1[0].len() ]; mat1.len() ]; + for i in 0..mat1.len() { + assert!(mat1[i].len() == mat2[i].len()); + for j in 0..mat1[i].len() { + result[i][j] = mat1[i][j] + ratio * (mat2[i][j] - mat1[i][j]); + } + } + result +} + +// Element-wise interpolation of two sparse matrices: Result = A + ratio * (B - A). +// ratio is has intended range [0, 1] +// ratio=0: Result = A +// ratio=1: Result = B +#[allow(dead_code)] +pub fn interpolate_sparse( mat1: &Vec>, mat2: &Vec>, columns: u16, ratio: I32F32 ) -> Vec> { + if ratio == I32F32::from_num(0) { return mat1.clone(); } + if ratio == I32F32::from_num(1) { return mat2.clone(); } + assert!(mat1.len() == mat2.len()); + let rows = mat1.len(); + let zero: I32F32 = I32F32::from_num( 0 ); + let mut result: Vec> = vec![ vec![]; rows ]; + for i in 0..rows { + let mut row1: Vec = vec![ zero; columns as usize ]; + for (j, value) in mat1[i].iter() { + row1[*j as usize] = *value; + } + let mut row2: Vec = vec![ zero; columns as usize ]; + for (j, value) in mat2[i].iter() { + row2[*j as usize] = *value; + } + for j in 0..columns as usize { + let interp: I32F32 = row1[j] + ratio * (row2[j] - row1[j]); + if zero < interp { + result[i].push( (j as u16, interp) ) + } + } + } + result +} + // Element-wise product of two matrices. #[allow(dead_code)] pub fn hadamard( mat1: &Vec>, mat2: &Vec> ) -> Vec> { @@ -2318,6 +2370,264 @@ mod tests { assert_eq!(median, weighted_median_col_sparse(&stake, &weights, 3, fixed(0.51))); } + #[test] + fn test_math_interpolate() { + let mat1: Vec> = vec![ vec![] ]; + let mat2: Vec> = vec![ vec![] ]; + let target: Vec> = vec![ vec![] ]; + let ratio = I32F32::from_num(0); + let result = interpolate(&mat1, &mat2, ratio); + assert_mat_compare(&result, &target, I32F32::from_num( 0 )); + + let mat1: Vec> = vec![ vec![ I32F32::from_num(0) ] ]; + let mat2: Vec> = vec![ vec![ I32F32::from_num(1) ] ]; + let target: Vec> = vec![ vec![ I32F32::from_num(0) ] ]; + let ratio = I32F32::from_num(0); + let result = interpolate(&mat1, &mat2, ratio); + assert_mat_compare(&result, &target, I32F32::from_num( 0 )); + + let target: Vec> = vec![ vec![ I32F32::from_num(1) ] ]; + let ratio = I32F32::from_num(1); + let result = interpolate(&mat1, &mat2, ratio); + assert_mat_compare(&result, &target, I32F32::from_num( 0 )); + + let mat1: Vec = vec![ 0., 0., 0., + 0., 0., 0., + 0., 0., 0., + 0., 0., 0.]; + let mat2: Vec = vec![ 0., 0., 0., + 0., 0., 0., + 0., 0., 0., + 0., 0., 0.]; + let target: Vec = vec![0., 0., 0., + 0., 0., 0., + 0., 0., 0., + 0., 0., 0.]; + let mat1 = vec_to_mat_fixed(&mat1, 4, false); + let mat2 = vec_to_mat_fixed(&mat2, 4, false); + let ratio = I32F32::from_num(0); + let target = vec_to_mat_fixed(&target, 4, false); + let result = interpolate(&mat1, &mat2, ratio); + assert_mat_compare(&result, &target, I32F32::from_num( 0 )); + + let ratio = I32F32::from_num(1); + let result = interpolate(&mat1, &mat2, ratio); + assert_mat_compare(&result, &target, I32F32::from_num( 0 )); + + let mat1: Vec = vec![ 1., 10., + 100., 1000., + 10000., 100000.]; + let mat2: Vec = vec![ 10., 100., + 1000., 10000., + 100000., 1000000.]; + let target: Vec = vec![1., 10., + 100., 1000., + 10000., 100000.]; + let mat1 = vec_to_mat_fixed(&mat1, 3, false); + let mat2 = vec_to_mat_fixed(&mat2, 3, false); + let ratio = I32F32::from_num(0); + let target = vec_to_mat_fixed(&target, 3, false); + let result = interpolate(&mat1, &mat2, ratio); + assert_mat_compare(&result, &target, I32F32::from_num( 0 )); + + let target: Vec = vec![9.1, 91., + 910., 9100., + 91000., 910000.]; + let ratio = I32F32::from_num(0.9); + let target = vec_to_mat_fixed(&target, 3, false); + let result = interpolate(&mat1, &mat2, ratio); + assert_mat_compare(&result, &target, I32F32::from_num( 0.0001 )); + + let mat1: Vec = vec![ 0., 0., 0., + 0., 0., 0., + 0., 0., 0., + 0., 0., 0.]; + let mat2: Vec = vec![ 1., 1., 1., + 1., 1., 1., + 1., 1., 1., + 1., 1., 1.]; + let target: Vec = vec![0., 0., 0., + 0., 0., 0., + 0., 0., 0., + 0., 0., 0.]; + let mat1 = vec_to_mat_fixed(&mat1, 4, false); + let mat2 = vec_to_mat_fixed(&mat2, 4, false); + let ratio = I32F32::from_num(0); + let target = vec_to_mat_fixed(&target, 4, false); + let result = interpolate(&mat1, &mat2, ratio); + assert_mat_compare(&result, &target, I32F32::from_num( 0 )); + + let target: Vec = vec![0.000000001, 0.000000001, 0.000000001, + 0.000000001, 0.000000001, 0.000000001, + 0.000000001, 0.000000001, 0.000000001, + 0.000000001, 0.000000001, 0.000000001]; + let ratio = I32F32::from_num(0.000000001); + let target = vec_to_mat_fixed(&target, 4, false); + let result = interpolate(&mat1, &mat2, ratio); + assert_mat_compare(&result, &target, I32F32::from_num( 0 )); + + let target: Vec = vec![0.5, 0.5, 0.5, + 0.5, 0.5, 0.5, + 0.5, 0.5, 0.5, + 0.5, 0.5, 0.5]; + let ratio = I32F32::from_num(0.5); + let target = vec_to_mat_fixed(&target, 4, false); + let result = interpolate(&mat1, &mat2, ratio); + assert_mat_compare(&result, &target, I32F32::from_num( 0 )); + + let target: Vec = vec![0.9999998808, 0.9999998808, 0.9999998808, + 0.9999998808, 0.9999998808, 0.9999998808, + 0.9999998808, 0.9999998808, 0.9999998808, + 0.9999998808, 0.9999998808, 0.9999998808]; + let ratio = I32F32::from_num(0.9999998808); + let target = vec_to_mat_fixed(&target, 4, false); + let result = interpolate(&mat1, &mat2, ratio); + assert_mat_compare(&result, &target, I32F32::from_num( 0 )); + + let target: Vec = vec![1., 1., 1., + 1., 1., 1., + 1., 1., 1., + 1., 1., 1.]; + let ratio = I32F32::from_num(1); + let target = vec_to_mat_fixed(&target, 4, false); + let result = interpolate(&mat1, &mat2, ratio); + assert_mat_compare(&result, &target, I32F32::from_num( 0 )); + } + + #[test] + fn test_math_interpolate_sparse() { + let mat1: Vec> = vec![ vec![] ]; + let mat2: Vec> = vec![ vec![] ]; + let target: Vec> = vec![ vec![] ]; + let ratio = I32F32::from_num(0); + let result = interpolate_sparse(&mat1, &mat2, 0, ratio); + assert_sparse_mat_compare(&result, &target, I32F32::from_num( 0 )); + + let mat1: Vec = vec![ 0. ]; + let mat2: Vec = vec![ 1. ]; + let target: Vec = vec![0.]; + let mat1 = vec_to_sparse_mat_fixed(&mat1, 1, false); + let mat2 = vec_to_sparse_mat_fixed(&mat2, 1, false); + let ratio = I32F32::from_num(0); + let target = vec_to_sparse_mat_fixed(&target, 1, false); + let result = interpolate_sparse(&mat1, &mat2, 1, ratio); + assert_sparse_mat_compare(&result, &target, I32F32::from_num( 0 )); + + let target: Vec = vec![0.5]; + let ratio = I32F32::from_num(0.5); + let target = vec_to_sparse_mat_fixed(&target, 1, false); + let result = interpolate_sparse(&mat1, &mat2, 1, ratio); + assert_sparse_mat_compare(&result, &target, I32F32::from_num( 0 )); + + let target: Vec = vec![1.]; + let ratio = I32F32::from_num(1); + let target = vec_to_sparse_mat_fixed(&target, 1, false); + let result = interpolate_sparse(&mat1, &mat2, 1, ratio); + assert_sparse_mat_compare(&result, &target, I32F32::from_num( 0 )); + + let mat1: Vec = vec![ 0., 0., 0., + 0., 0., 0., + 0., 0., 0., + 0., 0., 0.]; + let mat2: Vec = vec![ 0., 0., 0., + 0., 0., 0., + 0., 0., 0., + 0., 0., 0.]; + let target: Vec = vec![0., 0., 0., + 0., 0., 0., + 0., 0., 0., + 0., 0., 0.]; + let mat1 = vec_to_sparse_mat_fixed(&mat1, 4, false); + let mat2 = vec_to_sparse_mat_fixed(&mat2, 4, false); + let ratio = I32F32::from_num(0); + let target = vec_to_sparse_mat_fixed(&target, 4, false); + let result = interpolate_sparse(&mat1, &mat2, 3, ratio); + assert_sparse_mat_compare(&result, &target, I32F32::from_num( 0 )); + + let ratio = I32F32::from_num(1); + let result = interpolate_sparse(&mat1, &mat2, 3, ratio); + assert_sparse_mat_compare(&result, &target, I32F32::from_num( 0 )); + + let mat1: Vec = vec![ 1., 0., + 100., 1000., + 10000., 100000.]; + let mat2: Vec = vec![ 10., 100., + 1000., 10000., + 100000., 0.]; + let target: Vec = vec![1., 0., + 100., 1000., + 10000., 100000.]; + let mat1 = vec_to_sparse_mat_fixed(&mat1, 3, false); + let mat2 = vec_to_sparse_mat_fixed(&mat2, 3, false); + let ratio = I32F32::from_num(0); + let target = vec_to_sparse_mat_fixed(&target, 3, false); + let result = interpolate_sparse(&mat1, &mat2, 2, ratio); + assert_sparse_mat_compare(&result, &target, I32F32::from_num( 0 )); + + let target: Vec = vec![9.1, 90., + 910., 9100., + 91000., 10000.]; + let ratio = I32F32::from_num(0.9); + let target = vec_to_sparse_mat_fixed(&target, 3, false); + let result = interpolate_sparse(&mat1, &mat2, 2, ratio); + assert_sparse_mat_compare(&result, &target, I32F32::from_num( 0.0001 )); + + let mat1: Vec = vec![ 0., 0., 0., + 0., 0., 0., + 0., 0., 0., + 0., 0., 0.]; + let mat2: Vec = vec![ 1., 1., 1., + 1., 1., 1., + 1., 1., 1., + 1., 1., 1.]; + let target: Vec = vec![0., 0., 0., + 0., 0., 0., + 0., 0., 0., + 0., 0., 0.]; + let mat1 = vec_to_sparse_mat_fixed(&mat1, 4, false); + let mat2 = vec_to_sparse_mat_fixed(&mat2, 4, false); + let ratio = I32F32::from_num(0); + let target = vec_to_sparse_mat_fixed(&target, 4, false); + let result = interpolate_sparse(&mat1, &mat2, 3, ratio); + assert_sparse_mat_compare(&result, &target, I32F32::from_num( 0 )); + + let target: Vec = vec![0.000000001, 0.000000001, 0.000000001, + 0.000000001, 0.000000001, 0.000000001, + 0.000000001, 0.000000001, 0.000000001, + 0.000000001, 0.000000001, 0.000000001]; + let ratio = I32F32::from_num(0.000000001); + let target = vec_to_sparse_mat_fixed(&target, 4, false); + let result = interpolate_sparse(&mat1, &mat2, 3, ratio); + assert_sparse_mat_compare(&result, &target, I32F32::from_num( 0 )); + + let target: Vec = vec![0.5, 0.5, 0.5, + 0.5, 0.5, 0.5, + 0.5, 0.5, 0.5, + 0.5, 0.5, 0.5]; + let ratio = I32F32::from_num(0.5); + let target = vec_to_sparse_mat_fixed(&target, 4, false); + let result = interpolate_sparse(&mat1, &mat2, 3, ratio); + assert_sparse_mat_compare(&result, &target, I32F32::from_num( 0 )); + + let target: Vec = vec![0.9999998808, 0.9999998808, 0.9999998808, + 0.9999998808, 0.9999998808, 0.9999998808, + 0.9999998808, 0.9999998808, 0.9999998808, + 0.9999998808, 0.9999998808, 0.9999998808]; + let ratio = I32F32::from_num(0.9999998808); + let target = vec_to_sparse_mat_fixed(&target, 4, false); + let result = interpolate_sparse(&mat1, &mat2, 3, ratio); + assert_sparse_mat_compare(&result, &target, I32F32::from_num( 0 )); + + let target: Vec = vec![1., 1., 1., + 1., 1., 1., + 1., 1., 1., + 1., 1., 1.]; + let ratio = I32F32::from_num(1); + let target = vec_to_sparse_mat_fixed(&target, 4, false); + let result = interpolate_sparse(&mat1, &mat2, 3, ratio); + assert_sparse_mat_compare(&result, &target, I32F32::from_num( 0 )); + } + #[test] fn test_math_hadamard() { let mat2: Vec = vec![ 1., 2., 3., diff --git a/pallets/subtensor/src/utils.rs b/pallets/subtensor/src/utils.rs index 4b39e7b01f..d5aece75f2 100644 --- a/pallets/subtensor/src/utils.rs +++ b/pallets/subtensor/src/utils.rs @@ -404,6 +404,17 @@ impl Pallet { Ok(()) } + pub fn get_bonds_penalty( netuid: u16 ) -> u16 { BondsPenalty::::get( netuid ) } + pub fn set_bonds_penalty( netuid: u16, bonds_penalty: u16 ) { BondsPenalty::::insert( netuid, bonds_penalty ); } + pub fn do_sudo_set_bonds_penalty( origin:T::RuntimeOrigin, netuid: u16, bonds_penalty: u16 ) -> DispatchResult { + ensure_root( origin )?; + ensure!(Self::if_subnet_exist(netuid), Error::::NetworkDoesNotExist); + Self::set_bonds_penalty( netuid, bonds_penalty ); + log::info!("BondsPenaltySet( netuid: {:?} bonds_penalty: {:?} ) ", netuid, bonds_penalty ); + Self::deposit_event( Event::BondsPenaltySet( netuid, bonds_penalty ) ); + Ok(()) + } + pub fn get_max_registrations_per_block( netuid: u16 ) -> u16 { MaxRegistrationsPerBlock::::get( netuid ) } pub fn set_max_registrations_per_block( netuid: u16, max_registrations_per_block: u16 ) { MaxRegistrationsPerBlock::::insert( netuid, max_registrations_per_block ); } pub fn do_sudo_set_max_registrations_per_block( diff --git a/pallets/subtensor/tests/epoch.rs b/pallets/subtensor/tests/epoch.rs index 8e5122d2fe..6e98c505bd 100644 --- a/pallets/subtensor/tests/epoch.rs +++ b/pallets/subtensor/tests/epoch.rs @@ -155,9 +155,11 @@ fn init_run_epochs( random_weights: bool, random_seed: u64, sparse: bool, + bonds_penalty: u16 ) { // === Create the network add_network(netuid, u16::MAX - 1, 0); // set higher tempo to avoid built-in epoch, then manual epoch instead + SubtensorModule::set_bonds_penalty(netuid, bonds_penalty); // === Register uids SubtensorModule::set_max_allowed_uids(netuid, n); @@ -408,21 +410,25 @@ fn test_consensus_guarantees() { let epochs: u16 = 1; let interleave = 2; log::info!("test_consensus_guarantees ({network_n:?}, {validators_n:?} validators)"); - for (major_stake, major_weight, minor_weight, weight_stddev) in vec![ - (0.51, 1., 1., 0.001), - (0.51, 0.03, 0., 0.001), - (0.51, 0.51, 0.49, 0.001), - (0.51, 0.51, 1., 0.001), - (0.51, 0.61, 0.8, 0.1), - (0.6, 0.67, 0.65, 0.2), - (0.6, 0.74, 0.77, 0.4), - (0.6, 0.76, 0.8, 0.4), - (0.6, 0.76, 1., 0.4), - (0.6, 0.92, 1., 0.4), - (0.6, 0.94, 1., 0.4), - (0.65, 0.78, 0.85, 0.6), - (0.7, 0.81, 0.85, 0.8), - (0.7, 0.83, 0.85, 1.), + for (major_stake, major_weight, minor_weight, weight_stddev, bonds_penalty) in vec![ + (0.51, 1., 1., 0.001, u16::MAX), + (0.51, 0.03, 0., 0.001, u16::MAX), + (0.51, 0.51, 0.49, 0.001, u16::MAX), + (0.51, 0.51, 1., 0.001, u16::MAX), + (0.51, 0.61, 0.8, 0.1, u16::MAX), + (0.6, 0.67, 0.65, 0.2, u16::MAX), + (0.6, 0.74, 0.77, 0.4, u16::MAX), + (0.6, 0.76, 0.8, 0.4, u16::MAX), + (0.6, 0.73, 1., 0.4, u16::MAX), // bonds_penalty = 100% + (0.6, 0.74, 1., 0.4, 55800), // bonds_penalty = 85% + (0.6, 0.76, 1., 0.4, 43690), // bonds_penalty = 66% + (0.6, 0.78, 1., 0.4, 21845), // bonds_penalty = 33% + (0.6, 0.79, 1., 0.4, 0), // bonds_penalty = 0% + (0.6, 0.92, 1., 0.4, u16::MAX), + (0.6, 0.94, 1., 0.4, u16::MAX), + (0.65, 0.78, 0.85, 0.6, u16::MAX), + (0.7, 0.81, 0.85, 0.8, u16::MAX), + (0.7, 0.83, 0.85, 1., u16::MAX), ] { let ( validators, @@ -460,6 +466,7 @@ fn test_consensus_guarantees() { false, 0, false, + bonds_penalty ); let mut major_emission: I64F64 = I64F64::from_num(0); @@ -689,6 +696,7 @@ fn test_512_graph() { false, 0, false, + u16::MAX ); let bonds = SubtensorModule::get_bonds(netuid); for uid in validators { @@ -734,95 +742,99 @@ fn test_512_graph_random_weights() { let epochs: u16 = 1; log::info!("test_{network_n:?}_graph_random_weights ({validators_n:?} validators)"); for interleave in 0..3 { + // server-self weight off/on for server_self in vec![false, true] { - // server-self weight off/on - let (validators, servers) = distribute_nodes( - validators_n as usize, - network_n as usize, - interleave as usize, - ); - let server: usize = servers[0] as usize; - let validator: usize = validators[0] as usize; - let (mut rank, mut incentive, mut dividend, mut emission, mut bondv, mut bonds): ( - Vec, - Vec, - Vec, - Vec, - Vec, - Vec, - ) = (vec![], vec![], vec![], vec![], vec![], vec![]); - - // Dense epoch - new_test_ext().execute_with(|| { - init_run_epochs( - netuid, - network_n, - &validators, - &servers, - epochs, - 1, - server_self, - &vec![], - false, - &vec![], - false, - true, - interleave as u64, - false, + for bonds_penalty in vec![0, u16::MAX/2, u16::MAX] { + let (validators, servers) = distribute_nodes( + validators_n as usize, + network_n as usize, + interleave as usize, ); + let server: usize = servers[0] as usize; + let validator: usize = validators[0] as usize; + let (mut rank, mut incentive, mut dividend, mut emission, mut bondv, mut bonds): ( + Vec, + Vec, + Vec, + Vec, + Vec, + Vec, + ) = (vec![], vec![], vec![], vec![], vec![], vec![]); + + // Dense epoch + new_test_ext().execute_with(|| { + init_run_epochs( + netuid, + network_n, + &validators, + &servers, + epochs, + 1, + server_self, + &vec![], + false, + &vec![], + false, + true, + interleave as u64, + false, + bonds_penalty + ); - let bond = SubtensorModule::get_bonds(netuid); - for uid in 0..network_n { - rank.push(SubtensorModule::get_rank_for_uid(netuid, uid)); - incentive.push(SubtensorModule::get_incentive_for_uid(netuid, uid)); - dividend.push(SubtensorModule::get_dividends_for_uid(netuid, uid)); - emission.push(SubtensorModule::get_emission_for_uid(netuid, uid)); - bondv.push(bond[uid as usize][validator]); - bonds.push(bond[uid as usize][server]); - } - }); + let bond = SubtensorModule::get_bonds(netuid); + for uid in 0..network_n { + rank.push(SubtensorModule::get_rank_for_uid(netuid, uid)); + incentive.push(SubtensorModule::get_incentive_for_uid(netuid, uid)); + dividend.push(SubtensorModule::get_dividends_for_uid(netuid, uid)); + emission.push(SubtensorModule::get_emission_for_uid(netuid, uid)); + bondv.push(bond[uid as usize][validator]); + bonds.push(bond[uid as usize][server]); + } + }); - // Sparse epoch (same random seed as dense) - new_test_ext().execute_with(|| { - init_run_epochs( - netuid, - network_n, - &validators, - &servers, - epochs, - 1, - server_self, - &vec![], - false, - &vec![], - false, - true, - interleave as u64, - true, - ); - // Assert that dense and sparse epoch results are equal - let bond = SubtensorModule::get_bonds(netuid); - for uid in 0..network_n { - assert_eq!( - SubtensorModule::get_rank_for_uid(netuid, uid), - rank[uid as usize] - ); - assert_eq!( - SubtensorModule::get_incentive_for_uid(netuid, uid), - incentive[uid as usize] - ); - assert_eq!( - SubtensorModule::get_dividends_for_uid(netuid, uid), - dividend[uid as usize] - ); - assert_eq!( - SubtensorModule::get_emission_for_uid(netuid, uid), - emission[uid as usize] + // Sparse epoch (same random seed as dense) + new_test_ext().execute_with(|| { + init_run_epochs( + netuid, + network_n, + &validators, + &servers, + epochs, + 1, + server_self, + &vec![], + false, + &vec![], + false, + true, + interleave as u64, + true, + bonds_penalty ); - assert_eq!(bond[uid as usize][validator], bondv[uid as usize]); - assert_eq!(bond[uid as usize][server], bonds[uid as usize]); - } - }); + // Assert that dense and sparse epoch results are equal + let bond = SubtensorModule::get_bonds(netuid); + for uid in 0..network_n { + assert_eq!( + SubtensorModule::get_rank_for_uid(netuid, uid), + rank[uid as usize] + ); + assert_eq!( + SubtensorModule::get_incentive_for_uid(netuid, uid), + incentive[uid as usize] + ); + assert_eq!( + SubtensorModule::get_dividends_for_uid(netuid, uid), + dividend[uid as usize] + ); + assert_eq!( + SubtensorModule::get_emission_for_uid(netuid, uid), + emission[uid as usize] + ); + assert_eq!(bond[uid as usize][validator], bondv[uid as usize]); + assert_eq!(bond[uid as usize][server], bonds[uid as usize]); + } + }); + } } } } @@ -863,6 +875,7 @@ fn test_4096_graph() { false, 0, true, + u16::MAX ); assert_eq!(SubtensorModule::get_total_stake(), 21_000_000_000_000_000); let bonds = SubtensorModule::get_bonds(netuid); @@ -930,6 +943,7 @@ fn test_16384_graph_sparse() { false, 0, true, + u16::MAX ); let bonds = SubtensorModule::get_bonds(netuid); for uid in validators { @@ -983,6 +997,7 @@ fn test_bonds() { SubtensorModule::set_max_registrations_per_block( netuid, n ); SubtensorModule::set_target_registrations_per_interval(netuid, n); SubtensorModule::set_weights_set_rate_limit( netuid, 0 ); + SubtensorModule::set_bonds_penalty(netuid, u16::MAX); // === Register [validator1, validator2, validator3, validator4, server1, server2, server3, server4] for key in 0..n as u64 { @@ -1479,6 +1494,7 @@ fn test_outdated_weights() { SubtensorModule::set_max_allowed_uids(netuid, n); SubtensorModule::set_weights_set_rate_limit(netuid, 0); SubtensorModule::set_max_registrations_per_block(netuid, n + 1); // should be n, but RegistrationsThisBlock is not reset (TODO: Saeideh) + SubtensorModule::set_bonds_penalty(netuid, u16::MAX); // === Register [validator1, validator2, server1, server2] for key in 0..n as u64 { @@ -2014,6 +2030,7 @@ fn _map_consensus_guarantees() { let epochs: u16 = 1; let interleave = 0; let weight_stddev: I32F32 = fixed(0.4); + let bonds_penalty: u16 = u16::MAX; println!("["); for _major_stake in vec![0.51, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 0.99] { let major_stake: I32F32 = I32F32::from_num(_major_stake); @@ -2043,7 +2060,7 @@ fn _map_consensus_guarantees() { ); new_test_ext().execute_with(|| { - init_run_epochs(netuid, network_n, &validators, &servers, epochs, 1, true, &stake, true, &weights, true, false, 0, true); + init_run_epochs(netuid, network_n, &validators, &servers, epochs, 1, true, &stake, true, &weights, true, false, 0, true, bonds_penalty); let mut major_emission: I64F64 = I64F64::from_num(0); let mut minor_emission: I64F64 = I64F64::from_num(0); diff --git a/pallets/subtensor/tests/mock.rs b/pallets/subtensor/tests/mock.rs index fdf84e220b..ad95de38e4 100644 --- a/pallets/subtensor/tests/mock.rs +++ b/pallets/subtensor/tests/mock.rs @@ -125,6 +125,7 @@ parameter_types! { pub const InitialImmunityPeriod: u16 = 2; pub const InitialMaxAllowedUids: u16 = 2; pub const InitialBondsMovingAverage: u64 = 900_000; + pub const InitialBondsPenalty: u16 = 0; pub const InitialStakePruningMin: u16 = 0; pub const InitialFoundationDistribution: u64 = 0; pub const InitialDefaultTake: u16 = 11_796; // 18% honest number. @@ -332,6 +333,7 @@ impl pallet_subtensor::Config for Test { type InitialMaxRegistrationsPerBlock = InitialMaxRegistrationsPerBlock; type InitialPruningScore = InitialPruningScore; type InitialBondsMovingAverage = InitialBondsMovingAverage; + type InitialBondsPenalty = InitialBondsPenalty; type InitialMaxAllowedValidators = InitialMaxAllowedValidators; type InitialDefaultTake = InitialDefaultTake; type InitialWeightsVersionKey = InitialWeightsVersionKey; diff --git a/pallets/subtensor/tests/sudo.rs b/pallets/subtensor/tests/sudo.rs index 00b5e64224..0dfe7557da 100644 --- a/pallets/subtensor/tests/sudo.rs +++ b/pallets/subtensor/tests/sudo.rs @@ -28,6 +28,7 @@ fn test_defaults() { assert_eq!(SubtensorModule::get_min_allowed_weights(netuid), 0); assert_eq!(SubtensorModule::get_adjustment_interval(netuid), 100); assert_eq!(SubtensorModule::get_bonds_moving_average(netuid), 900_000); + assert_eq!(SubtensorModule::get_bonds_penalty(netuid), 0); assert_eq!(SubtensorModule::get_last_adjustment_block(netuid), 0); assert_eq!(SubtensorModule::get_last_mechanism_step_block(netuid), 0); assert_eq!(SubtensorModule::get_blocks_since_last_step(netuid), 0); @@ -931,6 +932,42 @@ fn test_sudo_set_bonds_moving_average() { }); } +#[test] +fn test_sudo_set_bonds_penalty() { + new_test_ext().execute_with(|| { + let netuid: u16 = 1; + let to_be_set: u16 = 10; + let init_value: u16 = SubtensorModule::get_bonds_penalty(netuid); + add_network(netuid, 10, 0); + assert_eq!( + SubtensorModule::sudo_set_bonds_penalty( + <::RuntimeOrigin>::signed(U256::from(0)), + netuid, + to_be_set + ), + Err(DispatchError::BadOrigin.into()) + ); + assert_eq!( + SubtensorModule::sudo_set_bonds_penalty( + <::RuntimeOrigin>::root(), + netuid + 1, + to_be_set + ), + Err(Error::::NetworkDoesNotExist.into()) + ); + assert_eq!( + SubtensorModule::get_bonds_penalty(netuid), + init_value + ); + assert_ok!(SubtensorModule::sudo_set_bonds_penalty( + <::RuntimeOrigin>::root(), + netuid, + to_be_set + )); + assert_eq!(SubtensorModule::get_bonds_penalty(netuid), to_be_set); + }); +} + #[test] fn test_sudo_set_network_connection_requirement() { new_test_ext().execute_with(|| { diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 55cc1813a0..3561ec603f 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -575,6 +575,7 @@ parameter_types! { pub const SubtensorInitialMaxRegistrationsPerBlock: u16 = 1; pub const SubtensorInitialPruningScore : u16 = u16::MAX; pub const SubtensorInitialBondsMovingAverage: u64 = 900_000; + pub const SubtensorInitialBondsPenalty: u16 = 0; pub const SubtensorInitialDefaultTake: u16 = 11_796; // 18% honest number. pub const SubtensorInitialWeightsVersionKey: u64 = 0; pub const SubtensorInitialMinDifficulty: u64 = 10_000_000; @@ -600,6 +601,7 @@ impl pallet_subtensor::Config for Runtime { type InitialKappa = SubtensorInitialKappa; type InitialMaxAllowedUids = SubtensorInitialMaxAllowedUids; type InitialBondsMovingAverage = SubtensorInitialBondsMovingAverage; + type InitialBondsPenalty = SubtensorInitialBondsPenalty; type InitialIssuance = SubtensorInitialIssuance; type InitialMinAllowedWeights = SubtensorInitialMinAllowedWeights; type InitialEmissionValue = SubtensorInitialEmissionValue;