diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs
index 8987439db8..bc889e002b 100644
--- a/substrate/bin/node/runtime/src/lib.rs
+++ b/substrate/bin/node/runtime/src/lib.rs
@@ -24,7 +24,7 @@
use codec::{Decode, Encode, MaxEncodedLen};
use frame_election_provider_support::{
- onchain, ElectionDataProvider, ExtendedBalance, SequentialPhragmen, VoteWeight,
+ onchain, BalancingConfig, ElectionDataProvider, SequentialPhragmen, VoteWeight,
};
use frame_support::{
construct_runtime,
@@ -630,10 +630,10 @@ pub const MINER_MAX_ITERATIONS: u32 = 10;
/// A source of random balance for NposSolver, which is meant to be run by the OCW election miner.
pub struct OffchainRandomBalancing;
-impl Get> for OffchainRandomBalancing {
- fn get() -> Option<(usize, ExtendedBalance)> {
+impl Get > for OffchainRandomBalancing {
+ fn get() -> Option {
use sp_runtime::traits::TrailingZeroInput;
- let iters = match MINER_MAX_ITERATIONS {
+ let iterations = match MINER_MAX_ITERATIONS {
0 => 0,
max => {
let seed = sp_io::offchain::random_seed();
@@ -644,7 +644,8 @@ impl Get> for OffchainRandomBalancing {
},
};
- Some((iters, 0))
+ let config = BalancingConfig { iterations, tolerance: 0 };
+ Some(config)
}
}
diff --git a/substrate/frame/election-provider-multi-phase/src/lib.rs b/substrate/frame/election-provider-multi-phase/src/lib.rs
index 7d8559050f..2f1f6463df 100644
--- a/substrate/frame/election-provider-multi-phase/src/lib.rs
+++ b/substrate/frame/election-provider-multi-phase/src/lib.rs
@@ -1808,7 +1808,7 @@ mod tests {
};
use frame_election_provider_support::ElectionProvider;
use frame_support::{assert_noop, assert_ok};
- use sp_npos_elections::Support;
+ use sp_npos_elections::{BalancingConfig, Support};
#[test]
fn phase_rotation_works() {
@@ -2163,7 +2163,7 @@ mod tests {
assert_eq!(MultiPhase::current_phase(), Phase::Signed);
// set the solution balancing to get the desired score.
- crate::mock::Balancing::set(Some((2, 0)));
+ crate::mock::Balancing::set(Some(BalancingConfig { iterations: 2, tolerance: 0 }));
let (solution, _) = MultiPhase::mine_solution().unwrap();
// Default solution's score.
diff --git a/substrate/frame/election-provider-multi-phase/src/mock.rs b/substrate/frame/election-provider-multi-phase/src/mock.rs
index bbc2d6d43b..7eff70b47e 100644
--- a/substrate/frame/election-provider-multi-phase/src/mock.rs
+++ b/substrate/frame/election-provider-multi-phase/src/mock.rs
@@ -39,8 +39,8 @@ use sp_core::{
H256,
};
use sp_npos_elections::{
- assignment_ratio_to_staked_normalized, seq_phragmen, to_supports, ElectionResult,
- EvaluateSupport, ExtendedBalance,
+ assignment_ratio_to_staked_normalized, seq_phragmen, to_supports, BalancingConfig,
+ ElectionResult, EvaluateSupport,
};
use sp_runtime::{
testing::Header,
@@ -324,7 +324,7 @@ impl InstantElectionProvider for MockFallback {
}
parameter_types! {
- pub static Balancing: Option<(usize, ExtendedBalance)> = Some((0, 0));
+ pub static Balancing: Option = Some( BalancingConfig { iterations: 0, tolerance: 0 } );
}
pub struct TestBenchmarkingConfig;
diff --git a/substrate/frame/election-provider-support/src/lib.rs b/substrate/frame/election-provider-support/src/lib.rs
index 27bf7a37f9..eee865d0b7 100644
--- a/substrate/frame/election-provider-support/src/lib.rs
+++ b/substrate/frame/election-provider-support/src/lib.rs
@@ -177,8 +177,8 @@ pub use frame_support::{traits::Get, weights::Weight, BoundedVec, RuntimeDebug};
/// Re-export some type as they are used in the interface.
pub use sp_arithmetic::PerThing;
pub use sp_npos_elections::{
- Assignment, ElectionResult, Error, ExtendedBalance, IdentifierT, PerThing128, Support,
- Supports, VoteWeight,
+ Assignment, BalancingConfig, ElectionResult, Error, ExtendedBalance, IdentifierT, PerThing128,
+ Support, Supports, VoteWeight,
};
pub use traits::NposSolution;
@@ -568,11 +568,8 @@ pub struct SequentialPhragmen(
sp_std::marker::PhantomData<(AccountId, Accuracy, Balancing)>,
);
-impl<
- AccountId: IdentifierT,
- Accuracy: PerThing128,
- Balancing: Get>,
- > NposSolver for SequentialPhragmen
+impl>>
+ NposSolver for SequentialPhragmen
{
type AccountId = AccountId;
type Accuracy = Accuracy;
@@ -596,11 +593,8 @@ pub struct PhragMMS(
sp_std::marker::PhantomData<(AccountId, Accuracy, Balancing)>,
);
-impl<
- AccountId: IdentifierT,
- Accuracy: PerThing128,
- Balancing: Get>,
- > NposSolver for PhragMMS
+impl>>
+ NposSolver for PhragMMS
{
type AccountId = AccountId;
type Accuracy = Accuracy;
diff --git a/substrate/frame/election-provider-support/src/weights.rs b/substrate/frame/election-provider-support/src/weights.rs
index f288ae63bb..c603b19651 100644
--- a/substrate/frame/election-provider-support/src/weights.rs
+++ b/substrate/frame/election-provider-support/src/weights.rs
@@ -15,15 +15,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-//! Autogenerated weights for pallet_election_provider_support_onchain_benchmarking
+//! Autogenerated weights for pallet_election_provider_support_benchmarking
//!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
-//! DATE: 2022-04-04, STEPS: `1`, REPEAT: 1, LOW RANGE: `[]`, HIGH RANGE: `[]`
+//! DATE: 2022-04-23, STEPS: `1`, REPEAT: 1, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024
// Executed Command:
// target/release/substrate
// benchmark
+// pallet
// --chain=dev
// --steps=1
// --repeat=1
diff --git a/substrate/primitives/npos-elections/README.md b/substrate/primitives/npos-elections/README.md
index b518e63615..6881fc6418 100644
--- a/substrate/primitives/npos-elections/README.md
+++ b/substrate/primitives/npos-elections/README.md
@@ -8,7 +8,7 @@ sub-system. Notable implementation include:
it can achieve a constant factor approximation of the maximin problem, similar to that of the
MMS algorithm.
- [`balance_solution`]: Implements the star balancing algorithm. This iterative process can push
- a solution toward being more `balances`, which in turn can increase its score.
+ a solution toward being more `balanced`, which in turn can increase its score.
### Terminology
@@ -46,7 +46,7 @@ let election_result = ElectionResult { winners, assignments };
The `Assignment` field of the election result is voter-major, i.e. it is from the perspective of
the voter. The struct that represents the opposite is called a `Support`. This struct is usually
-accessed in a map-like manner, i.e. keyed vy voters, therefor it is stored as a mapping called
+accessed in a map-like manner, i.e. keyed by voters, therefore it is stored as a mapping called
`SupportMap`.
Moreover, the support is built from absolute backing values, not ratios like the example above.
diff --git a/substrate/primitives/npos-elections/fuzzer/Cargo.toml b/substrate/primitives/npos-elections/fuzzer/Cargo.toml
index 42a80296a4..a200d5c41e 100644
--- a/substrate/primitives/npos-elections/fuzzer/Cargo.toml
+++ b/substrate/primitives/npos-elections/fuzzer/Cargo.toml
@@ -36,4 +36,4 @@ path = "src/phragmms_balancing.rs"
[[bin]]
name = "phragmen_pjr"
-path = "src/phragmen_pjr.rs"
+path = "src/phragmen_pjr.rs"
\ No newline at end of file
diff --git a/substrate/primitives/npos-elections/fuzzer/src/common.rs b/substrate/primitives/npos-elections/fuzzer/src/common.rs
index 6b89983c16..e5853f28c4 100644
--- a/substrate/primitives/npos-elections/fuzzer/src/common.rs
+++ b/substrate/primitives/npos-elections/fuzzer/src/common.rs
@@ -21,7 +21,7 @@
#![allow(dead_code)]
use rand::{self, seq::SliceRandom, Rng, RngCore};
-use sp_npos_elections::{phragmms, seq_phragmen, ElectionResult, VoteWeight};
+use sp_npos_elections::{phragmms, seq_phragmen, BalancingConfig, ElectionResult, VoteWeight};
use sp_runtime::Perbill;
use std::collections::{BTreeMap, HashSet};
@@ -38,8 +38,8 @@ pub fn to_range(x: usize, a: usize, b: usize) -> usize {
}
pub enum ElectionType {
- Phragmen(Option<(usize, u128)>),
- Phragmms(Option<(usize, u128)>),
+ Phragmen(Option),
+ Phragmms(Option),
}
pub type AccountId = u64;
diff --git a/substrate/primitives/npos-elections/fuzzer/src/phragmen_balancing.rs b/substrate/primitives/npos-elections/fuzzer/src/phragmen_balancing.rs
index cacf64e81c..e053f9aa0c 100644
--- a/substrate/primitives/npos-elections/fuzzer/src/phragmen_balancing.rs
+++ b/substrate/primitives/npos-elections/fuzzer/src/phragmen_balancing.rs
@@ -23,8 +23,8 @@ use common::*;
use honggfuzz::fuzz;
use rand::{self, SeedableRng};
use sp_npos_elections::{
- assignment_ratio_to_staked_normalized, seq_phragmen, to_supports, ElectionResult,
- EvaluateSupport, VoteWeight,
+ assignment_ratio_to_staked_normalized, seq_phragmen, to_supports, BalancingConfig,
+ ElectionResult, EvaluateSupport, VoteWeight,
};
use sp_runtime::Perbill;
@@ -66,8 +66,9 @@ fn main() {
};
if iterations > 0 {
+ let config = BalancingConfig { iterations, tolerance: 0 };
let balanced: ElectionResult =
- seq_phragmen(to_elect, candidates, voters, Some((iterations, 0))).unwrap();
+ seq_phragmen(to_elect, candidates, voters, Some(config)).unwrap();
let balanced_score = {
let staked =
diff --git a/substrate/primitives/npos-elections/fuzzer/src/phragmms_balancing.rs b/substrate/primitives/npos-elections/fuzzer/src/phragmms_balancing.rs
index 988889428c..3f114674e2 100644
--- a/substrate/primitives/npos-elections/fuzzer/src/phragmms_balancing.rs
+++ b/substrate/primitives/npos-elections/fuzzer/src/phragmms_balancing.rs
@@ -23,8 +23,8 @@ use common::*;
use honggfuzz::fuzz;
use rand::{self, SeedableRng};
use sp_npos_elections::{
- assignment_ratio_to_staked_normalized, phragmms, to_supports, ElectionResult, EvaluateSupport,
- VoteWeight,
+ assignment_ratio_to_staked_normalized, phragmms, to_supports, BalancingConfig, ElectionResult,
+ EvaluateSupport, VoteWeight,
};
use sp_runtime::Perbill;
@@ -65,8 +65,9 @@ fn main() {
score
};
+ let config = BalancingConfig { iterations, tolerance: 0 };
let balanced: ElectionResult =
- phragmms(to_elect, candidates, voters, Some((iterations, 0))).unwrap();
+ phragmms(to_elect, candidates, voters, Some(config)).unwrap();
let balanced_score = {
let staked =
diff --git a/substrate/primitives/npos-elections/src/balancing.rs b/substrate/primitives/npos-elections/src/balancing.rs
index 54b8ee4bf2..4a713658ad 100644
--- a/substrate/primitives/npos-elections/src/balancing.rs
+++ b/substrate/primitives/npos-elections/src/balancing.rs
@@ -26,14 +26,15 @@
//!
//! See [`balance`] for more information.
-use crate::{Edge, ExtendedBalance, IdentifierT, Voter};
+use crate::{BalancingConfig, Edge, ExtendedBalance, IdentifierT, Voter};
use sp_arithmetic::traits::Zero;
use sp_std::prelude::*;
/// Balance the weight distribution of a given `voters` at most `iterations` times, or up until the
/// point where the biggest difference created per iteration of all stakes is `tolerance`. If this
/// is called with `tolerance = 0`, then exactly `iterations` rounds will be executed, except if no
-/// change has been made (`difference = 0`).
+/// change has been made (`difference = 0`). `tolerance` and `iterations` are part of the
+/// [`BalancingConfig`] struct.
///
/// In almost all cases, a balanced solution will have a better score than an unbalanced solution,
/// yet this is not 100% guaranteed because the first element of a [`crate::ElectionScore`] does not
@@ -52,12 +53,13 @@ use sp_std::prelude::*;
/// - [A new approach to the maximum flow problem](https://dl.acm.org/doi/10.1145/48014.61051).
/// - [Validator election in nominated proof-of-stake](https://arxiv.org/abs/2004.12990) (Appendix
/// A.)
+/// - [Computing a balanced solution](https://research.web3.foundation/en/latest/polkadot/NPoS/3.%20Balancing.html),
+/// which contains the details of the algorithm implementation.
pub fn balance(
voters: &mut Vec>,
- iterations: usize,
- tolerance: ExtendedBalance,
+ config: &BalancingConfig,
) -> usize {
- if iterations == 0 {
+ if config.iterations == 0 {
return 0
}
@@ -65,14 +67,14 @@ pub fn balance(
loop {
let mut max_diff = 0;
for voter in voters.iter_mut() {
- let diff = balance_voter(voter, tolerance);
+ let diff = balance_voter(voter, config.tolerance);
if diff > max_diff {
max_diff = diff;
}
}
iter += 1;
- if max_diff <= tolerance || iter >= iterations {
+ if max_diff <= config.tolerance || iter >= config.iterations {
break iter
}
}
@@ -158,7 +160,7 @@ pub(crate) fn balance_voter(
.get(last_index)
.expect(
"length of elected_edges is greater than or equal 2; last_index index is at the \
- minimum elected_edges.len() - 1; index is within range; qed",
+ minimum elected_edges.len() - 1; index is within range; qed",
)
.candidate
.borrow()
diff --git a/substrate/primitives/npos-elections/src/lib.rs b/substrate/primitives/npos-elections/src/lib.rs
index 63b9740b74..dd2a9bf198 100644
--- a/substrate/primitives/npos-elections/src/lib.rs
+++ b/substrate/primitives/npos-elections/src/lib.rs
@@ -62,7 +62,7 @@
//!
//! The `Assignment` field of the election result is voter-major, i.e. it is from the perspective of
//! the voter. The struct that represents the opposite is called a `Support`. This struct is usually
-//! accessed in a map-like manner, i.e. keyed by voters, therefor it is stored as a mapping called
+//! accessed in a map-like manner, i.e. keyed by voters, therefore it is stored as a mapping called
//! `SupportMap`.
//!
//! Moreover, the support is built from absolute backing values, not ratios like the example above.
@@ -217,6 +217,13 @@ impl sp_std::cmp::PartialOrd for ElectionScore {
}
}
+/// Utility struct to group parameters for the balancing algorithm.
+#[derive(Clone, Copy)]
+pub struct BalancingConfig {
+ pub iterations: usize,
+ pub tolerance: ExtendedBalance,
+}
+
/// A pointer to a candidate struct with interior mutability.
pub type CandidatePtr = Rc>>;
@@ -320,7 +327,7 @@ impl Voter {
///
/// Note that this might create _un-normalized_ assignments, due to accuracy loss of `P`. Call
/// site might compensate by calling `normalize()` on the returned `Assignment` as a
- /// post-precessing.
+ /// post-processing.
pub fn into_assignment(self) -> Option> {
let who = self.who;
let budget = self.budget;
diff --git a/substrate/primitives/npos-elections/src/phragmen.rs b/substrate/primitives/npos-elections/src/phragmen.rs
index 9b0bfa4221..9b50dc36e4 100644
--- a/substrate/primitives/npos-elections/src/phragmen.rs
+++ b/substrate/primitives/npos-elections/src/phragmen.rs
@@ -21,8 +21,8 @@
//! to the Maximin problem.
use crate::{
- balancing, setup_inputs, CandidatePtr, ElectionResult, ExtendedBalance, IdentifierT,
- PerThing128, VoteWeight, Voter,
+ balancing, setup_inputs, BalancingConfig, CandidatePtr, ElectionResult, ExtendedBalance,
+ IdentifierT, PerThing128, VoteWeight, Voter,
};
use sp_arithmetic::{
helpers_128bit::multiply_by_rational,
@@ -71,16 +71,16 @@ pub fn seq_phragmen(
to_elect: usize,
candidates: Vec,
voters: Vec<(AccountId, VoteWeight, impl IntoIterator- )>,
- balancing: Option<(usize, ExtendedBalance)>,
+ balancing: Option
,
) -> Result, crate::Error> {
let (candidates, voters) = setup_inputs(candidates, voters);
let (candidates, mut voters) = seq_phragmen_core::(to_elect, candidates, voters)?;
- if let Some((iterations, tolerance)) = balancing {
+ if let Some(ref config) = balancing {
// NOTE: might create zero-edges, but we will strip them again when we convert voter into
// assignment.
- let _iters = balancing::balance::(&mut voters, iterations, tolerance);
+ let _iters = balancing::balance::(&mut voters, config);
}
let mut winners = candidates
diff --git a/substrate/primitives/npos-elections/src/phragmms.rs b/substrate/primitives/npos-elections/src/phragmms.rs
index 5d63517c8e..3fbbad75e2 100644
--- a/substrate/primitives/npos-elections/src/phragmms.rs
+++ b/substrate/primitives/npos-elections/src/phragmms.rs
@@ -22,15 +22,15 @@
//! MMS algorithm.
use crate::{
- balance, setup_inputs, CandidatePtr, ElectionResult, ExtendedBalance, IdentifierT, PerThing128,
- VoteWeight, Voter,
+ balance, setup_inputs, BalancingConfig, CandidatePtr, ElectionResult, ExtendedBalance,
+ IdentifierT, PerThing128, VoteWeight, Voter,
};
use sp_arithmetic::{traits::Bounded, PerThing, Rational128};
use sp_std::{prelude::*, rc::Rc};
/// Execute the phragmms method.
///
-/// This can be used interchangeably with [`seq-phragmen`] and offers a similar API, namely:
+/// This can be used interchangeably with `seq-phragmen` and offers a similar API, namely:
///
/// - The resulting edge weight distribution is normalized (thus, safe to use for submission).
/// - The accuracy can be configured via the generic type `P`.
@@ -45,7 +45,7 @@ pub fn phragmms(
to_elect: usize,
candidates: Vec,
voters: Vec<(AccountId, VoteWeight, impl IntoIterator- )>,
- balancing: Option<(usize, ExtendedBalance)>,
+ balancing: Option
,
) -> Result, crate::Error> {
let (candidates, mut voters) = setup_inputs(candidates, voters);
@@ -58,8 +58,8 @@ pub fn phragmms(
round_winner.borrow_mut().elected = true;
winners.push(round_winner);
- if let Some((iterations, tolerance)) = balancing {
- balance(&mut voters, iterations, tolerance);
+ if let Some(ref config) = balancing {
+ balance(&mut voters, config);
}
} else {
break
@@ -275,7 +275,8 @@ mod tests {
drop(winner);
// balancing makes no difference here but anyhow.
- balance(&mut voters, 10, 0);
+ let config = BalancingConfig { iterations: 10, tolerance: 0 };
+ balance(&mut voters, &config);
// round 2
let winner =
@@ -315,7 +316,7 @@ mod tests {
drop(winner);
// balancing will improve stuff here.
- balance(&mut voters, 10, 0);
+ balance(&mut voters, &config);
assert_eq!(
voters
@@ -348,8 +349,9 @@ mod tests {
let candidates = vec![1, 2, 3];
let voters = vec![(10, 10, vec![1, 2]), (20, 20, vec![1, 3]), (30, 30, vec![2, 3])];
+ let config = BalancingConfig { iterations: 2, tolerance: 0 };
let ElectionResult::<_, Perbill> { winners, assignments } =
- phragmms(2, candidates, voters, Some((2, 0))).unwrap();
+ phragmms(2, candidates, voters, Some(config)).unwrap();
assert_eq!(winners, vec![(3, 30), (2, 30)]);
assert_eq!(
assignments,
@@ -380,8 +382,9 @@ mod tests {
(130, 1000, vec![61, 71]),
];
+ let config = BalancingConfig { iterations: 2, tolerance: 0 };
let ElectionResult::<_, Perbill> { winners, assignments: _ } =
- phragmms(4, candidates, voters, Some((2, 0))).unwrap();
+ phragmms(4, candidates, voters, Some(config)).unwrap();
assert_eq!(winners, vec![(11, 3000), (31, 2000), (51, 1500), (61, 1500),]);
}
@@ -393,8 +396,9 @@ mod tests {
// give a bit more to 1 and 3.
voters.push((2, u64::MAX, vec![1, 3]));
+ let config = BalancingConfig { iterations: 2, tolerance: 0 };
let ElectionResult::<_, Perbill> { winners, assignments: _ } =
- phragmms(2, candidates, voters, Some((2, 0))).unwrap();
+ phragmms(2, candidates, voters, Some(config)).unwrap();
assert_eq!(winners.into_iter().map(|(w, _)| w).collect::>(), vec![1u32, 3]);
}
}
diff --git a/substrate/primitives/npos-elections/src/tests.rs b/substrate/primitives/npos-elections/src/tests.rs
index 1cf5ea8a24..5b88889201 100644
--- a/substrate/primitives/npos-elections/src/tests.rs
+++ b/substrate/primitives/npos-elections/src/tests.rs
@@ -19,7 +19,7 @@
use crate::{
balancing, helpers::*, mock::*, seq_phragmen, seq_phragmen_core, setup_inputs, to_support_map,
- Assignment, ElectionResult, ExtendedBalance, StakedAssignment, Support, Voter,
+ Assignment, BalancingConfig, ElectionResult, ExtendedBalance, StakedAssignment, Support, Voter,
};
use sp_arithmetic::{PerU16, Perbill, Percent, Permill};
use substrate_test_utils::assert_eq_uvec;
@@ -142,7 +142,8 @@ fn balancing_core_works() {
let (candidates, voters) = setup_inputs(candidates, voters);
let (candidates, mut voters) = seq_phragmen_core(4, candidates, voters).unwrap();
- let iters = balancing::balance::(&mut voters, 4, 0);
+ let config = BalancingConfig { iterations: 4, tolerance: 0 };
+ let iters = balancing::balance::(&mut voters, &config);
assert!(iters > 0);
@@ -282,6 +283,7 @@ fn phragmen_poc_works_with_balancing() {
let voters = vec![(10, vec![1, 2]), (20, vec![1, 3]), (30, vec![2, 3])];
let stake_of = create_stake_of(&[(10, 10), (20, 20), (30, 30)]);
+ let config = BalancingConfig { iterations: 4, tolerance: 0 };
let ElectionResult::<_, Perbill> { winners, assignments } = seq_phragmen(
2,
candidates,
@@ -289,7 +291,7 @@ fn phragmen_poc_works_with_balancing() {
.iter()
.map(|(ref v, ref vs)| (v.clone(), stake_of(v), vs.clone()))
.collect::>(),
- Some((4, 0)),
+ Some(config),
)
.unwrap();