Create trait for NPoS election algorithms (#9664)

* build the template, hand it over to zeke now.

* Tests working

* save wip

* Some updates

* Some cleanup

* mo cleanin

* Link to issue

* Apply suggestions from code review

Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>

* Apply suggestions from code review

Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>

* Apply suggestions from code review

Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>

* Apply suggestions from code review

Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>

* Bound accuracy for prepare_election_result

* Use npos_election::Error for phragmms

* save

* Apply suggestions from code review

* Simplify test to use Balancing::set

* Cargo.lock after build

* Revert "Cargo.lock after build"

This reverts commit 7d726c8efa687c09e4f377196b106eb9e9760487.

* Try reduce cargo.lock diff

* Update bin/node/runtime/src/lib.rs

* Comment

* Apply suggestions from code review

* Set balancing directly

* Document som pub items

* Update frame/election-provider-multi-phase/src/unsigned.rs

* Apply suggestions from code review

Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>

* Improve some comments

* Revert accidental change to random file

* tiney

* revert

Co-authored-by: kianenigma <kian@parity.io>
Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>
This commit is contained in:
Zeke Mostov
2021-09-09 12:46:24 -07:00
committed by GitHub
parent d1c281461d
commit 6bfcfeed4c
10 changed files with 203 additions and 105 deletions
@@ -19,11 +19,11 @@ sp-std = { version = "4.0.0-dev", default-features = false, path = "../std" }
sp-npos-elections-solution-type = { version = "4.0.0-dev", path = "./solution-type" }
sp-arithmetic = { version = "4.0.0-dev", default-features = false, path = "../arithmetic" }
sp-core = { version = "4.0.0-dev", default-features = false, path = "../core" }
sp-runtime = { version = "4.0.0-dev", path = "../runtime", default-features = false }
[dev-dependencies]
substrate-test-utils = { version = "4.0.0-dev", path = "../../test-utils" }
rand = "0.7.3"
sp-runtime = { version = "4.0.0-dev", path = "../runtime" }
[features]
default = ["std"]
@@ -34,4 +34,5 @@ std = [
"sp-std/std",
"sp-arithmetic/std",
"sp-core/std",
"sp-runtime/std",
]
@@ -68,16 +68,16 @@ const DEN: ExtendedBalance = ExtendedBalance::max_value();
/// check where t is the standard threshold. The underlying algorithm is sound, but the conversions
/// between numeric types can be lossy.
pub fn seq_phragmen<AccountId: IdentifierT, P: PerThing128>(
rounds: usize,
initial_candidates: Vec<AccountId>,
initial_voters: Vec<(AccountId, VoteWeight, Vec<AccountId>)>,
balance: Option<(usize, ExtendedBalance)>,
to_elect: usize,
candidates: Vec<AccountId>,
voters: Vec<(AccountId, VoteWeight, Vec<AccountId>)>,
balancing: Option<(usize, ExtendedBalance)>,
) -> Result<ElectionResult<AccountId, P>, crate::Error> {
let (candidates, voters) = setup_inputs(initial_candidates, initial_voters);
let (candidates, voters) = setup_inputs(candidates, voters);
let (candidates, mut voters) = seq_phragmen_core::<AccountId>(rounds, candidates, voters)?;
let (candidates, mut voters) = seq_phragmen_core::<AccountId>(to_elect, candidates, voters)?;
if let Some((iterations, tolerance)) = balance {
if let Some((iterations, tolerance)) = balancing {
// NOTE: might create zero-edges, but we will strip them again when we convert voter into
// assignment.
let _iters = balancing::balance::<AccountId>(&mut voters, iterations, tolerance);
@@ -87,7 +87,7 @@ pub fn seq_phragmen<AccountId: IdentifierT, P: PerThing128>(
.into_iter()
.filter(|c_ptr| c_ptr.borrow().elected)
// defensive only: seq-phragmen-core returns only up to rounds.
.take(rounds)
.take(to_elect)
.collect::<Vec<_>>();
// sort winners based on desirability.
@@ -116,12 +116,12 @@ pub fn seq_phragmen<AccountId: IdentifierT, P: PerThing128>(
/// This can only fail if the normalization fails.
// To create the inputs needed for this function, see [`crate::setup_inputs`].
pub fn seq_phragmen_core<AccountId: IdentifierT>(
rounds: usize,
to_elect: usize,
candidates: Vec<CandidatePtr<AccountId>>,
mut voters: Vec<Voter<AccountId>>,
) -> Result<(Vec<CandidatePtr<AccountId>>, Vec<Voter<AccountId>>), crate::Error> {
// we have already checked that we have more candidates than minimum_candidate_count.
let to_elect = rounds.min(candidates.len());
let to_elect = to_elect.min(candidates.len());
// main election loop
for round in 0..to_elect {
@@ -43,11 +43,11 @@ use sp_std::{prelude::*, rc::Rc};
/// `expect` this to return `Ok`.
pub fn phragmms<AccountId: IdentifierT, P: PerThing128>(
to_elect: usize,
initial_candidates: Vec<AccountId>,
initial_voters: Vec<(AccountId, VoteWeight, Vec<AccountId>)>,
balancing_config: Option<(usize, ExtendedBalance)>,
) -> Result<ElectionResult<AccountId, P>, &'static str> {
let (candidates, mut voters) = setup_inputs(initial_candidates, initial_voters);
candidates: Vec<AccountId>,
voters: Vec<(AccountId, VoteWeight, Vec<AccountId>)>,
balancing: Option<(usize, ExtendedBalance)>,
) -> Result<ElectionResult<AccountId, P>, crate::Error> {
let (candidates, mut voters) = setup_inputs(candidates, voters);
let mut winners = vec![];
for round in 0..to_elect {
@@ -58,7 +58,7 @@ pub fn phragmms<AccountId: IdentifierT, P: PerThing128>(
round_winner.borrow_mut().elected = true;
winners.push(round_winner);
if let Some((iterations, tolerance)) = balancing_config {
if let Some((iterations, tolerance)) = balancing {
balance(&mut voters, iterations, tolerance);
}
} else {
@@ -68,7 +68,11 @@ pub fn phragmms<AccountId: IdentifierT, P: PerThing128>(
let mut assignments =
voters.into_iter().filter_map(|v| v.into_assignment()).collect::<Vec<_>>();
let _ = assignments.iter_mut().map(|a| a.try_normalize()).collect::<Result<(), _>>()?;
let _ = assignments
.iter_mut()
.map(|a| a.try_normalize())
.collect::<Result<(), _>>()
.map_err(|e| crate::Error::ArithmeticError(e))?;
let winners = winners
.into_iter()
.map(|w_ptr| (w_ptr.borrow().who.clone(), w_ptr.borrow().backed_stake))