PhragMMS election. (#6685)

* Revamp npos-elections and implement phragmms

* Update primitives/npos-elections/src/phragmms.rs

* Fix build

* Some review grumbles

* Add some stuff for remote testing

* fix some of the grumbles.

* Add remote testing stuff.

* Cleanup

* fix docs

* Update primitives/arithmetic/src/rational.rs

Co-authored-by: Dan Forbes <dan@danforbes.dev>

* Small config change

* Better handling of approval_stake == 0

* Final touhces.

* Clean fuzzer a bit

* Clean fuzzer a bit

* Update primitives/npos-elections/src/balancing.rs

Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>

* Fix fuzzer.

* Better api for normalize

* Add noramlize_up

* A large number of small fixes.

* make it merge ready

* Fix warns

* bump

* Fix fuzzers a bit.

* Fix warns as well.

* Fix more tests.

Co-authored-by: Dan Forbes <dan@danforbes.dev>
Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>
This commit is contained in:
Kian Paimani
2020-09-23 10:16:10 +02:00
committed by GitHub
parent ecdc94420e
commit 313f86ec23
32 changed files with 2074 additions and 914 deletions
+17 -4
View File
@@ -17,12 +17,13 @@
//! Infinite precision unsigned integer for substrate runtime.
use num_traits::Zero;
use num_traits::{Zero, One};
use sp_std::{cmp::Ordering, ops, prelude::*, vec, cell::RefCell, convert::TryFrom};
// A sensible value for this would be half of the dword size of the host machine. Since the
// runtime is compiled to 32bit webassembly, using 32 and 64 for single and double respectively
// should yield the most performance.
/// Representation of a single limb.
pub type Single = u32;
/// Representation of two limbs.
@@ -75,7 +76,7 @@ fn div_single(a: Double, b: Single) -> (Double, Single) {
/// Simple wrapper around an infinitely large integer, represented as limbs of [`Single`].
#[derive(Clone, Default)]
pub struct BigUint {
/// digits (limbs) of this number (sorted as msb -> lsd).
/// digits (limbs) of this number (sorted as msb -> lsb).
pub(crate) digits: Vec<Single>,
}
@@ -515,6 +516,12 @@ impl Zero for BigUint {
}
}
impl One for BigUint {
fn one() -> Self {
Self { digits: vec![Single::one()] }
}
}
macro_rules! impl_try_from_number_for {
($([$type:ty, $len:expr]),+) => {
$(
@@ -550,15 +557,21 @@ macro_rules! impl_from_for_smaller_than_word {
})*
}
}
impl_from_for_smaller_than_word!(u8, u16, Single);
impl_from_for_smaller_than_word!(u8, u16, u32);
impl From<Double> for BigUint {
impl From<u64> for BigUint {
fn from(a: Double) -> Self {
let (ah, al) = split(a);
Self { digits: vec![ah, al] }
}
}
impl From<u128> for BigUint {
fn from(a: u128) -> Self {
crate::helpers_128bit::to_big_uint(a)
}
}
#[cfg(test)]
pub mod tests {
use super::*;
+18 -9
View File
@@ -36,13 +36,13 @@ macro_rules! assert_eq_error_rate {
pub mod biguint;
pub mod helpers_128bit;
pub mod traits;
mod per_things;
mod fixed_point;
mod rational128;
pub mod per_things;
pub mod fixed_point;
pub mod rational;
pub use fixed_point::{FixedPointNumber, FixedPointOperand, FixedI64, FixedI128, FixedU128};
pub use per_things::{PerThing, InnerOf, UpperOf, Percent, PerU16, Permill, Perbill, Perquintill};
pub use rational128::Rational128;
pub use rational::{Rational128, RationalInfinite};
use sp_std::{prelude::*, cmp::Ordering, fmt::Debug, convert::TryInto};
use traits::{BaseArithmetic, One, Zero, SaturatedConversion, Unsigned};
@@ -114,13 +114,22 @@ impl_normalize_for_numeric!(u8, u16, u32, u64, u128);
impl<P: PerThing> Normalizable<P> for Vec<P> {
fn normalize(&self, targeted_sum: P) -> Result<Vec<P>, &'static str> {
let inners = self.iter().map(|p| p.clone().deconstruct().into()).collect::<Vec<_>>();
let inners = self
.iter()
.map(|p| p.clone().deconstruct().into())
.collect::<Vec<_>>();
let normalized = normalize(inners.as_ref(), targeted_sum.deconstruct().into())?;
Ok(normalized.into_iter().map(|i: UpperOf<P>| P::from_parts(i.saturated_into())).collect())
Ok(
normalized
.into_iter()
.map(|i: UpperOf<P>| P::from_parts(i.saturated_into()))
.collect()
)
}
}
/// Normalize `input` so that the sum of all elements reaches `targeted_sum`.
///
/// This implementation is currently in a balanced position between being performant and accurate.
@@ -143,8 +152,8 @@ impl<P: PerThing> Normalizable<P> for Vec<P> {
/// `leftover` value. This ensures that the result will always stay accurate, yet it might cause the
/// execution to become increasingly slow, since leftovers are applied one by one.
///
/// All in all, the complicated case above is rare to happen in all substrate use cases, hence we
/// opt for it due to its simplicity.
/// All in all, the complicated case above is rare to happen in most use cases within this repo ,
/// hence we opt for it due to its simplicity.
///
/// This function will return an error is if length of `input` cannot fit in `T`, or if `sum(input)`
/// cannot fit inside `T`.
@@ -17,19 +17,106 @@
use sp_std::{cmp::Ordering, prelude::*};
use crate::helpers_128bit;
use num_traits::Zero;
use sp_debug_derive::RuntimeDebug;
use num_traits::{Zero, One, Bounded};
use crate::biguint::BigUint;
/// A wrapper for any rational number with infinitely large numerator and denominator.
///
/// This type exists to facilitate `cmp` operation
/// on values like `a/b < c/d` where `a, b, c, d` are all `BigUint`.
#[derive(Clone, Default, Eq)]
pub struct RationalInfinite(BigUint, BigUint);
impl RationalInfinite {
/// Return the numerator reference.
pub fn n(&self) -> &BigUint {
&self.0
}
/// Return the denominator reference.
pub fn d(&self) -> &BigUint {
&self.1
}
/// Build from a raw `n/d`.
pub fn from(n: BigUint, d: BigUint) -> Self {
Self(n, d.max(BigUint::one()))
}
/// Zero.
pub fn zero() -> Self {
Self(BigUint::zero(), BigUint::one())
}
/// One.
pub fn one() -> Self {
Self(BigUint::one(), BigUint::one())
}
}
impl PartialOrd for RationalInfinite {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for RationalInfinite {
fn cmp(&self, other: &Self) -> Ordering {
// handle some edge cases.
if self.d() == other.d() {
self.n().cmp(&other.n())
} else if self.d().is_zero() {
Ordering::Greater
} else if other.d().is_zero() {
Ordering::Less
} else {
// (a/b) cmp (c/d) => (a*d) cmp (c*b)
self.n().clone().mul(&other.d()).cmp(&other.n().clone().mul(&self.d()))
}
}
}
impl PartialEq for RationalInfinite {
fn eq(&self, other: &Self) -> bool {
self.cmp(other) == Ordering::Equal
}
}
impl From<Rational128> for RationalInfinite {
fn from(t: Rational128) -> Self {
Self(t.0.into(), t.1.into())
}
}
/// A wrapper for any rational number with a 128 bit numerator and denominator.
#[derive(Clone, Copy, Default, Eq, RuntimeDebug)]
#[derive(Clone, Copy, Default, Eq)]
pub struct Rational128(u128, u128);
#[cfg(feature = "std")]
impl sp_std::fmt::Debug for Rational128 {
fn fmt(&self, f: &mut sp_std::fmt::Formatter<'_>) -> sp_std::fmt::Result {
write!(f, "Rational128({:.4})", self.0 as f32 / self.1 as f32)
}
}
#[cfg(not(feature = "std"))]
impl sp_std::fmt::Debug for Rational128 {
fn fmt(&self, f: &mut sp_std::fmt::Formatter<'_>) -> sp_std::fmt::Result {
write!(f, "Rational128(..)")
}
}
impl Rational128 {
/// Nothing.
/// Zero.
pub fn zero() -> Self {
Self(0, 1)
}
/// One
pub fn one() -> Self {
Self(1, 1)
}
/// If it is zero or not
pub fn is_zero(&self) -> bool {
self.0.is_zero()
@@ -122,6 +209,22 @@ impl Rational128 {
}
}
impl Bounded for Rational128 {
fn min_value() -> Self {
Self(0, 1)
}
fn max_value() -> Self {
Self(Bounded::max_value(), 1)
}
}
impl<T: Into<u128>> From<T> for Rational128 {
fn from(t: T) -> Self {
Self::from(t.into(), 1)
}
}
impl PartialOrd for Rational128 {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))