Move phragmen benchmarks out of Staking (#3588)

* Move phragmen benches to.. phragmen.

* Move some basic phragmen tests to.. phragmen.

* Line-width

* Add phragmen equ implementation as flot

* Add phragmen equ implementation as flot

* Add mock and test file.
This commit is contained in:
Kian Paimani
2019-09-13 08:41:33 +02:00
committed by GitHub
parent 7b87cd206b
commit a6b5d1d155
10 changed files with 661 additions and 374 deletions
+4 -224
View File
@@ -37,6 +37,9 @@ use rstd::{prelude::*, collections::btree_map::BTreeMap};
use sr_primitives::PerU128;
use sr_primitives::traits::{Zero, Convert, Member, SimpleArithmetic};
mod mock;
mod tests;
/// Type used as the fraction.
type Fraction = PerU128;
@@ -355,7 +358,7 @@ pub fn elect<AccountId, Balance, FS, C>(
/// * `tolerance`: maximum difference that can occur before an early quite happens.
/// * `iterations`: maximum number of iterations that will be processed.
/// * `stake_of`: something that can return the stake stake of a particular candidate or voter.
pub fn equalize<Balance, AccountId, C, FS>(
pub fn equalize<Balance, AccountId, FS, C>(
mut assignments: Vec<(AccountId, Vec<PhragmenAssignment<AccountId>>)>,
supports: &mut SupportMap<AccountId>,
tolerance: ExtendedBalance,
@@ -489,226 +492,3 @@ fn do_equalize<Balance, AccountId, C>(
difference
}
#[cfg(test)]
mod tests {
use super::{elect, ACCURACY, PhragmenResult};
use sr_primitives::traits::{Convert, Member, SaturatedConversion};
use rstd::collections::btree_map::BTreeMap;
use support::assert_eq_uvec;
pub struct C;
impl Convert<u64, u64> for C {
fn convert(x: u64) -> u64 { x }
}
impl Convert<u128, u64> for C {
fn convert(x: u128) -> u64 { x.saturated_into() }
}
#[derive(Default, Debug)]
struct _Candidate<AccountId> {
who: AccountId,
score: f64,
approval_stake: f64,
elected: bool,
}
#[derive(Default, Debug)]
struct _Voter<AccountId> {
who: AccountId,
edges: Vec<_Edge<AccountId>>,
budget: f64,
load: f64,
}
#[derive(Default, Debug)]
struct _Edge<AccountId> {
who: AccountId,
load: f64,
candidate_index: usize,
}
type _PhragmenAssignment<AccountId> = (AccountId, f64);
#[derive(Debug)]
pub struct _PhragmenResult<AccountId> {
pub winners: Vec<AccountId>,
pub assignments: Vec<(AccountId, Vec<_PhragmenAssignment<AccountId>>)>
}
pub fn elect_poc<AccountId, FS>(
candidate_count: usize,
minimum_candidate_count: usize,
initial_candidates: Vec<AccountId>,
initial_voters: Vec<(AccountId, Vec<AccountId>)>,
stake_of: FS,
self_vote: bool,
) -> Option<_PhragmenResult<AccountId>> where
AccountId: Default + Ord + Member + Copy,
for<'r> FS: Fn(&'r AccountId) -> u64,
{
let mut elected_candidates: Vec<AccountId>;
let mut assigned: Vec<(AccountId, Vec<_PhragmenAssignment<AccountId>>)>;
let mut c_idx_cache = BTreeMap::<AccountId, usize>::new();
let num_voters = initial_candidates.len() + initial_voters.len();
let mut voters: Vec<_Voter<AccountId>> = Vec::with_capacity(num_voters);
let mut candidates = if self_vote {
initial_candidates.into_iter().map(|who| {
let stake = stake_of(&who) as f64;
_Candidate { who, approval_stake: stake, ..Default::default() }
})
.filter(|c| c.approval_stake != 0f64)
.enumerate()
.map(|(i, c)| {
let who = c.who;
voters.push(_Voter {
who: who.clone(),
edges: vec![
_Edge { who: who.clone(), candidate_index: i, ..Default::default() }
],
budget: c.approval_stake,
load: 0f64,
});
c_idx_cache.insert(c.who.clone(), i);
c
})
.collect::<Vec<_Candidate<AccountId>>>()
} else {
initial_candidates.into_iter()
.enumerate()
.map(|(idx, who)| {
c_idx_cache.insert(who.clone(), idx);
_Candidate { who, ..Default::default() }
})
.collect::<Vec<_Candidate<AccountId>>>()
};
if candidates.len() < minimum_candidate_count {
return None;
}
voters.extend(initial_voters.into_iter().map(|(who, votes)| {
let voter_stake = stake_of(&who) as f64;
let mut edges: Vec<_Edge<AccountId>> = Vec::with_capacity(votes.len());
for v in votes {
if let Some(idx) = c_idx_cache.get(&v) {
candidates[*idx].approval_stake = candidates[*idx].approval_stake + voter_stake;
edges.push(
_Edge { who: v.clone(), candidate_index: *idx, ..Default::default() }
);
}
}
_Voter {
who,
edges: edges,
budget: voter_stake,
load: 0f64,
}
}));
let to_elect = candidate_count.min(candidates.len());
elected_candidates = Vec::with_capacity(candidate_count);
assigned = Vec::with_capacity(candidate_count);
for _round in 0..to_elect {
for c in &mut candidates {
if !c.elected {
c.score = 1.0 / c.approval_stake;
}
}
for n in &voters {
for e in &n.edges {
let c = &mut candidates[e.candidate_index];
if !c.elected && !(c.approval_stake == 0f64) {
c.score += n.budget * n.load / c.approval_stake;
}
}
}
if let Some(winner) = candidates
.iter_mut()
.filter(|c| !c.elected)
.min_by(|x, y| x.score.partial_cmp(&y.score).unwrap_or(rstd::cmp::Ordering::Equal))
{
winner.elected = true;
for n in &mut voters {
for e in &mut n.edges {
if e.who == winner.who {
e.load = winner.score - n.load;
n.load = winner.score;
}
}
}
elected_candidates.push(winner.who.clone());
} else {
break
}
}
for n in &mut voters {
let mut assignment = (n.who.clone(), vec![]);
for e in &mut n.edges {
if let Some(c) = elected_candidates.iter().cloned().find(|c| *c == e.who) {
if c != n.who {
let ratio = e.load / n.load;
assignment.1.push((e.who.clone(), ratio));
}
}
}
assigned.push(assignment);
}
Some(_PhragmenResult {
winners: elected_candidates,
assignments: assigned,
})
}
#[test]
fn float_poc_works() {
let candidates = vec![1, 2, 3];
let voters = vec![
(10, vec![1, 2]),
(20, vec![1, 3]),
(30, vec![2, 3]),
];
let stake_of = |x: &u64| { if *x >= 10 { *x } else { 0 }};
let _PhragmenResult { winners, assignments } =
elect_poc(2, 2, candidates, voters, stake_of, false).unwrap();
assert_eq_uvec!(winners, vec![2, 3]);
assert_eq_uvec!(
assignments,
vec![
(10, vec![(2, 1.0)]),
(20, vec![(3, 1.0)]),
(30, vec![(2, 0.5), (3, 0.5)])
]
);
}
#[test]
fn phragmen_works() {
let candidates = vec![1, 2, 3];
let voters = vec![
(10, vec![1, 2]),
(20, vec![1, 3]),
(30, vec![2, 3]),
];
let stake_of = |x: &u64| { if *x >= 10 { *x } else { 0 }};
let PhragmenResult { winners, assignments } =
elect::<_, _, _, C>(2, 2, candidates, voters, stake_of, false).unwrap();
assert_eq_uvec!(winners, vec![2, 3]);
assert_eq_uvec!(
assignments,
vec![
(10, vec![(2, ACCURACY)]),
(20, vec![(3, ACCURACY)]),
(30, vec![(2, ACCURACY/2), (3, ACCURACY/2)])
]
);
}
}