Accept new Phragmén solutions if they are epsilon better + Better pre-inclusion checks. (#6173)

* part1: Accept inly epsilon better solutions

* Fix pre-dispatch check

* Fix build

* review grumbles

* Epsilon -> Threshold
This commit is contained in:
Kian Paimani
2020-06-02 17:22:56 +02:00
committed by GitHub
parent 6547d7a09a
commit 0eec4bb795
14 changed files with 310 additions and 46 deletions
@@ -19,8 +19,8 @@
/// converts x into the range [a, b] in a pseudo-fair way.
pub fn to_range(x: usize, a: usize, b: usize) -> usize {
// does not work correctly if b < 2*a
assert!(b > 2 * a);
// does not work correctly if b < 2 * a
assert!(b >= 2 * a);
let collapsed = x % b;
if collapsed >= a {
collapsed
@@ -131,7 +131,7 @@ fn main() {
return;
}
let enhance = is_score_better(initial_score, final_score);
let enhance = is_score_better(final_score, initial_score, Perbill::zero());
println!(
"iter = {} // {:?} -> {:?} [{}]",
@@ -140,6 +140,7 @@ fn main() {
final_score,
enhance,
);
// if more than one iteration has been done, or they must be equal.
assert!(enhance || initial_score == final_score || i == 0)
});
+24 -11
View File
@@ -36,7 +36,7 @@
use sp_std::{prelude::*, collections::btree_map::BTreeMap, fmt::Debug, cmp::Ordering, convert::TryFrom};
use sp_arithmetic::{
PerThing, Rational128,
PerThing, Rational128, ThresholdOrd,
helpers_128bit::multiply_by_rational,
traits::{Zero, Saturating, Bounded, SaturatedConversion},
};
@@ -614,23 +614,36 @@ pub fn evaluate_support<AccountId>(
[min_support, sum, sum_squared]
}
/// Compares two sets of phragmen scores based on desirability and returns true if `that` is
/// better than `this`.
/// Compares two sets of phragmen scores based on desirability and returns true if `this` is
/// better than `that`.
///
/// Evaluation is done in a lexicographic manner.
/// Evaluation is done in a lexicographic manner, and if each element of `this` is `that * epsilon`
/// greater or less than `that`.
///
/// Note that the third component should be minimized.
pub fn is_score_better(this: PhragmenScore, that: PhragmenScore) -> bool {
match that
pub fn is_score_better<P: PerThing>(this: PhragmenScore, that: PhragmenScore, epsilon: P) -> bool
where ExtendedBalance: From<sp_arithmetic::InnerOf<P>>
{
match this
.iter()
.enumerate()
.map(|(i, e)| e.cmp(&this[i]))
.collect::<Vec<Ordering>>()
.map(|(i, e)| (
e.ge(&that[i]),
e.tcmp(&that[i], epsilon.mul_ceil(that[i])),
))
.collect::<Vec<(bool, Ordering)>>()
.as_slice()
{
[Ordering::Greater, _, _] => true,
[Ordering::Equal, Ordering::Greater, _] => true,
[Ordering::Equal, Ordering::Equal, Ordering::Less] => true,
// epsilon better in the score[0], accept.
[(_, Ordering::Greater), _, _] => true,
// less than epsilon better in score[0], but more than epsilon better in the second.
[(true, Ordering::Equal), (_, Ordering::Greater), _] => true,
// less than epsilon better in score[0, 1], but more than epsilon better in the third
[(true, Ordering::Equal), (true, Ordering::Equal), (_, Ordering::Less)] => true,
// anything else is not a good score.
_ => false,
}
}
+2 -2
View File
@@ -640,8 +640,8 @@ fn reduce_all<A: IdentifierT>(assignments: &mut Vec<StakedAssignment<A>>) -> u32
num_changed
}
/// Reduce the given [`Vec<StakedAssignment<IdentifierT>>`]. This removes redundant edges from without changing the
/// overall backing of any of the elected candidates.
/// Reduce the given [`Vec<StakedAssignment<IdentifierT>>`]. This removes redundant edges from
/// without changing the overall backing of any of the elected candidates.
///
/// Returns the number of edges removed.
///
+130 -5
View File
@@ -616,28 +616,153 @@ fn assignment_convert_works() {
}
#[test]
fn score_comparison_is_lexicographical() {
fn score_comparison_is_lexicographical_no_epsilon() {
let epsilon = Perbill::zero();
// only better in the fist parameter, worse in the other two ✅
assert_eq!(
is_score_better([10, 20, 30], [12, 10, 35]),
is_score_better([12, 10, 35], [10, 20, 30], epsilon),
true,
);
// worse in the first, better in the other two ❌
assert_eq!(
is_score_better([10, 20, 30], [9, 30, 10]),
is_score_better([9, 30, 10], [10, 20, 30], epsilon),
false,
);
// equal in the first, the second one dictates.
assert_eq!(
is_score_better([10, 20, 30], [10, 25, 40]),
is_score_better([10, 25, 40], [10, 20, 30], epsilon),
true,
);
// equal in the first two, the last one dictates.
assert_eq!(
is_score_better([10, 20, 30], [10, 20, 40]),
is_score_better([10, 20, 40], [10, 20, 30], epsilon),
false,
);
}
#[test]
fn score_comparison_with_epsilon() {
let epsilon = Perbill::from_percent(1);
{
// no more than 1 percent (10) better in the first param.
assert_eq!(
is_score_better([1009, 5000, 100000], [1000, 5000, 100000], epsilon),
false,
);
// now equal, still not better.
assert_eq!(
is_score_better([1010, 5000, 100000], [1000, 5000, 100000], epsilon),
false,
);
// now it is.
assert_eq!(
is_score_better([1011, 5000, 100000], [1000, 5000, 100000], epsilon),
true,
);
}
{
// First score score is epsilon better, but first score is no longer `ge`. Then this is
// still not a good solution.
assert_eq!(
is_score_better([999, 6000, 100000], [1000, 5000, 100000], epsilon),
false,
);
}
{
// first score is equal or better, but not epsilon. Then second one is the determinant.
assert_eq!(
is_score_better([1005, 5000, 100000], [1000, 5000, 100000], epsilon),
false,
);
assert_eq!(
is_score_better([1005, 5050, 100000], [1000, 5000, 100000], epsilon),
false,
);
assert_eq!(
is_score_better([1005, 5051, 100000], [1000, 5000, 100000], epsilon),
true,
);
}
{
// first score and second are equal or less than epsilon more, third is determinant.
assert_eq!(
is_score_better([1005, 5025, 100000], [1000, 5000, 100000], epsilon),
false,
);
assert_eq!(
is_score_better([1005, 5025, 99_000], [1000, 5000, 100000], epsilon),
false,
);
assert_eq!(
is_score_better([1005, 5025, 98_999], [1000, 5000, 100000], epsilon),
true,
);
}
}
#[test]
fn score_comparison_large_value() {
// some random value taken from eras in kusama.
let initial = [12488167277027543u128, 5559266368032409496, 118749283262079244270992278287436446];
// this claim is 0.04090% better in the third component. It should be accepted as better if
// epsilon is smaller than 5/10_0000
let claim = [12488167277027543u128, 5559266368032409496, 118700736389524721358337889258988054];
assert_eq!(
is_score_better(
claim.clone(),
initial.clone(),
Perbill::from_rational_approximation(1u32, 10_000),
),
true,
);
assert_eq!(
is_score_better(
claim.clone(),
initial.clone(),
Perbill::from_rational_approximation(2u32, 10_000),
),
true,
);
assert_eq!(
is_score_better(
claim.clone(),
initial.clone(),
Perbill::from_rational_approximation(3u32, 10_000),
),
true,
);
assert_eq!(
is_score_better(
claim.clone(),
initial.clone(),
Perbill::from_rational_approximation(4u32, 10_000),
),
true,
);
assert_eq!(
is_score_better(
claim.clone(),
initial.clone(),
Perbill::from_rational_approximation(5u32, 10_000),
),
false,
);
}