Compare commits

..

6 Commits

5 changed files with 25 additions and 18 deletions

1
.gitignore vendored
View File

@@ -1,5 +1,6 @@
/target
/Cargo.lock
/temp
.justfile
*.svg

View File

@@ -1,7 +1,7 @@
[package]
name = "trueskill-tt"
version = "0.1.0"
edition = "2021"
edition = "2024"
[lib]
bench = false

View File

@@ -15,6 +15,7 @@ Rust port of [TrueSkillThroughTime.py](https://github.com/glandfried/TrueSkillTh
- [x] Implement approx for Gaussian
- [x] Add more tests from `TrueSkillThroughTime.jl`
- [ ] Add tests for `quality()` (Use [sublee/trueskill](https://github.com/sublee/trueskill/tree/master) as reference)
- [ ] Benchmark Batch::iteration()
- [ ] Time needs to be an enum so we can have multiple states (see `batch::compute_elapsed()`)
- [ ] Add examples (use same TrueSkillThroughTime.(py|jl))

View File

@@ -23,12 +23,12 @@ impl<'a> Game<'a> {
weights: &'a [Vec<f64>],
p_draw: f64,
) -> Self {
assert!(
debug_assert!(
(result.len() == teams.len()),
"result must have the same length as teams"
);
assert!(
debug_assert!(
weights
.iter()
.zip(teams.iter())
@@ -36,12 +36,12 @@ impl<'a> Game<'a> {
"weights must have the same dimensions as teams"
);
assert!(
debug_assert!(
(0.0..1.0).contains(&p_draw),
"draw probability.must be >= 0.0 and < 1.0"
);
assert!(
debug_assert!(
p_draw > 0.0 || {
let mut r = result.to_vec();
r.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap());

View File

@@ -82,7 +82,7 @@ where
pub fn key(&self, idx: Index) -> Option<&K> {
self.0
.iter()
.find(|(_, &value)| value == idx)
.find(|&(_, value)| *value == idx)
.map(|(key, _)| key)
}
@@ -115,11 +115,7 @@ fn erfc(x: f64) -> f64 {
let r = t * (-z * z - 1.26551223 + t * h).exp();
if x >= 0.0 {
r
} else {
2.0 - r
}
if x >= 0.0 { r } else { 2.0 - r }
}
fn erfc_inv(mut y: f64) -> f64 {
@@ -147,11 +143,7 @@ fn erfc_inv(mut y: f64) -> f64 {
x += err / (FRAC_2_SQRT_PI * (-(x.powi(2))).exp() - x * err)
}
if y < 1.0 {
x
} else {
-x
}
if y < 1.0 { x } else { -x }
}
fn ppf(p: f64, mu: f64, sigma: f64) -> f64 {
@@ -239,9 +231,9 @@ pub(crate) fn sort_time(xs: &[i64], reverse: bool) -> Vec<usize> {
let mut x = xs.iter().enumerate().collect::<Vec<_>>();
if reverse {
x.sort_by_key(|(_, &x)| Reverse(x));
x.sort_by_key(|&(_, x)| Reverse(x));
} else {
x.sort_by_key(|(_, &x)| x);
x.sort_by_key(|&(_, x)| x);
}
x.into_iter().map(|(i, _)| i).collect()
@@ -256,6 +248,7 @@ pub(crate) fn evidence(d: &[DiffMessage], margin: &[f64], tie: &[bool], e: usize
}
}
/// Calculates the match quality of the given rating groups. A result is the draw probability in the association
pub fn quality(rating_groups: &[&[Gaussian]], beta: f64) -> f64 {
let flatten_ratings = rating_groups
.iter()
@@ -319,6 +312,8 @@ pub fn quality(rating_groups: &[&[Gaussian]], beta: f64) -> f64 {
#[cfg(test)]
mod tests {
use ::approx::assert_ulps_eq;
use super::*;
#[test]
@@ -330,4 +325,14 @@ mod tests {
fn test_sort_time() {
assert_eq!(sort_time(&[0, 1, 2, 0], true), vec![2, 1, 0, 3]);
}
#[test]
fn test_quality() {
let a = Gaussian::from_ms(25.0, 3.0);
let b = Gaussian::from_ms(25.0, 3.0);
let q = quality(&[&[a], &[b]], 25.0 / 3.0 / 2.0);
assert_ulps_eq!(q, 0.8115343414514944, epsilon = 1e-6)
}
}