Use PlayerIndex instead of String

This commit is contained in:
2022-06-14 22:51:11 +02:00
parent 3fbac02de3
commit 9b025fb53a
6 changed files with 352 additions and 254 deletions

View File

@@ -2,7 +2,15 @@
Rust port of [TrueSkillThroughTime.py](https://github.com/glandfried/TrueSkillThroughTime.py). Rust port of [TrueSkillThroughTime.py](https://github.com/glandfried/TrueSkillThroughTime.py).
## Other implementations
- [ttt-scala](https://github.com/ankurdave/ttt-scala)
- [ChessAnalysis #F](https://github.com/lucasmaystre/ChessAnalysis)
- [TrueSkillThroughTime.jl](https://github.com/glandfried/TrueSkillThroughTime.jl)
- [TrueSkillThroughTime.R](https://github.com/glandfried/TrueSkillThroughTime.R)
## Todo ## Todo
- [ ] Add examples (use same TrueSkillThroughTime.py) - [ ] Add examples (use same TrueSkillThroughTime.py)
- [ ] Change `time` to always be a u64 (or usize?) - [x] Change `time` to always be f64
- [ ] Add Observer (see [argmin](https://docs.rs/argmin/latest/argmin/core/trait.Observe.html) for inspiration)

View File

@@ -2,7 +2,27 @@ use std::collections::HashMap;
use time::Date; use time::Date;
use trueskill_tt::{History, BETA, MU, P_DRAW}; use trueskill_tt::{History, PlayerIndex, BETA, MU, P_DRAW};
struct Players(HashMap<String, PlayerIndex>);
impl Players {
fn new() -> Self {
Self(HashMap::new())
}
fn get(&mut self, name: &str) -> PlayerIndex {
if let Some(idx) = self.0.get(name) {
*idx
} else {
let idx = PlayerIndex::new(self.0.len());
self.0.insert(name.to_string(), idx);
idx
}
}
}
fn main() { fn main() {
let mut csv = csv::Reader::open("examples/atp.csv").unwrap(); let mut csv = csv::Reader::open("examples/atp.csv").unwrap();
@@ -13,17 +33,23 @@ fn main() {
let time_format = time::format_description::parse("[year]-[month]-[day]").unwrap(); let time_format = time::format_description::parse("[year]-[month]-[day]").unwrap();
let mut players = Players::new();
for row in csv.records() { for row in csv.records() {
if &row["double"] == "t" { if &row["double"] == "t" {
composition.push(vec![ let w1_id = players.get(&row["w1_id"]);
vec![row["w1_id"].to_string(), row["w2_id"].to_string()], let w2_id = players.get(&row["w2_id"]);
vec![row["l1_id"].to_string(), row["l2_id"].to_string()],
]); let l1_id = players.get(&row["l1_id"]);
let l2_id = players.get(&row["l2_id"]);
composition.push(vec![vec![w1_id, w2_id], vec![l1_id, l2_id]]);
} else { } else {
composition.push(vec![ let w1_id = players.get(&row["w1_id"]);
vec![row["w1_id"].to_string()],
vec![row["l1_id"].to_string()], let l1_id = players.get(&row["l1_id"]);
]);
composition.push(vec![vec![w1_id], vec![l1_id]]);
} }
results.push(vec![1, 0]); results.push(vec![1, 0]);
@@ -37,6 +63,49 @@ fn main() {
times.push(time as f64 / (60 * 60 * 24) as f64); times.push(time as f64 / (60 * 60 * 24) as f64);
} }
/*
let mut history = History::builder()
.sigma(1.6)
.gamma(0.036)
.priors(priors)
.build::<Time>();
history.add_event(teams: [[a, b], [c]], result: [1, 0], 0.0);
let mut history = History::builder()
.sigma(1.6)
.gamma(0.036)
.priors(priors)
.build::<NoTime>();
history.add_event(teams: [[a, b], [c]], result: [1, 0]);
history.convergence(Ctx::default());
history.convergence(Ctx { epsilon: 1.6, ..Default::default() });
history.convergence(Ctx { epsilon: 1.6, iterations: 10 });
history.convergence().run();
history
.convergence()
.epsilon(1.6)
.run();
history
.convergence()
.epsilon(1.6)
.iterations(10)
.run();
history
.convergence()
.epsilon(1.6)
.iterations(10)
.inspect(|step, i| println!("Iteration {}: step={:?}", i, step))
.run();
*/
let mut h = History::new( let mut h = History::new(
composition, composition,
results, results,
@@ -53,87 +122,6 @@ fn main() {
h.iterations = 10; h.iterations = 10;
h.convergence(); h.convergence();
/*
composition,
results,
times,
priors,
MU,
BETA,
SIGMA,
GAMMA,
P_DRAW,
*/
/*
let mut priors = HashMap::new();
for k in ["aj", "bj", "cj"] {
let player = Player::new(
Gaussian::new(25.0, 25.0 / 3.0),
25.0 / 6.0,
0.15 * 25.0 / 3.0,
N_INF,
);
priors.insert(k.to_string(), player);
}
let mut h1 = History::new(
composition,
results,
times,
priors,
MU,
BETA,
SIGMA,
GAMMA,
P_DRAW,
);
*/
/*
let columns = data
.lines()
.skip(1)
.map(|line| {
let columns = line.split(',').collect::<Vec<_>>();
Column {
w1_id: columns[3],
w2_id: columns[3],
l1_id: columns[3],
l2_id: columns[3],
double: columns[3],
}
})
.collect::<Vec<_>>();
*/
// match_id,double,round_number,w1_id,w1_name,w2_id,w2_name,l1_id,l1_name,l2_id,l2_name,time_start,time_end,ground,tour_id,tour_name
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
/*
import pandas as pd
#sudo pip3 install trueskillthroughtime
from trueskillthroughtime import *
import time
from datetime import datetime
# Data
df = pd.read_csv('input/history.csv', low_memory=False)
columns = zip(df.w1_id, df.w2_id, df.l1_id, df.l2_id, df.double)
composition = [[[w1,w2],[l1,l2]] if d == 't' else [[w1],[l1]] for w1, w2, l1, l2, d in columns ]
times = [ datetime.strptime(t, "%Y-%m-%d").timestamp()/(60*60*24) for t in df.time_start]
#start = time.time()
h = History(composition = composition, times = times, sigma = 1.6, gamma = 0.036)
h.convergence(epsilon=0.01, iterations=10)
#end = time.time()
#print(end-start)
*/
} }
mod csv { mod csv {

View File

@@ -1,6 +1,6 @@
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use crate::{Game, Gaussian, Player, N_INF}; use crate::{Game, Gaussian, Player, PlayerIndex, N_INF};
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Skill { pub struct Skill {
@@ -48,7 +48,7 @@ impl Agent {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Item { pub struct Item {
name: String, index: PlayerIndex,
likelihood: Gaussian, likelihood: Gaussian,
} }
@@ -65,6 +65,7 @@ pub struct Event {
} }
impl Event { impl Event {
/*
pub fn names(&self) -> Vec<&str> { pub fn names(&self) -> Vec<&str> {
self.teams self.teams
.iter() .iter()
@@ -72,6 +73,7 @@ impl Event {
.map(|item| item.name.as_str()) .map(|item| item.name.as_str())
.collect::<Vec<_>>() .collect::<Vec<_>>()
} }
*/
pub fn result(&self) -> Vec<u16> { pub fn result(&self) -> Vec<u16> {
self.teams self.teams
@@ -93,18 +95,28 @@ fn compute_elapsed(last_time: f64, actual_time: f64) -> f64 {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Batch { pub struct Batch {
pub skills: HashMap<String, Skill>, pub skills: HashMap<PlayerIndex, Skill>,
pub events: Vec<Event>, pub events: Vec<Event>,
pub time: f64, pub time: f64,
p_draw: f64, p_draw: f64,
} }
/*
fn test<S, I>(inp: S) where S: AsRef<[I]>, I: AsRef<[u8]> {
for a in inp.as_ref().iter() {
for b in a.as_ref().iter() {
println!("{}", b);
}
}
}
*/
impl Batch { impl Batch {
pub fn new<C: AsRef<str> + Clone>( pub fn new(
composition: Vec<Vec<Vec<C>>>, composition: Vec<Vec<Vec<PlayerIndex>>>,
results: Vec<Vec<u16>>, results: Vec<Vec<u16>>,
time: f64, time: f64,
agents: &mut HashMap<String, Agent>, agents: &mut HashMap<PlayerIndex, Agent>,
p_draw: f64, p_draw: f64,
) -> Self { ) -> Self {
let mut this = Self { let mut this = Self {
@@ -119,17 +131,16 @@ impl Batch {
this this
} }
pub fn add_events<C: AsRef<str> + Clone>( pub fn add_events(
&mut self, &mut self,
composition: Vec<Vec<Vec<C>>>, composition: Vec<Vec<Vec<PlayerIndex>>>,
results: Vec<Vec<u16>>, results: Vec<Vec<u16>>,
agents: &mut HashMap<String, Agent>, agents: &mut HashMap<PlayerIndex, Agent>,
) { ) {
let this_agent = composition let this_agent = composition
.iter() .iter()
.flatten() .flatten()
.flatten() .flatten()
.map(AsRef::as_ref)
.collect::<HashSet<_>>(); .collect::<HashSet<_>>();
for a in this_agent { for a in this_agent {
@@ -140,7 +151,7 @@ impl Batch {
skill.elapsed = elapsed; skill.elapsed = elapsed;
} else { } else {
self.skills.insert( self.skills.insert(
a.to_string(), *a,
Skill { Skill {
forward: agents[a].receive(elapsed), forward: agents[a].receive(elapsed),
elapsed, elapsed,
@@ -157,7 +168,7 @@ impl Batch {
.map(|t| { .map(|t| {
let items = (0..composition[e][t].len()) let items = (0..composition[e][t].len())
.map(|a| Item { .map(|a| Item {
name: composition[e][t][a].as_ref().to_string(), index: composition[e][t][a],
likelihood: N_INF, likelihood: N_INF,
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
@@ -180,22 +191,22 @@ impl Batch {
self.iteration(from, agents); self.iteration(from, agents);
} }
pub fn posterior(&self, agent: &str) -> Gaussian { pub fn posterior(&self, agent: &PlayerIndex) -> Gaussian {
let skill = &self.skills[agent]; let skill = &self.skills[agent];
skill.likelihood * skill.backward * skill.forward skill.likelihood * skill.backward * skill.forward
} }
pub fn posteriors(&self) -> HashMap<String, Gaussian> { pub fn posteriors(&self) -> HashMap<PlayerIndex, Gaussian> {
self.skills self.skills
.keys() .keys()
.map(|a| (a.clone(), self.posterior(a))) .map(|a| (*a, self.posterior(a)))
.collect::<HashMap<_, _>>() .collect::<HashMap<_, _>>()
} }
fn within_prior(&self, item: &Item, agents: &mut HashMap<String, Agent>) -> Player { fn within_prior(&self, item: &Item, agents: &mut HashMap<PlayerIndex, Agent>) -> Player {
let r = &agents[&item.name].player; let r = &agents[&item.index].player;
let g = self.posterior(&item.name) / item.likelihood; let g = self.posterior(&item.index) / item.likelihood;
Player::new(g, r.beta, r.gamma, N_INF) Player::new(g, r.beta, r.gamma, N_INF)
} }
@@ -203,7 +214,7 @@ impl Batch {
pub fn within_priors( pub fn within_priors(
&self, &self,
event: usize, event: usize,
agents: &mut HashMap<String, Agent>, agents: &mut HashMap<PlayerIndex, Agent>,
) -> Vec<Vec<Player>> { ) -> Vec<Vec<Player>> {
self.events[event] self.events[event]
.teams .teams
@@ -217,7 +228,7 @@ impl Batch {
.collect::<Vec<_>>() .collect::<Vec<_>>()
} }
fn iteration(&mut self, from: usize, agents: &mut HashMap<String, Agent>) { fn iteration(&mut self, from: usize, agents: &mut HashMap<PlayerIndex, Agent>) {
for e in from..self.events.len() { for e in from..self.events.len() {
let teams = self.within_priors(e, agents); let teams = self.within_priors(e, agents);
let result = self.events[e].result(); let result = self.events[e].result();
@@ -226,8 +237,8 @@ impl Batch {
for (t, team) in self.events[e].teams.iter_mut().enumerate() { for (t, team) in self.events[e].teams.iter_mut().enumerate() {
for (i, item) in team.items.iter_mut().enumerate() { for (i, item) in team.items.iter_mut().enumerate() {
self.skills.get_mut(&item.name).unwrap().likelihood = self.skills.get_mut(&item.index).unwrap().likelihood =
(self.skills[&item.name].likelihood / item.likelihood) (self.skills[&item.index].likelihood / item.likelihood)
* g.likelihoods[t][i]; * g.likelihoods[t][i];
item.likelihood = g.likelihoods[t][i]; item.likelihood = g.likelihoods[t][i];
@@ -238,7 +249,7 @@ impl Batch {
} }
} }
pub fn convergence(&mut self, agents: &mut HashMap<String, Agent>) -> usize { pub fn convergence(&mut self, agents: &mut HashMap<PlayerIndex, Agent>) -> usize {
let epsilon = 1e-6; let epsilon = 1e-6;
let iterations = 20; let iterations = 20;
@@ -267,20 +278,24 @@ impl Batch {
i i
} }
pub fn forward_prior_out(&self, agent: &str) -> Gaussian { pub fn forward_prior_out(&self, agent: &PlayerIndex) -> Gaussian {
let skill = &self.skills[agent]; let skill = &self.skills[agent];
skill.forward * skill.likelihood skill.forward * skill.likelihood
} }
pub fn backward_prior_out(&self, agent: &str, agents: &mut HashMap<String, Agent>) -> Gaussian { pub fn backward_prior_out(
&self,
agent: &PlayerIndex,
agents: &mut HashMap<PlayerIndex, Agent>,
) -> Gaussian {
let skill = &self.skills[agent]; let skill = &self.skills[agent];
let n = skill.likelihood * skill.backward; let n = skill.likelihood * skill.backward;
n.forget(agents[agent].player.gamma, skill.elapsed) n.forget(agents[agent].player.gamma, skill.elapsed)
} }
pub fn new_backward_info(&mut self, agents: &mut HashMap<String, Agent>) { pub fn new_backward_info(&mut self, agents: &mut HashMap<PlayerIndex, Agent>) {
for (agent, skill) in self.skills.iter_mut() { for (agent, skill) in self.skills.iter_mut() {
skill.backward = agents[agent].message; skill.backward = agents[agent].message;
} }
@@ -288,7 +303,7 @@ impl Batch {
self.iteration(0, agents); self.iteration(0, agents);
} }
pub fn new_forward_info(&mut self, agents: &mut HashMap<String, Agent>) { pub fn new_forward_info(&mut self, agents: &mut HashMap<PlayerIndex, Agent>) {
for (agent, skill) in self.skills.iter_mut() { for (agent, skill) in self.skills.iter_mut() {
skill.forward = agents[agent].receive(skill.elapsed); skill.forward = agents[agent].receive(skill.elapsed);
} }
@@ -307,7 +322,14 @@ mod tests {
fn test_one_event_each() { fn test_one_event_each() {
let mut agents = HashMap::new(); let mut agents = HashMap::new();
for k in ["a", "b", "c", "d", "e", "f"] { let a = PlayerIndex::new(0);
let b = PlayerIndex::new(1);
let c = PlayerIndex::new(2);
let d = PlayerIndex::new(3);
let e = PlayerIndex::new(4);
let f = PlayerIndex::new(5);
for k in [a, b, c, d, e, f] {
let agent = Agent::new( let agent = Agent::new(
Player::new( Player::new(
Gaussian::new(25.0, 25.0 / 3.0), Gaussian::new(25.0, 25.0 / 3.0),
@@ -319,14 +341,14 @@ mod tests {
f64::NEG_INFINITY, f64::NEG_INFINITY,
); );
agents.insert(k.to_string(), agent); agents.insert(k, agent);
} }
let mut b = Batch::new( let mut batch = Batch::new(
vec![ vec![
vec![vec!["a"], vec!["b"]], vec![vec![a], vec![b]],
vec![vec!["c"], vec!["d"]], vec![vec![c], vec![d]],
vec![vec!["e"], vec!["f"]], vec![vec![e], vec![f]],
], ],
vec![vec![1, 0], vec![0, 1], vec![1, 0]], vec![vec![1, 0], vec![0, 1], vec![1, 0]],
0.0, 0.0,
@@ -334,25 +356,32 @@ mod tests {
0.0, 0.0,
); );
let post = b.posteriors(); let post = batch.posteriors();
assert_eq!(post["a"].mu(), 29.205220743876975); assert_eq!(post[&a].mu(), 29.205220743876975);
assert_eq!(post["a"].sigma(), 7.194481422570443); assert_eq!(post[&a].sigma(), 7.194481422570443);
assert_eq!(post["b"].mu(), 20.79477925612302); assert_eq!(post[&b].mu(), 20.79477925612302);
assert_eq!(post["b"].sigma(), 7.194481422570443); assert_eq!(post[&b].sigma(), 7.194481422570443);
assert_eq!(post["c"].mu(), 20.79477925612302); assert_eq!(post[&c].mu(), 20.79477925612302);
assert_eq!(post["c"].sigma(), 7.194481422570443); assert_eq!(post[&c].sigma(), 7.194481422570443);
assert_eq!(b.convergence(&mut agents), 1); assert_eq!(batch.convergence(&mut agents), 1);
} }
#[test] #[test]
fn test_same_strength() { fn test_same_strength() {
let mut agents = HashMap::new(); let mut agents = HashMap::new();
for k in ["a", "b", "c", "d", "e", "f"] { let a = PlayerIndex::new(0);
let b = PlayerIndex::new(1);
let c = PlayerIndex::new(2);
let d = PlayerIndex::new(3);
let e = PlayerIndex::new(4);
let f = PlayerIndex::new(5);
for k in [a, b, c, d, e, f] {
let agent = Agent::new( let agent = Agent::new(
Player::new( Player::new(
Gaussian::new(25.0, 25.0 / 3.0), Gaussian::new(25.0, 25.0 / 3.0),
@@ -364,14 +393,14 @@ mod tests {
f64::NEG_INFINITY, f64::NEG_INFINITY,
); );
agents.insert(k.to_string(), agent); agents.insert(k, agent);
} }
let mut b = Batch::new( let mut batch = Batch::new(
vec![ vec![
vec![vec!["a"], vec!["b"]], vec![vec![a], vec![b]],
vec![vec!["a"], vec!["c"]], vec![vec![a], vec![c]],
vec![vec!["b"], vec!["c"]], vec![vec![b], vec![c]],
], ],
vec![vec![1, 0], vec![0, 1], vec![1, 0]], vec![vec![1, 0], vec![0, 1], vec![1, 0]],
2.0, 2.0,
@@ -379,28 +408,105 @@ mod tests {
0.0, 0.0,
); );
let post = b.posteriors(); let post = batch.posteriors();
assert_eq!(post["a"].mu(), 24.96097857478182); assert_eq!(post[&a].mu(), 24.96097857478182);
assert_eq!(post["a"].sigma(), 6.298544763358269); assert_eq!(post[&a].sigma(), 6.298544763358269);
assert_eq!(post["b"].mu(), 27.095590669107086); assert_eq!(post[&b].mu(), 27.095590669107086);
assert_eq!(post["b"].sigma(), 6.010330439043099); assert_eq!(post[&b].sigma(), 6.010330439043099);
assert_eq!(post["c"].mu(), 24.88968178743119); assert_eq!(post[&c].mu(), 24.88968178743119);
assert_eq!(post["c"].sigma(), 5.866311348102562); assert_eq!(post[&c].sigma(), 5.866311348102562);
assert!(b.convergence(&mut agents) > 1); assert!(batch.convergence(&mut agents) > 1);
let post = b.posteriors(); let post = batch.posteriors();
assert_ulps_eq!(post["a"].mu(), 25.000000, epsilon = 0.000001); assert_ulps_eq!(post[&a].mu(), 25.000000, epsilon = 0.000001);
assert_ulps_eq!(post["a"].sigma(), 5.4192120, epsilon = 0.000001); assert_ulps_eq!(post[&a].sigma(), 5.4192120, epsilon = 0.000001);
assert_ulps_eq!(post["b"].mu(), 25.000000, epsilon = 0.000001); assert_ulps_eq!(post[&b].mu(), 25.000000, epsilon = 0.000001);
assert_ulps_eq!(post["b"].sigma(), 5.4192120, epsilon = 0.000001); assert_ulps_eq!(post[&b].sigma(), 5.4192120, epsilon = 0.000001);
assert_ulps_eq!(post["c"].mu(), 25.000000, epsilon = 0.000001); assert_ulps_eq!(post[&c].mu(), 25.000000, epsilon = 0.000001);
assert_ulps_eq!(post["c"].sigma(), 5.4192120, epsilon = 0.000001); assert_ulps_eq!(post[&c].sigma(), 5.4192120, epsilon = 0.000001);
}
#[test]
fn test_add_events() {
let mut agents = HashMap::new();
let a = PlayerIndex::new(0);
let b = PlayerIndex::new(1);
let c = PlayerIndex::new(2);
let d = PlayerIndex::new(3);
let e = PlayerIndex::new(4);
let f = PlayerIndex::new(5);
for k in [a, b, c, d, e, f] {
let agent = Agent::new(
Player::new(
Gaussian::new(25.0, 25.0 / 3.0),
25.0 / 6.0,
25.0 / 300.0,
N_INF,
),
N_INF,
f64::NEG_INFINITY,
);
agents.insert(k, agent);
}
let mut batch = Batch::new(
vec![
vec![vec![a], vec![b]],
vec![vec![a], vec![c]],
vec![vec![b], vec![c]],
],
vec![vec![1, 0], vec![0, 1], vec![1, 0]],
0.0,
&mut agents,
0.0,
);
batch.convergence(&mut agents);
let p = batch.posteriors();
assert_ulps_eq!(p[&a].mu(), 25.000000, epsilon = 0.000001);
assert_ulps_eq!(p[&a].sigma(), 5.4192120, epsilon = 0.000001);
assert_ulps_eq!(p[&b].mu(), 25.000000, epsilon = 0.000001);
assert_ulps_eq!(p[&b].sigma(), 5.4192120, epsilon = 0.000001);
assert_ulps_eq!(p[&c].mu(), 25.000000, epsilon = 0.000001);
assert_ulps_eq!(p[&c].sigma(), 5.4192120, epsilon = 0.000001);
batch.add_events(
vec![
vec![vec![a], vec![b]],
vec![vec![a], vec![c]],
vec![vec![b], vec![c]],
],
vec![vec![1, 0], vec![0, 1], vec![1, 0]],
&mut agents,
);
assert_eq!(batch.events.len(), 6);
batch.convergence(&mut agents);
let p = batch.posteriors();
assert_ulps_eq!(p[&a].mu(), 25.00000315330858, epsilon = 0.000001);
assert_ulps_eq!(p[&a].sigma(), 3.880150268080797, epsilon = 0.000001);
assert_ulps_eq!(p[&b].mu(), 25.00000315330858, epsilon = 0.000001);
assert_ulps_eq!(p[&b].sigma(), 3.880150268080797, epsilon = 0.000001);
assert_ulps_eq!(p[&c].mu(), 25.00000315330858, epsilon = 0.000001);
assert_ulps_eq!(p[&c].sigma(), 3.880150268080797, epsilon = 0.000001);
} }
} }

View File

@@ -1,11 +1,11 @@
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use crate::{utils, Agent, Batch, Gaussian, Player, N_INF}; use crate::{utils, Agent, Batch, Gaussian, Player, PlayerIndex, N_INF};
pub struct History { pub struct History {
size: usize, size: usize,
pub batches: Vec<Batch>, batches: Vec<Batch>,
pub agents: HashMap<String, Agent>, agents: HashMap<PlayerIndex, Agent>,
mu: f64, mu: f64,
sigma: f64, sigma: f64,
gamma: f64, gamma: f64,
@@ -17,11 +17,11 @@ pub struct History {
} }
impl History { impl History {
pub fn new<C: AsRef<str> + Clone>( pub fn new(
composition: Vec<Vec<Vec<C>>>, composition: Vec<Vec<Vec<PlayerIndex>>>,
results: Vec<Vec<u16>>, results: Vec<Vec<u16>>,
times: Vec<f64>, times: Vec<f64>,
priors: HashMap<String, Player>, priors: HashMap<PlayerIndex, Player>,
mu: f64, mu: f64,
sigma: f64, sigma: f64,
beta: f64, beta: f64,
@@ -32,7 +32,6 @@ impl History {
.iter() .iter()
.flatten() .flatten()
.flatten() .flatten()
.map(AsRef::as_ref)
.collect::<HashSet<_>>(); .collect::<HashSet<_>>();
let agents = this_agent let agents = this_agent
@@ -44,7 +43,7 @@ impl History {
.unwrap_or_else(|| Player::new(Gaussian::new(mu, sigma), beta, gamma, N_INF)); .unwrap_or_else(|| Player::new(Gaussian::new(mu, sigma), beta, gamma, N_INF));
( (
a.to_string(), *a,
Agent { Agent {
player, player,
message: N_INF, message: N_INF,
@@ -73,9 +72,9 @@ impl History {
this this
} }
fn trueskill<C: AsRef<str> + Clone>( fn trueskill(
&mut self, &mut self,
composition: Vec<Vec<Vec<C>>>, composition: Vec<Vec<Vec<PlayerIndex>>>,
results: Vec<Vec<u16>>, results: Vec<Vec<u16>>,
times: Vec<f64>, times: Vec<f64>,
) { ) {
@@ -207,8 +206,8 @@ impl History {
(step, i) (step, i)
} }
pub fn learning_curves(&self) -> HashMap<String, Vec<(f64, Gaussian)>> { pub fn learning_curves(&self) -> HashMap<PlayerIndex, Vec<(f64, Gaussian)>> {
let mut data: HashMap<String, Vec<(f64, Gaussian)>> = HashMap::new(); let mut data: HashMap<PlayerIndex, Vec<(f64, Gaussian)>> = HashMap::new();
for b in &self.batches { for b in &self.batches {
for agent in b.skills.keys() { for agent in b.skills.keys() {
@@ -217,7 +216,7 @@ impl History {
if let Some(entry) = data.get_mut(agent) { if let Some(entry) = data.get_mut(agent) {
entry.push(point); entry.push(point);
} else { } else {
data.insert(agent.to_string(), vec![point]); data.insert(*agent, vec![point]);
} }
} }
} }
@@ -261,16 +260,20 @@ mod tests {
#[test] #[test]
fn test_init() { fn test_init() {
let a = PlayerIndex::new(0);
let b = PlayerIndex::new(1);
let c = PlayerIndex::new(2);
let composition = vec![ let composition = vec![
vec![vec!["a"], vec!["b"]], vec![vec![a], vec![b]],
vec![vec!["a"], vec!["c"]], vec![vec![a], vec![c]],
vec![vec!["b"], vec!["c"]], vec![vec![b], vec![c]],
]; ];
let results = vec![vec![1, 0], vec![0, 1], vec![1, 0]]; let results = vec![vec![1, 0], vec![0, 1], vec![1, 0]];
let mut priors = HashMap::new(); let mut priors = HashMap::new();
for k in ["a", "b", "c"] { for k in [a, b, c] {
let player = Player::new( let player = Player::new(
Gaussian::new(25.0, 25.0 / 3.0), Gaussian::new(25.0, 25.0 / 3.0),
25.0 / 6.0, 25.0 / 6.0,
@@ -278,7 +281,7 @@ mod tests {
N_INF, N_INF,
); );
priors.insert(k.to_string(), player); priors.insert(k, player);
} }
let mut h = History::new( let mut h = History::new(
@@ -295,16 +298,16 @@ mod tests {
let p0 = h.batches[0].posteriors(); let p0 = h.batches[0].posteriors();
assert_ulps_eq!(p0["a"].mu(), 29.205220743876975, epsilon = 0.000001); assert_ulps_eq!(p0[&a].mu(), 29.205220743876975, epsilon = 0.000001);
assert_ulps_eq!(p0["a"].sigma(), 7.194481422570443, epsilon = 0.000001); assert_ulps_eq!(p0[&a].sigma(), 7.194481422570443, epsilon = 0.000001);
let observed = h.batches[1].skills["a"].forward.sigma(); let observed = h.batches[1].skills[&a].forward.sigma();
let gamma: f64 = 0.15 * 25.0 / 3.0; let gamma: f64 = 0.15 * 25.0 / 3.0;
let expected = (gamma.powi(2) + h.batches[0].posterior("a").sigma().powi(2)).sqrt(); let expected = (gamma.powi(2) + h.batches[0].posterior(&a).sigma().powi(2)).sqrt();
assert_ulps_eq!(observed, expected, epsilon = 0.000001); assert_ulps_eq!(observed, expected, epsilon = 0.000001);
let observed = h.batches[1].posterior("a"); let observed = h.batches[1].posterior(&a);
let p = Game::new( let p = Game::new(
h.batches[1].within_priors(0, &mut h.agents), h.batches[1].within_priors(0, &mut h.agents),
vec![0, 1], vec![0, 1],
@@ -319,17 +322,21 @@ mod tests {
#[test] #[test]
fn test_one_batch() { fn test_one_batch() {
let a = PlayerIndex::new(0);
let b = PlayerIndex::new(1);
let c = PlayerIndex::new(2);
let composition = vec![ let composition = vec![
vec![vec!["aj"], vec!["bj"]], vec![vec![a], vec![b]],
vec![vec!["bj"], vec!["cj"]], vec![vec![b], vec![c]],
vec![vec!["cj"], vec!["aj"]], vec![vec![c], vec![a]],
]; ];
let results = vec![vec![1, 0], vec![1, 0], vec![1, 0]]; let results = vec![vec![1, 0], vec![1, 0], vec![1, 0]];
let times = vec![1.0, 1.0, 1.0]; let times = vec![1.0, 1.0, 1.0];
let mut priors = HashMap::new(); let mut priors = HashMap::new();
for k in ["aj", "bj", "cj"] { for k in [a, b, c] {
let player = Player::new( let player = Player::new(
Gaussian::new(25.0, 25.0 / 3.0), Gaussian::new(25.0, 25.0 / 3.0),
25.0 / 6.0, 25.0 / 6.0,
@@ -337,7 +344,7 @@ mod tests {
N_INF, N_INF,
); );
priors.insert(k.to_string(), player); priors.insert(k, player);
} }
let mut h1 = History::new( let mut h1 = History::new(
@@ -353,22 +360,22 @@ mod tests {
); );
assert_ulps_eq!( assert_ulps_eq!(
h1.batches[0].posterior("aj").mu(), h1.batches[0].posterior(&a).mu(),
22.904409330892914, 22.904409330892914,
epsilon = 0.000001 epsilon = 0.000001
); );
assert_ulps_eq!( assert_ulps_eq!(
h1.batches[0].posterior("aj").sigma(), h1.batches[0].posterior(&a).sigma(),
6.0103304390431, 6.0103304390431,
epsilon = 0.000001 epsilon = 0.000001
); );
assert_ulps_eq!( assert_ulps_eq!(
h1.batches[0].posterior("cj").mu(), h1.batches[0].posterior(&c).mu(),
25.110318212568806, 25.110318212568806,
epsilon = 0.000001 epsilon = 0.000001
); );
assert_ulps_eq!( assert_ulps_eq!(
h1.batches[0].posterior("cj").sigma(), h1.batches[0].posterior(&c).sigma(),
5.866311348102563, 5.866311348102563,
epsilon = 0.000001 epsilon = 0.000001
); );
@@ -376,37 +383,37 @@ mod tests {
let (_step, _i) = h1.convergence(); let (_step, _i) = h1.convergence();
assert_ulps_eq!( assert_ulps_eq!(
h1.batches[0].posterior("aj").mu(), h1.batches[0].posterior(&a).mu(),
25.00000000, 25.00000000,
epsilon = 0.000001 epsilon = 0.000001
); );
assert_ulps_eq!( assert_ulps_eq!(
h1.batches[0].posterior("aj").sigma(), h1.batches[0].posterior(&a).sigma(),
5.41921200, 5.41921200,
epsilon = 0.000001 epsilon = 0.000001
); );
assert_ulps_eq!( assert_ulps_eq!(
h1.batches[0].posterior("cj").mu(), h1.batches[0].posterior(&c).mu(),
25.00000000, 25.00000000,
epsilon = 0.000001 epsilon = 0.000001
); );
assert_ulps_eq!( assert_ulps_eq!(
h1.batches[0].posterior("cj").sigma(), h1.batches[0].posterior(&c).sigma(),
5.41921200, 5.41921200,
epsilon = 0.000001 epsilon = 0.000001
); );
let composition = vec![ let composition = vec![
vec![vec!["aj"], vec!["bj"]], vec![vec![a], vec![b]],
vec![vec!["bj"], vec!["cj"]], vec![vec![b], vec![c]],
vec![vec!["cj"], vec!["aj"]], vec![vec![c], vec![a]],
]; ];
let results = vec![vec![1, 0], vec![1, 0], vec![1, 0]]; let results = vec![vec![1, 0], vec![1, 0], vec![1, 0]];
let times = vec![1.0, 2.0, 3.0]; let times = vec![1.0, 2.0, 3.0];
let mut priors = HashMap::new(); let mut priors = HashMap::new();
for k in ["aj", "bj", "cj"] { for k in [a, b, c] {
let player = Player::new( let player = Player::new(
Gaussian::new(25.0, 25.0 / 3.0), Gaussian::new(25.0, 25.0 / 3.0),
25.0 / 6.0, 25.0 / 6.0,
@@ -414,7 +421,7 @@ mod tests {
N_INF, N_INF,
); );
priors.insert(k.to_string(), player); priors.insert(k, player);
} }
let mut h2 = History::new( let mut h2 = History::new(
@@ -430,22 +437,22 @@ mod tests {
); );
assert_ulps_eq!( assert_ulps_eq!(
h2.batches[2].posterior("aj").mu(), h2.batches[2].posterior(&a).mu(),
22.90352227792141, 22.90352227792141,
epsilon = 0.000001 epsilon = 0.000001
); );
assert_ulps_eq!( assert_ulps_eq!(
h2.batches[2].posterior("aj").sigma(), h2.batches[2].posterior(&a).sigma(),
6.011017301320632, 6.011017301320632,
epsilon = 0.000001 epsilon = 0.000001
); );
assert_ulps_eq!( assert_ulps_eq!(
h2.batches[2].posterior("cj").mu(), h2.batches[2].posterior(&c).mu(),
25.110702468366718, 25.110702468366718,
epsilon = 0.000001 epsilon = 0.000001
); );
assert_ulps_eq!( assert_ulps_eq!(
h2.batches[2].posterior("cj").sigma(), h2.batches[2].posterior(&c).sigma(),
5.866811597660157, 5.866811597660157,
epsilon = 0.000001 epsilon = 0.000001
); );
@@ -453,22 +460,22 @@ mod tests {
let (_step, _i) = h2.convergence(); let (_step, _i) = h2.convergence();
assert_ulps_eq!( assert_ulps_eq!(
h2.batches[2].posterior("aj").mu(), h2.batches[2].posterior(&a).mu(),
24.99866831022851, 24.99866831022851,
epsilon = 0.000001 epsilon = 0.000001
); );
assert_ulps_eq!( assert_ulps_eq!(
h2.batches[2].posterior("aj").sigma(), h2.batches[2].posterior(&a).sigma(),
5.420053708148435, 5.420053708148435,
epsilon = 0.000001 epsilon = 0.000001
); );
assert_ulps_eq!( assert_ulps_eq!(
h2.batches[2].posterior("cj").mu(), h2.batches[2].posterior(&c).mu(),
25.000532179593538, 25.000532179593538,
epsilon = 0.000001 epsilon = 0.000001
); );
assert_ulps_eq!( assert_ulps_eq!(
h2.batches[2].posterior("cj").sigma(), h2.batches[2].posterior(&c).sigma(),
5.419827012784138, 5.419827012784138,
epsilon = 0.000001 epsilon = 0.000001
); );
@@ -476,17 +483,21 @@ mod tests {
#[test] #[test]
fn test_learning_curves() { fn test_learning_curves() {
let a = PlayerIndex::new(0);
let b = PlayerIndex::new(1);
let c = PlayerIndex::new(2);
let composition = vec![ let composition = vec![
vec![vec!["aj"], vec!["bj"]], vec![vec![a], vec![b]],
vec![vec!["bj"], vec!["cj"]], vec![vec![b], vec![c]],
vec![vec!["cj"], vec!["aj"]], vec![vec![c], vec![a]],
]; ];
let results = vec![vec![1, 0], vec![1, 0], vec![1, 0]]; let results = vec![vec![1, 0], vec![1, 0], vec![1, 0]];
let times = vec![5.0, 6.0, 7.0]; let times = vec![5.0, 6.0, 7.0];
let mut priors = HashMap::new(); let mut priors = HashMap::new();
for k in ["aj", "bj", "cj"] { for k in [a, b, c] {
let player = Player::new( let player = Player::new(
Gaussian::new(25.0, 25.0 / 3.0), Gaussian::new(25.0, 25.0 / 3.0),
25.0 / 6.0, 25.0 / 6.0,
@@ -494,7 +505,7 @@ mod tests {
N_INF, N_INF,
); );
priors.insert(k.to_string(), player); priors.insert(k, player);
} }
let mut h = History::new( let mut h = History::new(
@@ -513,29 +524,29 @@ mod tests {
let lc = h.learning_curves(); let lc = h.learning_curves();
let aj_e = lc["aj"].len(); let aj_e = lc[&a].len();
let cj_e = lc["cj"].len(); let cj_e = lc[&c].len();
assert_eq!(lc["aj"][0].0, 5.0); assert_eq!(lc[&a][0].0, 5.0);
assert_eq!(lc["aj"][aj_e - 1].0, 7.0); assert_eq!(lc[&a][aj_e - 1].0, 7.0);
assert_ulps_eq!( assert_ulps_eq!(
lc["aj"][aj_e - 1].1.mu(), lc[&a][aj_e - 1].1.mu(),
24.99866831022851, 24.99866831022851,
epsilon = 0.000001 epsilon = 0.000001
); );
assert_ulps_eq!( assert_ulps_eq!(
lc["aj"][aj_e - 1].1.sigma(), lc[&a][aj_e - 1].1.sigma(),
5.420053708148435, 5.420053708148435,
epsilon = 0.000001 epsilon = 0.000001
); );
assert_ulps_eq!( assert_ulps_eq!(
lc["cj"][cj_e - 1].1.mu(), lc[&c][cj_e - 1].1.mu(),
25.000532179593538, 25.000532179593538,
epsilon = 0.000001 epsilon = 0.000001
); );
assert_ulps_eq!( assert_ulps_eq!(
lc["cj"][cj_e - 1].1.sigma(), lc[&c][cj_e - 1].1.sigma(),
5.419827012784138, 5.419827012784138,
epsilon = 0.000001 epsilon = 0.000001
); );
@@ -543,10 +554,14 @@ mod tests {
#[test] #[test]
fn test_env_ttt() { fn test_env_ttt() {
let a = PlayerIndex::new(0);
let b = PlayerIndex::new(1);
let c = PlayerIndex::new(2);
let composition = vec![ let composition = vec![
vec![vec!["a"], vec!["b"]], vec![vec![a], vec![b]],
vec![vec!["a"], vec!["c"]], vec![vec![a], vec![c]],
vec![vec!["b"], vec!["c"]], vec![vec![b], vec![c]],
]; ];
let results = vec![vec![1, 0], vec![0, 1], vec![1, 0]]; let results = vec![vec![1, 0], vec![0, 1], vec![1, 0]];
@@ -564,36 +579,36 @@ mod tests {
let (_step, _i) = h.convergence(); let (_step, _i) = h.convergence();
assert_eq!(h.batches[2].skills["b"].elapsed, 1.0); assert_eq!(h.batches[2].skills[&b].elapsed, 1.0);
assert_eq!(h.batches[2].skills["c"].elapsed, 1.0); assert_eq!(h.batches[2].skills[&c].elapsed, 1.0);
assert_ulps_eq!( assert_ulps_eq!(
h.batches[0].posterior("a").mu(), h.batches[0].posterior(&a).mu(),
25.0002673, 25.0002673,
epsilon = 0.000001 epsilon = 0.000001
); );
assert_ulps_eq!( assert_ulps_eq!(
h.batches[0].posterior("a").sigma(), h.batches[0].posterior(&a).sigma(),
5.41938162, 5.41938162,
epsilon = 0.000001 epsilon = 0.000001
); );
assert_ulps_eq!( assert_ulps_eq!(
h.batches[0].posterior("b").mu(), h.batches[0].posterior(&b).mu(),
24.999465, 24.999465,
epsilon = 0.000001 epsilon = 0.000001
); );
assert_ulps_eq!( assert_ulps_eq!(
h.batches[0].posterior("b").sigma(), h.batches[0].posterior(&b).sigma(),
5.419425831, 5.419425831,
epsilon = 0.000001 epsilon = 0.000001
); );
assert_ulps_eq!( assert_ulps_eq!(
h.batches[2].posterior("b").mu(), h.batches[2].posterior(&b).mu(),
25.00053219, 25.00053219,
epsilon = 0.000001 epsilon = 0.000001
); );
assert_ulps_eq!( assert_ulps_eq!(
h.batches[2].posterior("b").sigma(), h.batches[2].posterior(&b).sigma(),
5.419696790, 5.419696790,
epsilon = 0.000001 epsilon = 0.000001
); );

View File

@@ -1,28 +0,0 @@
use std::collections::HashMap;
use trueskill_tt::*;
fn main() {
let composition = vec![
vec![vec!["a"], vec!["b"]],
vec![vec!["a"], vec!["c"]],
vec![vec!["b"], vec!["c"]],
];
let results = vec![vec![1, 0], vec![0, 1], vec![1, 0]];
let mut h = History::new(
composition,
results,
vec![],
HashMap::new(),
25.0,
25.0 / 3.0,
25.0 / 6.0,
25.0 / 300.0,
0.0,
);
let (_step, _i) = h.convergence();
println!("{:#?}", h.batches);
}

View File

@@ -1,5 +1,14 @@
use crate::{Gaussian, BETA, GAMMA, N_INF}; use crate::{Gaussian, BETA, GAMMA, N_INF};
#[derive(Copy, Clone, Debug, Default, PartialEq, PartialOrd, Eq, Ord, Hash)]
pub struct PlayerIndex(usize);
impl PlayerIndex {
pub fn new(index: usize) -> Self {
Self(index)
}
}
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub struct Player { pub struct Player {
pub prior: Gaussian, pub prior: Gaussian,