Port from julia version instead
This commit is contained in:
@@ -3,6 +3,8 @@ name = "trueskill-tt"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
approx = "0.5.1"
|
approx = "0.5.1"
|
||||||
time = { version = "0.3.9", features = ["parsing"] }
|
time = { version = "0.3.9", features = ["parsing"] }
|
||||||
|
|||||||
@@ -11,6 +11,6 @@ Rust port of [TrueSkillThroughTime.py](https://github.com/glandfried/TrueSkillTh
|
|||||||
|
|
||||||
## Todo
|
## Todo
|
||||||
|
|
||||||
- [ ] Add examples (use same TrueSkillThroughTime.py)
|
- [ ] Change time from u64 to i64 so we can use i64::MIN in `batch::compute_elapsed()`
|
||||||
- [x] Change `time` to always be f64
|
- [ ] Add examples (use same TrueSkillThroughTime.(py|jl))
|
||||||
- [ ] Add Observer (see [argmin](https://docs.rs/argmin/latest/argmin/core/trait.Observe.html) for inspiration)
|
- [ ] Add Observer (see [argmin](https://docs.rs/argmin/latest/argmin/core/trait.Observe.html) for inspiration)
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/*
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use time::Date;
|
use time::Date;
|
||||||
@@ -193,3 +194,8 @@ mod csv {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|||||||
37
src/agent.rs
Normal file
37
src/agent.rs
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
use crate::{gaussian::Gaussian, player::Player, N_INF};
|
||||||
|
|
||||||
|
pub(crate) struct Agent {
|
||||||
|
pub(crate) player: Player,
|
||||||
|
pub(crate) message: Gaussian,
|
||||||
|
pub(crate) last_time: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Agent {
|
||||||
|
pub(crate) fn receive(&self, elapsed: u64) -> Gaussian {
|
||||||
|
if self.message != N_INF {
|
||||||
|
self.message.forget(self.player.gamma, elapsed)
|
||||||
|
} else {
|
||||||
|
self.player.prior
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Agent {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
player: Player::default(),
|
||||||
|
message: N_INF,
|
||||||
|
last_time: u64::MIN,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn clean<'a, A: Iterator<Item = &'a mut Agent>>(agents: A, last_time: bool) {
|
||||||
|
for a in agents {
|
||||||
|
a.message = N_INF;
|
||||||
|
|
||||||
|
if last_time {
|
||||||
|
a.last_time = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
500
src/batch.rs
500
src/batch.rs
@@ -1,13 +1,15 @@
|
|||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
use crate::{Game, Gaussian, Player, PlayerIndex, N_INF};
|
use crate::{
|
||||||
|
agent::Agent, game::Game, gaussian::Gaussian, player::Player, tuple_gt, tuple_max, N_INF,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
pub(crate) struct Skill {
|
||||||
pub struct Skill {
|
pub(crate) forward: Gaussian,
|
||||||
pub forward: Gaussian,
|
backward: Gaussian,
|
||||||
pub backward: Gaussian,
|
likelihood: Gaussian,
|
||||||
pub likelihood: Gaussian,
|
pub(crate) elapsed: u64,
|
||||||
pub elapsed: f64,
|
pub(crate) online: Gaussian,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Skill {
|
impl Default for Skill {
|
||||||
@@ -16,67 +18,30 @@ impl Default for Skill {
|
|||||||
forward: N_INF,
|
forward: N_INF,
|
||||||
backward: N_INF,
|
backward: N_INF,
|
||||||
likelihood: N_INF,
|
likelihood: N_INF,
|
||||||
elapsed: 0.0,
|
elapsed: 0,
|
||||||
|
online: N_INF,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
struct Item {
|
||||||
pub struct Agent {
|
agent: String,
|
||||||
pub player: Player,
|
|
||||||
pub message: Gaussian,
|
|
||||||
pub last_time: f64,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Agent {
|
|
||||||
pub fn new(player: Player, message: Gaussian, last_time: f64) -> Self {
|
|
||||||
Self {
|
|
||||||
player,
|
|
||||||
message,
|
|
||||||
last_time,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn receive(&self, elapsed: f64) -> Gaussian {
|
|
||||||
if self.message != N_INF {
|
|
||||||
self.message.forget(self.player.gamma, elapsed)
|
|
||||||
} else {
|
|
||||||
self.player.prior
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct Item {
|
|
||||||
index: PlayerIndex,
|
|
||||||
likelihood: Gaussian,
|
likelihood: Gaussian,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
struct Team {
|
||||||
pub struct Team {
|
|
||||||
items: Vec<Item>,
|
items: Vec<Item>,
|
||||||
output: u16,
|
output: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
struct Event {
|
||||||
pub struct Event {
|
|
||||||
teams: Vec<Team>,
|
teams: Vec<Team>,
|
||||||
pub evidence: f64,
|
evidence: f64,
|
||||||
|
weights: Vec<Vec<f64>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Event {
|
impl Event {
|
||||||
/*
|
fn outputs(&self) -> Vec<f64> {
|
||||||
pub fn names(&self) -> Vec<&str> {
|
|
||||||
self.teams
|
|
||||||
.iter()
|
|
||||||
.flat_map(|team| team.items.iter())
|
|
||||||
.map(|item| item.name.as_str())
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
pub fn result(&self) -> Vec<u16> {
|
|
||||||
self.teams
|
self.teams
|
||||||
.iter()
|
.iter()
|
||||||
.map(|team| team.output)
|
.map(|team| team.output)
|
||||||
@@ -84,75 +49,126 @@ impl Event {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compute_elapsed(last_time: f64, actual_time: f64) -> f64 {
|
pub(crate) struct Batch {
|
||||||
if last_time == f64::NEG_INFINITY {
|
events: Vec<Event>,
|
||||||
0.0
|
pub(crate) skills: HashMap<String, Skill>,
|
||||||
} else if last_time == f64::INFINITY {
|
pub(crate) time: u64,
|
||||||
1.0
|
|
||||||
} else {
|
|
||||||
actual_time - last_time
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct Batch {
|
|
||||||
pub skills: HashMap<PlayerIndex, Skill>,
|
|
||||||
pub events: Vec<Event>,
|
|
||||||
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(
|
pub(crate) fn new(
|
||||||
composition: Vec<Vec<Vec<PlayerIndex>>>,
|
composition: Vec<Vec<Vec<&str>>>,
|
||||||
results: Vec<Vec<u16>>,
|
results: Vec<Vec<f64>>,
|
||||||
time: f64,
|
weights: Vec<Vec<Vec<f64>>>,
|
||||||
agents: &mut HashMap<PlayerIndex, Agent>,
|
time: u64,
|
||||||
p_draw: f64,
|
p_draw: f64,
|
||||||
|
agents: &mut HashMap<String, Agent>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
assert!(
|
||||||
|
results.is_empty() || results.len() == composition.len(),
|
||||||
|
"TODO: Add a comment here"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
weights.is_empty() || weights.len() == composition.len(),
|
||||||
|
"TODO: Add a comment here"
|
||||||
|
);
|
||||||
|
|
||||||
|
let this_agent = composition
|
||||||
|
.iter()
|
||||||
|
.flatten()
|
||||||
|
.flatten()
|
||||||
|
.cloned()
|
||||||
|
.collect::<HashSet<_>>();
|
||||||
|
|
||||||
|
let elapsed = this_agent
|
||||||
|
.iter()
|
||||||
|
.map(|&a| (a, compute_elapsed(agents[a].last_time, time)))
|
||||||
|
.collect::<HashMap<_, _>>();
|
||||||
|
|
||||||
|
let skills = this_agent
|
||||||
|
.iter()
|
||||||
|
.map(|&a| {
|
||||||
|
(
|
||||||
|
a.to_string(),
|
||||||
|
Skill {
|
||||||
|
forward: agents[a].receive(elapsed[a]),
|
||||||
|
elapsed: elapsed[a],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect::<HashMap<_, _>>();
|
||||||
|
|
||||||
|
let events = (0..composition.len())
|
||||||
|
.map(|e| {
|
||||||
|
let teams = (0..composition[e].len())
|
||||||
|
.map(|t| {
|
||||||
|
let items = (0..composition[e][t].len())
|
||||||
|
.map(|a| Item {
|
||||||
|
agent: composition[e][t][a].to_string(),
|
||||||
|
likelihood: N_INF,
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
Team {
|
||||||
|
items,
|
||||||
|
output: if results.is_empty() {
|
||||||
|
(composition[e].len() - (t + 1)) as f64
|
||||||
|
} else {
|
||||||
|
results[e][t]
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
Event {
|
||||||
|
teams,
|
||||||
|
evidence: 0.0,
|
||||||
|
weights: if weights.is_empty() {
|
||||||
|
Vec::new()
|
||||||
|
} else {
|
||||||
|
weights[e].clone()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let mut this = Self {
|
let mut this = Self {
|
||||||
skills: HashMap::new(),
|
|
||||||
events: Vec::new(),
|
|
||||||
time,
|
time,
|
||||||
|
events,
|
||||||
|
skills,
|
||||||
p_draw,
|
p_draw,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.add_events(composition, results, agents);
|
this.iteration(0, agents);
|
||||||
|
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_events(
|
pub(crate) fn add_events(
|
||||||
&mut self,
|
&mut self,
|
||||||
composition: Vec<Vec<Vec<PlayerIndex>>>,
|
composition: Vec<Vec<Vec<&str>>>,
|
||||||
results: Vec<Vec<u16>>,
|
results: Vec<Vec<f64>>,
|
||||||
agents: &mut HashMap<PlayerIndex, Agent>,
|
weights: Vec<Vec<Vec<f64>>>,
|
||||||
|
agents: &mut HashMap<String, Agent>,
|
||||||
) {
|
) {
|
||||||
let this_agent = composition
|
let this_agent = composition
|
||||||
.iter()
|
.iter()
|
||||||
.flatten()
|
.flatten()
|
||||||
.flatten()
|
.flatten()
|
||||||
|
.cloned()
|
||||||
.collect::<HashSet<_>>();
|
.collect::<HashSet<_>>();
|
||||||
|
|
||||||
for a in this_agent {
|
for a in this_agent {
|
||||||
let elapsed = compute_elapsed(agents[a].last_time, self.time);
|
let elapsed = compute_elapsed(agents[a].last_time, self.time);
|
||||||
|
|
||||||
if let Some(skill) = self.skills.get_mut(a) {
|
if let Some(skill) = self.skills.get_mut(a) {
|
||||||
skill.forward = agents[a].receive(elapsed);
|
|
||||||
skill.elapsed = elapsed;
|
skill.elapsed = elapsed;
|
||||||
|
skill.forward = agents[a].receive(elapsed);
|
||||||
} else {
|
} else {
|
||||||
self.skills.insert(
|
self.skills.insert(
|
||||||
*a,
|
a.to_string(),
|
||||||
Skill {
|
Skill {
|
||||||
forward: agents[a].receive(elapsed),
|
forward: agents[a].receive(elapsed),
|
||||||
elapsed,
|
elapsed,
|
||||||
@@ -169,14 +185,18 @@ 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 {
|
||||||
index: composition[e][t][a],
|
agent: composition[e][t][a].to_string(),
|
||||||
likelihood: N_INF,
|
likelihood: N_INF,
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
Team {
|
Team {
|
||||||
items,
|
items,
|
||||||
output: results[e][t],
|
output: if results.is_empty() {
|
||||||
|
(composition[e].len() - (t + 1)) as f64
|
||||||
|
} else {
|
||||||
|
results[e][t]
|
||||||
|
},
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
@@ -184,6 +204,11 @@ impl Batch {
|
|||||||
let event = Event {
|
let event = Event {
|
||||||
teams,
|
teams,
|
||||||
evidence: 0.0,
|
evidence: 0.0,
|
||||||
|
weights: if weights.is_empty() {
|
||||||
|
Vec::new()
|
||||||
|
} else {
|
||||||
|
weights[e].clone()
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
self.events.push(event);
|
self.events.push(event);
|
||||||
@@ -192,33 +217,45 @@ impl Batch {
|
|||||||
self.iteration(from, agents);
|
self.iteration(from, agents);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
pub(crate) 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
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
pub(crate) fn posteriors(&self) -> HashMap<String, Gaussian> {
|
||||||
pub fn posteriors(&self) -> HashMap<PlayerIndex, Gaussian> {
|
|
||||||
self.skills
|
self.skills
|
||||||
.keys()
|
.keys()
|
||||||
.map(|a| (*a, self.posterior(a)))
|
.map(|a| (a.to_string(), self.posterior(a)))
|
||||||
.collect::<HashMap<_, _>>()
|
.collect::<HashMap<_, _>>()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
fn within_prior(
|
||||||
fn within_prior(&self, item: &Item, agents: &mut HashMap<PlayerIndex, Agent>) -> Player {
|
&self,
|
||||||
let r = &agents[&item.index].player;
|
item: &Item,
|
||||||
let g = self.posterior(&item.index) / item.likelihood;
|
online: bool,
|
||||||
|
forward: bool,
|
||||||
|
agents: &mut HashMap<String, Agent>,
|
||||||
|
) -> Player {
|
||||||
|
let r = &agents[&item.agent].player;
|
||||||
|
|
||||||
Player::new(g, r.beta, r.gamma, N_INF)
|
if online {
|
||||||
|
Player::new(self.skills[&item.agent].online, r.beta, r.gamma)
|
||||||
|
} else if forward {
|
||||||
|
Player::new(self.skills[&item.agent].forward, r.beta, r.gamma)
|
||||||
|
} else {
|
||||||
|
let wp = self.posterior(&item.agent) / item.likelihood;
|
||||||
|
|
||||||
|
Player::new(wp, r.beta, r.gamma)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn within_priors(
|
pub(crate) fn within_priors(
|
||||||
&self,
|
&self,
|
||||||
event: usize,
|
event: usize,
|
||||||
agents: &mut HashMap<PlayerIndex, Agent>,
|
online: bool,
|
||||||
|
forward: bool,
|
||||||
|
agents: &mut HashMap<String, Agent>,
|
||||||
) -> Vec<Vec<Player>> {
|
) -> Vec<Vec<Player>> {
|
||||||
self.events[event]
|
self.events[event]
|
||||||
.teams
|
.teams
|
||||||
@@ -226,23 +263,23 @@ impl Batch {
|
|||||||
.map(|team| {
|
.map(|team| {
|
||||||
team.items
|
team.items
|
||||||
.iter()
|
.iter()
|
||||||
.map(|item| self.within_prior(item, agents))
|
.map(|item| self.within_prior(item, online, forward, agents))
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn iteration(&mut self, from: usize, agents: &mut HashMap<PlayerIndex, Agent>) {
|
pub(crate) fn iteration(&mut self, from: usize, agents: &mut HashMap<String, 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, false, false, agents);
|
||||||
let result = self.events[e].result();
|
let result = self.events[e].outputs();
|
||||||
|
|
||||||
let g = Game::new(teams, result, self.p_draw);
|
let g = Game::new(teams, result, self.events[e].weights.clone(), self.p_draw);
|
||||||
|
|
||||||
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.index).unwrap().likelihood =
|
self.skills.get_mut(&item.agent).unwrap().likelihood =
|
||||||
(self.skills[&item.index].likelihood / item.likelihood)
|
(self.skills[&item.agent].likelihood / item.likelihood)
|
||||||
* g.likelihoods[t][i];
|
* g.likelihoods[t][i];
|
||||||
|
|
||||||
item.likelihood = g.likelihoods[t][i];
|
item.likelihood = g.likelihoods[t][i];
|
||||||
@@ -253,27 +290,22 @@ impl Batch {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn convergence(&mut self, agents: &mut HashMap<PlayerIndex, Agent>) -> usize {
|
pub(crate) fn convergence(&mut self, agents: &mut HashMap<String, Agent>) -> usize {
|
||||||
let epsilon = 1e-6;
|
let epsilon = 1e-6;
|
||||||
let iterations = 20;
|
let iterations = 20;
|
||||||
|
|
||||||
let mut step = (f64::INFINITY, f64::INFINITY);
|
let mut step = (f64::INFINITY, f64::INFINITY);
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
|
|
||||||
while (step.0 > epsilon || step.1 > epsilon) && i < iterations {
|
while tuple_gt(step, epsilon) && i < iterations {
|
||||||
let old = self.posteriors();
|
let old = self.posteriors();
|
||||||
|
|
||||||
self.iteration(0, agents);
|
self.iteration(0, agents);
|
||||||
|
|
||||||
let new = self.posteriors();
|
let new = self.posteriors();
|
||||||
|
|
||||||
step = old.iter().fold((0.0, 0.0), |(o_l, o_r), (a, old)| {
|
step = old.iter().fold((0.0, 0.0), |step, (a, old)| {
|
||||||
let (n_l, n_r) = old.delta(new[a]);
|
tuple_max(step, old.delta(new[a]))
|
||||||
|
|
||||||
(
|
|
||||||
if n_l > o_l { n_l } else { o_l },
|
|
||||||
if n_r > o_r { n_r } else { o_r },
|
|
||||||
)
|
|
||||||
});
|
});
|
||||||
|
|
||||||
i += 1;
|
i += 1;
|
||||||
@@ -282,18 +314,16 @@ impl Batch {
|
|||||||
i
|
i
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
pub(crate) 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
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
pub(crate) fn backward_prior_out(
|
||||||
pub fn backward_prior_out(
|
|
||||||
&self,
|
&self,
|
||||||
agent: &PlayerIndex,
|
agent: &str,
|
||||||
agents: &mut HashMap<PlayerIndex, Agent>,
|
agents: &mut HashMap<String, Agent>,
|
||||||
) -> Gaussian {
|
) -> Gaussian {
|
||||||
let skill = &self.skills[agent];
|
let skill = &self.skills[agent];
|
||||||
let n = skill.likelihood * skill.backward;
|
let n = skill.likelihood * skill.backward;
|
||||||
@@ -301,8 +331,7 @@ impl Batch {
|
|||||||
n.forget(agents[agent].player.gamma, skill.elapsed)
|
n.forget(agents[agent].player.gamma, skill.elapsed)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
pub(crate) 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;
|
||||||
}
|
}
|
||||||
@@ -310,8 +339,7 @@ impl Batch {
|
|||||||
self.iteration(0, agents);
|
self.iteration(0, agents);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
pub(crate) 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);
|
||||||
}
|
}
|
||||||
@@ -320,60 +348,69 @@ impl Batch {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn compute_elapsed(last_time: u64, actual_time: u64) -> u64 {
|
||||||
|
if last_time == u64::MIN {
|
||||||
|
0
|
||||||
|
} else if last_time == u64::MAX {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
actual_time - last_time
|
||||||
|
}
|
||||||
|
}
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use approx::assert_ulps_eq;
|
use approx::assert_ulps_eq;
|
||||||
|
|
||||||
|
use crate::{agent::Agent, player::Player};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_one_event_each() {
|
fn test_one_event_each() {
|
||||||
let mut agents = HashMap::new();
|
let mut agents = HashMap::new();
|
||||||
|
|
||||||
let a = PlayerIndex::new(0);
|
for agent in ["a", "b", "c", "d", "e", "f"] {
|
||||||
let b = PlayerIndex::new(1);
|
agents.insert(
|
||||||
let c = PlayerIndex::new(2);
|
agent.to_string(),
|
||||||
let d = PlayerIndex::new(3);
|
Agent {
|
||||||
let e = PlayerIndex::new(4);
|
player: Player::new(Gaussian::new(25.0, 25.0 / 3.0), 25.0 / 6.0, 25.0 / 300.0),
|
||||||
let f = PlayerIndex::new(5);
|
..Default::default()
|
||||||
|
},
|
||||||
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(
|
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, 0.0], vec![0.0, 1.0], vec![1.0, 0.0]],
|
||||||
|
vec![],
|
||||||
|
0,
|
||||||
0.0,
|
0.0,
|
||||||
&mut agents,
|
&mut agents,
|
||||||
0.0,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
let post = batch.posteriors();
|
let post = batch.posteriors();
|
||||||
|
|
||||||
assert_eq!(post[&a].mu(), 29.205220743876975);
|
assert_ulps_eq!(post["a"].mu, 29.205220743876975, epsilon = 0.000001);
|
||||||
assert_eq!(post[&a].sigma(), 7.194481422570443);
|
assert_ulps_eq!(post["a"].sigma, 7.194481422570443, epsilon = 0.000001);
|
||||||
|
|
||||||
assert_eq!(post[&b].mu(), 20.79477925612302);
|
assert_ulps_eq!(post["b"].mu, 20.79477925612302, epsilon = 0.000001);
|
||||||
assert_eq!(post[&b].sigma(), 7.194481422570443);
|
assert_ulps_eq!(post["b"].sigma, 7.194481422570443, epsilon = 0.000001);
|
||||||
|
|
||||||
assert_eq!(post[&c].mu(), 20.79477925612302);
|
assert_ulps_eq!(post["c"].mu, 20.79477925612302, epsilon = 0.000001);
|
||||||
assert_eq!(post[&c].sigma(), 7.194481422570443);
|
assert_ulps_eq!(post["c"].sigma, 7.194481422570443, epsilon = 0.000001);
|
||||||
|
|
||||||
|
assert_ulps_eq!(post["d"].mu, 29.205220743876975, epsilon = 0.000001);
|
||||||
|
assert_ulps_eq!(post["d"].sigma, 7.194481422570443, epsilon = 0.000001);
|
||||||
|
|
||||||
|
assert_ulps_eq!(post["e"].mu, 29.205220743876975, epsilon = 0.000001);
|
||||||
|
assert_ulps_eq!(post["e"].sigma, 7.194481422570443, epsilon = 0.000001);
|
||||||
|
|
||||||
|
assert_ulps_eq!(post["f"].mu, 20.79477925612302, epsilon = 0.000001);
|
||||||
|
assert_ulps_eq!(post["f"].sigma, 7.194481422570443, epsilon = 0.000001);
|
||||||
|
|
||||||
assert_eq!(batch.convergence(&mut agents), 1);
|
assert_eq!(batch.convergence(&mut agents), 1);
|
||||||
}
|
}
|
||||||
@@ -382,123 +419,102 @@ mod tests {
|
|||||||
fn test_same_strength() {
|
fn test_same_strength() {
|
||||||
let mut agents = HashMap::new();
|
let mut agents = HashMap::new();
|
||||||
|
|
||||||
let a = PlayerIndex::new(0);
|
for agent in ["a", "b", "c", "d", "e", "f"] {
|
||||||
let b = PlayerIndex::new(1);
|
agents.insert(
|
||||||
let c = PlayerIndex::new(2);
|
agent.to_string(),
|
||||||
let d = PlayerIndex::new(3);
|
Agent {
|
||||||
let e = PlayerIndex::new(4);
|
player: Player::new(Gaussian::new(25.0, 25.0 / 3.0), 25.0 / 6.0, 25.0 / 300.0),
|
||||||
let f = PlayerIndex::new(5);
|
..Default::default()
|
||||||
|
},
|
||||||
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(
|
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, 0.0], vec![0.0, 1.0], vec![1.0, 0.0]],
|
||||||
2.0,
|
vec![],
|
||||||
&mut agents,
|
0,
|
||||||
0.0,
|
0.0,
|
||||||
|
&mut agents,
|
||||||
);
|
);
|
||||||
|
|
||||||
let post = batch.posteriors();
|
let post = batch.posteriors();
|
||||||
|
|
||||||
assert_eq!(post[&a].mu(), 24.96097857478182);
|
assert_ulps_eq!(post["a"].mu, 24.96097857478182, epsilon = 0.000001);
|
||||||
assert_eq!(post[&a].sigma(), 6.298544763358269);
|
assert_ulps_eq!(post["a"].sigma, 6.298544763358269, epsilon = 0.000001);
|
||||||
|
|
||||||
assert_eq!(post[&b].mu(), 27.095590669107086);
|
assert_ulps_eq!(post["b"].mu, 27.095590669107086, epsilon = 0.000001);
|
||||||
assert_eq!(post[&b].sigma(), 6.010330439043099);
|
assert_ulps_eq!(post["b"].sigma, 6.010330439043099, epsilon = 0.000001);
|
||||||
|
|
||||||
assert_eq!(post[&c].mu(), 24.88968178743119);
|
assert_ulps_eq!(post["c"].mu, 24.88968178743119, epsilon = 0.000001);
|
||||||
assert_eq!(post[&c].sigma(), 5.866311348102562);
|
assert_ulps_eq!(post["c"].sigma, 5.866311348102562, epsilon = 0.000001);
|
||||||
|
|
||||||
assert!(batch.convergence(&mut agents) > 1);
|
assert!(batch.convergence(&mut agents) > 1);
|
||||||
|
|
||||||
let post = batch.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]
|
#[test]
|
||||||
fn test_add_events() {
|
fn test_add_events() {
|
||||||
let mut agents = HashMap::new();
|
let mut agents = HashMap::new();
|
||||||
|
|
||||||
let a = PlayerIndex::new(0);
|
for agent in ["a", "b", "c", "d", "e", "f"] {
|
||||||
let b = PlayerIndex::new(1);
|
agents.insert(
|
||||||
let c = PlayerIndex::new(2);
|
agent.to_string(),
|
||||||
let d = PlayerIndex::new(3);
|
Agent {
|
||||||
let e = PlayerIndex::new(4);
|
player: Player::new(Gaussian::new(25.0, 25.0 / 3.0), 25.0 / 6.0, 25.0 / 300.0),
|
||||||
let f = PlayerIndex::new(5);
|
..Default::default()
|
||||||
|
},
|
||||||
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(
|
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, 0.0], vec![0.0, 1.0], vec![1.0, 0.0]],
|
||||||
|
vec![],
|
||||||
|
0,
|
||||||
0.0,
|
0.0,
|
||||||
&mut agents,
|
&mut agents,
|
||||||
0.0,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
batch.convergence(&mut agents);
|
batch.convergence(&mut agents);
|
||||||
|
|
||||||
let p = batch.posteriors();
|
let post = batch.posteriors();
|
||||||
|
|
||||||
assert_ulps_eq!(p[&a].mu(), 25.000000, epsilon = 0.000001);
|
assert_ulps_eq!(post["a"].mu, 25.000000, epsilon = 0.000001);
|
||||||
assert_ulps_eq!(p[&a].sigma(), 5.4192120, epsilon = 0.000001);
|
assert_ulps_eq!(post["a"].sigma, 5.4192120, epsilon = 0.000001);
|
||||||
|
|
||||||
assert_ulps_eq!(p[&b].mu(), 25.000000, epsilon = 0.000001);
|
assert_ulps_eq!(post["b"].mu, 25.000000, epsilon = 0.000001);
|
||||||
assert_ulps_eq!(p[&b].sigma(), 5.4192120, epsilon = 0.000001);
|
assert_ulps_eq!(post["b"].sigma, 5.4192120, epsilon = 0.000001);
|
||||||
|
|
||||||
assert_ulps_eq!(p[&c].mu(), 25.000000, epsilon = 0.000001);
|
assert_ulps_eq!(post["c"].mu, 25.000000, epsilon = 0.000001);
|
||||||
assert_ulps_eq!(p[&c].sigma(), 5.4192120, epsilon = 0.000001);
|
assert_ulps_eq!(post["c"].sigma, 5.4192120, epsilon = 0.000001);
|
||||||
|
|
||||||
batch.add_events(
|
batch.add_events(
|
||||||
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, 0.0], vec![0.0, 1.0], vec![1.0, 0.0]],
|
||||||
|
vec![],
|
||||||
&mut agents,
|
&mut agents,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -506,15 +522,15 @@ mod tests {
|
|||||||
|
|
||||||
batch.convergence(&mut agents);
|
batch.convergence(&mut agents);
|
||||||
|
|
||||||
let p = batch.posteriors();
|
let post = batch.posteriors();
|
||||||
|
|
||||||
assert_ulps_eq!(p[&a].mu(), 25.00000315330858, epsilon = 0.000001);
|
assert_ulps_eq!(post["a"].mu, 25.00000315330858, epsilon = 0.000001);
|
||||||
assert_ulps_eq!(p[&a].sigma(), 3.880150268080797, epsilon = 0.000001);
|
assert_ulps_eq!(post["a"].sigma, 3.880150268080797, epsilon = 0.000001);
|
||||||
|
|
||||||
assert_ulps_eq!(p[&b].mu(), 25.00000315330858, epsilon = 0.000001);
|
assert_ulps_eq!(post["b"].mu, 25.00000315330858, epsilon = 0.000001);
|
||||||
assert_ulps_eq!(p[&b].sigma(), 3.880150268080797, epsilon = 0.000001);
|
assert_ulps_eq!(post["b"].sigma, 3.880150268080797, epsilon = 0.000001);
|
||||||
|
|
||||||
assert_ulps_eq!(p[&c].mu(), 25.00000315330858, epsilon = 0.000001);
|
assert_ulps_eq!(post["c"].mu, 25.00000315330858, epsilon = 0.000001);
|
||||||
assert_ulps_eq!(p[&c].sigma(), 3.880150268080797, epsilon = 0.000001);
|
assert_ulps_eq!(post["c"].sigma, 3.880150268080797, epsilon = 0.000001);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
625
src/game.rs
625
src/game.rs
@@ -1,270 +1,222 @@
|
|||||||
use std::collections::HashSet;
|
use crate::{
|
||||||
|
approx, compute_margin, evidence,
|
||||||
use crate::{message::DiffMessages, utils, variable::TeamVariable, Gaussian, Player, N00};
|
gaussian::Gaussian,
|
||||||
|
message::{DiffMessage, TeamMessage},
|
||||||
|
player::Player,
|
||||||
|
sort_perm, tuple_gt, tuple_max, N00, N_INF,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct Game {
|
pub struct Game {
|
||||||
teams: Vec<Vec<Player>>,
|
teams: Vec<Vec<Player>>,
|
||||||
result: Vec<u16>,
|
result: Vec<f64>,
|
||||||
|
weights: Vec<Vec<f64>>,
|
||||||
p_draw: f64,
|
p_draw: f64,
|
||||||
pub likelihoods: Vec<Vec<Gaussian>>,
|
pub(crate) likelihoods: Vec<Vec<Gaussian>>,
|
||||||
pub evidence: f64,
|
pub(crate) evidence: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Game {
|
impl Game {
|
||||||
pub fn new(teams: Vec<Vec<Player>>, result: Vec<u16>, p_draw: f64) -> Self {
|
pub fn new(
|
||||||
if !result.is_empty() {
|
teams: Vec<Vec<Player>>,
|
||||||
debug_assert!(
|
mut result: Vec<f64>,
|
||||||
teams.len() == result.len(),
|
mut weights: Vec<Vec<f64>>,
|
||||||
"len(result) and (len(teams) != len(result))"
|
p_draw: f64,
|
||||||
|
) -> Self {
|
||||||
|
assert!(
|
||||||
|
(result.is_empty() || result.len() == teams.len()),
|
||||||
|
"result.must be empty or the same length as teams"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
(weights.is_empty() || weights.len() == teams.len()),
|
||||||
|
"weights.must be empty or the same length as teams"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
weights.is_empty()
|
||||||
|
|| weights
|
||||||
|
.iter()
|
||||||
|
.zip(teams.iter())
|
||||||
|
.all(|(w, t)| w.len() == t.len()),
|
||||||
|
"weights.must be empty or has the same dimensions as teams"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
(0.0..1.0).contains(&p_draw),
|
||||||
|
"draw probability.must be >= 0.0 and < 1.0"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
p_draw > 0.0 || {
|
||||||
|
let mut r = result.clone();
|
||||||
|
r.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap());
|
||||||
|
r.windows(2).all(|w| w[0] != w[1])
|
||||||
|
},
|
||||||
|
"draw.must be > 0.0 if there is teams with draw"
|
||||||
|
);
|
||||||
|
|
||||||
|
if result.is_empty() {
|
||||||
|
result = (0..teams.len()).rev().map(|i| i as f64).collect::<Vec<_>>();
|
||||||
}
|
}
|
||||||
|
|
||||||
debug_assert!(p_draw >= 0.0 && p_draw < 1.0, "0.0 <= p_draw < 1.0");
|
if weights.is_empty() {
|
||||||
|
weights = teams
|
||||||
if p_draw == 0.0 {
|
.iter()
|
||||||
debug_assert!(
|
.map(|team| vec![1.0; team.len()])
|
||||||
result.iter().collect::<HashSet<_>>().len() == result.len(),
|
.collect::<Vec<_>>();
|
||||||
"(p_draw == 0.0) and (len(result) > 0) and (len(set(result)) != len(result))"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut this = Self {
|
let mut this = Self {
|
||||||
teams,
|
teams,
|
||||||
result,
|
result,
|
||||||
|
weights,
|
||||||
p_draw,
|
p_draw,
|
||||||
likelihoods: Vec::new(),
|
likelihoods: Vec::new(),
|
||||||
evidence: 0.0,
|
evidence: 0.0,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.compute_likelihoods();
|
this.likelihoods();
|
||||||
|
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
fn performance(&self, index: usize) -> Gaussian {
|
fn likelihoods(&mut self) -> &Vec<Vec<Gaussian>> {
|
||||||
self.teams[index]
|
let m_t_ft = self.likelihood_teams();
|
||||||
|
|
||||||
|
self.likelihoods = self
|
||||||
|
.teams
|
||||||
.iter()
|
.iter()
|
||||||
.fold(N00, |sum, p| sum + p.performance())
|
.zip(self.weights.iter())
|
||||||
}
|
.zip(m_t_ft)
|
||||||
|
.map(|((p, w), m)| {
|
||||||
|
let performance = p.iter().zip(w.iter()).fold(N00, |p, (player, &weight)| {
|
||||||
|
p + (player.performance() * weight)
|
||||||
|
});
|
||||||
|
|
||||||
fn partial_evidence(&mut self, d: &[DiffMessages], margin: &[f64], tie: &[bool], e: usize) {
|
p.iter()
|
||||||
let mu = d[e].prior.mu();
|
.zip(w.iter())
|
||||||
let sigma = d[e].prior.sigma();
|
.map(|(p, &w)| {
|
||||||
|
((m - performance.exclude(p.performance() * w)) * (1.0 / w))
|
||||||
if tie[e] {
|
.forget(p.beta, 1)
|
||||||
self.evidence *= utils::cdf(margin[e], mu, sigma) - utils::cdf(-margin[e], mu, sigma)
|
})
|
||||||
} else {
|
.collect::<Vec<_>>()
|
||||||
self.evidence *= 1.0 - utils::cdf(margin[e], mu, sigma);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn graphical_model(
|
|
||||||
&mut self,
|
|
||||||
) -> (
|
|
||||||
Vec<usize>,
|
|
||||||
Vec<TeamVariable>,
|
|
||||||
Vec<DiffMessages>,
|
|
||||||
Vec<bool>,
|
|
||||||
Vec<f64>,
|
|
||||||
) {
|
|
||||||
if self.result.is_empty() {
|
|
||||||
self.result = (0..self.teams.len() as u16).rev().collect::<Vec<_>>();
|
|
||||||
}
|
|
||||||
|
|
||||||
let r = &self.result;
|
|
||||||
let o = utils::sort_perm(r);
|
|
||||||
|
|
||||||
let t = (0..self.teams.len())
|
|
||||||
.map(|e| TeamVariable {
|
|
||||||
prior: self.teams[o[e]]
|
|
||||||
.iter()
|
|
||||||
.fold(N00, |sum, p| sum + p.performance()),
|
|
||||||
..Default::default()
|
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let d = t
|
&self.likelihoods
|
||||||
.windows(2)
|
|
||||||
.map(|window| DiffMessages {
|
|
||||||
prior: window[0].prior - window[1].prior,
|
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let tie = (0..d.len())
|
|
||||||
.map(|e| r[o[e]] == r[o[e + 1]])
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let margin = (0..d.len())
|
|
||||||
.map(|e| {
|
|
||||||
if self.p_draw == 0.0 {
|
|
||||||
0.0
|
|
||||||
} else {
|
|
||||||
let a: f64 = self.teams[o[e]].iter().map(|a| a.beta.powi(2)).sum();
|
|
||||||
let b: f64 = self.teams[o[e + 1]].iter().map(|a| a.beta.powi(2)).sum();
|
|
||||||
|
|
||||||
utils::compute_margin(self.p_draw, (a + b).sqrt())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
self.evidence = 1.0;
|
|
||||||
|
|
||||||
(o, t, d, tie, margin)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn likelihood_analytical(&mut self) -> Vec<Vec<Gaussian>> {
|
|
||||||
let (o, t, d, tie, margin) = self.graphical_model();
|
|
||||||
|
|
||||||
self.partial_evidence(&d, &margin, &tie, 0);
|
|
||||||
|
|
||||||
let d = d[0].prior;
|
|
||||||
let (mu_trunc, sigma_trunc) = utils::trunc(d.mu(), d.sigma(), margin[0], tie[0]);
|
|
||||||
|
|
||||||
let (delta_div, theta_div_pow2) = if d.sigma() == sigma_trunc {
|
|
||||||
(
|
|
||||||
d.sigma().powi(2) * mu_trunc - sigma_trunc.powi(2) * d.mu(),
|
|
||||||
f64::INFINITY,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
(
|
|
||||||
(d.sigma().powi(2) * mu_trunc - sigma_trunc.powi(2) * d.mu())
|
|
||||||
/ (d.sigma().powi(2) - sigma_trunc.powi(2)),
|
|
||||||
(sigma_trunc.powi(2) * d.sigma().powi(2))
|
|
||||||
/ (d.sigma().powi(2) - sigma_trunc.powi(2)),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut res = Vec::new();
|
|
||||||
|
|
||||||
for i in 0..t.len() {
|
|
||||||
let mut team = Vec::new();
|
|
||||||
|
|
||||||
for j in 0..self.teams[o[i]].len() {
|
|
||||||
//
|
|
||||||
let mu = if d.sigma() == sigma_trunc {
|
|
||||||
0.0
|
|
||||||
} else {
|
|
||||||
self.teams[o[i]][j].prior.mu()
|
|
||||||
+ (delta_div - d.mu()) * (-1.0f64).powi(if i == 1 { 1 } else { 0 })
|
|
||||||
};
|
|
||||||
|
|
||||||
let sigma_analitico = (theta_div_pow2 + d.sigma().powi(2)
|
|
||||||
- self.teams[o[i]][j].prior.sigma().powi(2))
|
|
||||||
.sqrt();
|
|
||||||
|
|
||||||
team.push(Gaussian::new(mu, sigma_analitico));
|
|
||||||
}
|
|
||||||
|
|
||||||
res.push(team);
|
|
||||||
}
|
|
||||||
|
|
||||||
if o[0] >= o[1] {
|
|
||||||
res.swap(0, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
res
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn likelihood_teams(&mut self) -> Vec<Gaussian> {
|
fn likelihood_teams(&mut self) -> Vec<Gaussian> {
|
||||||
let (o, mut t, mut d, tie, margin) = self.graphical_model();
|
let o = sort_perm(&self.result, true);
|
||||||
|
|
||||||
|
let mut t = o
|
||||||
|
.iter()
|
||||||
|
.map(|&e| {
|
||||||
|
let performance = self.teams[e]
|
||||||
|
.iter()
|
||||||
|
.zip(self.weights[e].iter())
|
||||||
|
.fold(N00, |p, (player, &weight)| {
|
||||||
|
p + (player.performance() * weight)
|
||||||
|
});
|
||||||
|
|
||||||
|
TeamMessage {
|
||||||
|
prior: performance,
|
||||||
|
likelihood_lose: N_INF,
|
||||||
|
likelihood_win: N_INF,
|
||||||
|
likelihood_draw: N_INF,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let mut d = t
|
||||||
|
.windows(2)
|
||||||
|
.map(|w| DiffMessage {
|
||||||
|
prior: w[0].prior - w[1].prior,
|
||||||
|
likelihood: N_INF,
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let tie = o
|
||||||
|
.windows(2)
|
||||||
|
.map(|e| self.result[e[0]] == self.result[e[1]])
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let margin = if self.p_draw == 0.0 {
|
||||||
|
vec![0.0; o.len() - 1]
|
||||||
|
} else {
|
||||||
|
o.windows(2)
|
||||||
|
.map(|w| {
|
||||||
|
if self.p_draw == 0.0 {
|
||||||
|
0.0
|
||||||
|
} else {
|
||||||
|
let a: f64 = self.teams[w[0]].iter().map(|a| a.beta.powi(2)).sum();
|
||||||
|
let b: f64 = self.teams[w[1]].iter().map(|a| a.beta.powi(2)).sum();
|
||||||
|
|
||||||
|
compute_margin(self.p_draw, (a + b).sqrt())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
};
|
||||||
|
|
||||||
let mut step = (f64::INFINITY, f64::INFINITY);
|
let mut step = (f64::INFINITY, f64::INFINITY);
|
||||||
let mut i = 0;
|
let mut iter = 0;
|
||||||
|
|
||||||
while ((step.0 > 1e-6) || (step.1 > 1e-6)) && i < 10 {
|
while tuple_gt(step, 1e-6) && iter < 10 {
|
||||||
step = (0.0, 0.0);
|
step = (0.0, 0.0);
|
||||||
|
|
||||||
for e in 0..d.len() - 1 {
|
for e in 0..d.len() - 1 {
|
||||||
d[e].prior = t[e].posterior_win() - t[e + 1].posterior_lose();
|
d[e].prior = t[e].posterior_win() - t[e + 1].posterior_lose();
|
||||||
|
|
||||||
if i == 0 {
|
if iter == 0 {
|
||||||
let mu = d[e].prior.mu();
|
self.evidence *= evidence(&d, &margin, &tie, e);
|
||||||
let sigma = d[e].prior.sigma();
|
|
||||||
|
|
||||||
if tie[e] {
|
|
||||||
self.evidence *=
|
|
||||||
utils::cdf(margin[e], mu, sigma) - utils::cdf(-margin[e], mu, sigma)
|
|
||||||
} else {
|
|
||||||
self.evidence *= 1.0 - utils::cdf(margin[e], mu, sigma);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
d[e].likelihood = utils::approx(d[e].prior, margin[e], tie[e]) / d[e].prior;
|
d[e].likelihood = approx(d[e].prior, margin[e], tie[e]) / d[e].prior;
|
||||||
|
|
||||||
let likelihood_lose = t[e].posterior_win() - d[e].likelihood;
|
let likelihood_lose = t[e].posterior_win() - d[e].likelihood;
|
||||||
let delta = t[e + 1].likelihood_lose.delta(likelihood_lose);
|
step = tuple_max(step, t[e + 1].likelihood_lose.delta(likelihood_lose));
|
||||||
|
|
||||||
step = (
|
|
||||||
if step.0 > delta.0 { step.0 } else { delta.0 },
|
|
||||||
if step.1 > delta.1 { step.1 } else { delta.1 },
|
|
||||||
);
|
|
||||||
|
|
||||||
t[e + 1].likelihood_lose = likelihood_lose;
|
t[e + 1].likelihood_lose = likelihood_lose;
|
||||||
}
|
}
|
||||||
|
|
||||||
for e in (1..d.len()).rev() {
|
for e in (1..d.len()).rev() {
|
||||||
d[e].prior = t[e].posterior_win() - t[e + 1].posterior_lose();
|
d[e].prior = t[e].posterior_win() - t[e + 1].posterior_lose();
|
||||||
|
|
||||||
if i == 0 && e == d.len() - 1 {
|
if iter == 0 && e == d.len() - 1 {
|
||||||
self.partial_evidence(&d, &margin, &tie, e);
|
self.evidence *= evidence(&d, &margin, &tie, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
d[e].likelihood = utils::approx(d[e].prior, margin[e], tie[e]) / d[e].prior;
|
d[e].likelihood = approx(d[e].prior, margin[e], tie[e]) / d[e].prior;
|
||||||
|
|
||||||
let likelihood_win = t[e + 1].posterior_lose() + d[e].likelihood;
|
let likelihood_win = t[e + 1].posterior_lose() + d[e].likelihood;
|
||||||
let delta = t[e].likelihood_win.delta(likelihood_win);
|
step = tuple_max(step, t[e].likelihood_win.delta(likelihood_win));
|
||||||
|
|
||||||
step = (
|
|
||||||
if step.0 > delta.0 { step.0 } else { delta.0 },
|
|
||||||
if step.1 > delta.1 { step.1 } else { delta.1 },
|
|
||||||
);
|
|
||||||
|
|
||||||
t[e].likelihood_win = likelihood_win;
|
t[e].likelihood_win = likelihood_win;
|
||||||
}
|
}
|
||||||
|
|
||||||
i += 1;
|
iter += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if d.len() == 1 {
|
if d.len() == 1 {
|
||||||
self.partial_evidence(&d, &margin, &tie, 0);
|
self.evidence *= evidence(&d, &margin, &tie, 0);
|
||||||
|
|
||||||
d[0].prior = t[0].posterior_win() - t[1].posterior_lose();
|
d[0].prior = t[0].posterior_win() - t[1].posterior_lose();
|
||||||
d[0].likelihood = utils::approx(d[0].prior, margin[0], tie[0]) / d[0].prior;
|
d[0].likelihood = approx(d[0].prior, margin[0], tie[0]) / d[0].prior;
|
||||||
}
|
}
|
||||||
|
|
||||||
let t_e = t.len();
|
let t_end = t.len() - 1;
|
||||||
let d_e = d.len();
|
let d_end = d.len() - 1;
|
||||||
|
|
||||||
t[0].likelihood_win = t[1].posterior_lose() + d[0].likelihood;
|
t[0].likelihood_win = t[1].posterior_lose() + d[0].likelihood;
|
||||||
t[t_e - 1].likelihood_lose = t[t_e - 2].posterior_win() - d[d_e - 1].likelihood;
|
t[t_end].likelihood_lose = t[t_end - 1].posterior_win() - d[d_end].likelihood;
|
||||||
|
|
||||||
(0..t.len())
|
o.iter().map(|&e| t[e].likelihood()).collect::<Vec<_>>()
|
||||||
.map(|e| t[o[e]].likelihood())
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn compute_likelihoods(&mut self) {
|
|
||||||
if self.teams.len() > 2 {
|
|
||||||
let m_t_ft = self.likelihood_teams();
|
|
||||||
|
|
||||||
self.likelihoods = (0..self.teams.len())
|
|
||||||
.map(|e| {
|
|
||||||
(0..self.teams[e].len())
|
|
||||||
.map(|i| m_t_ft[e] - self.performance(e).exclude(self.teams[e][i].prior))
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
} else {
|
|
||||||
self.likelihoods = self.likelihood_analytical();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn posteriors(&self) -> Vec<Vec<Gaussian>> {
|
pub fn posteriors(&self) -> Vec<Vec<Gaussian>> {
|
||||||
(0..self.teams.len())
|
self.likelihoods
|
||||||
.map(|e| {
|
.iter()
|
||||||
(0..self.teams[e].len())
|
.zip(self.teams.iter())
|
||||||
.map(|i| self.likelihoods[e][i] * self.teams[e][i].prior)
|
.map(|(l, t)| {
|
||||||
|
l.iter()
|
||||||
|
.zip(t.iter())
|
||||||
|
.map(|(&l, p)| l * p.prior)
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
@@ -273,256 +225,183 @@ impl Game {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{Gaussian, Player, GAMMA, N_INF};
|
use crate::{Gaussian, Player, GAMMA};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_1vs1() {
|
fn test_1vs1() {
|
||||||
let t_a = Player::new(
|
let t_a = Player::new(Gaussian::new(25.0, 25.0 / 3.0), 25.0 / 6.0, 25.0 / 300.0);
|
||||||
Gaussian::new(25.0, 25.0 / 3.0),
|
let t_b = Player::new(Gaussian::new(25.0, 25.0 / 3.0), 25.0 / 6.0, 25.0 / 300.0);
|
||||||
25.0 / 6.0,
|
|
||||||
25.0 / 300.0,
|
|
||||||
N_INF,
|
|
||||||
);
|
|
||||||
|
|
||||||
let t_b = Player::new(
|
let g = Game::new(vec![vec![t_a], vec![t_b]], vec![0.0, 1.0], vec![], 0.0);
|
||||||
Gaussian::new(25.0, 25.0 / 3.0),
|
|
||||||
25.0 / 6.0,
|
|
||||||
25.0 / 300.0,
|
|
||||||
N_INF,
|
|
||||||
);
|
|
||||||
|
|
||||||
let g = Game::new(vec![vec![t_a], vec![t_b]], vec![0, 1], 0.0);
|
|
||||||
let p = g.posteriors();
|
let p = g.posteriors();
|
||||||
|
|
||||||
let a = p[0][0];
|
let a = p[0][0];
|
||||||
let b = p[1][0];
|
let b = p[1][0];
|
||||||
|
|
||||||
assert_eq!(a.mu(), 20.79477925612302);
|
assert_eq!(a.mu, 20.79477925612302);
|
||||||
assert_eq!(b.mu(), 29.205220743876975);
|
assert_eq!(b.mu, 29.205220743876975);
|
||||||
assert_eq!(a.sigma(), 7.194481422570443);
|
assert_eq!(a.sigma, 7.194481422570443);
|
||||||
|
|
||||||
let t_a = Player::new(Gaussian::new(29.0, 1.0), 25.0 / 6.0, GAMMA, N_INF);
|
let t_a = Player::new(Gaussian::new(29.0, 1.0), 25.0 / 6.0, GAMMA);
|
||||||
let t_b = Player::new(Gaussian::new(25.0, 25.0 / 3.0), 25.0 / 6.0, GAMMA, N_INF);
|
let t_b = Player::new(Gaussian::new(25.0, 25.0 / 3.0), 25.0 / 6.0, GAMMA);
|
||||||
|
|
||||||
let g = Game::new(vec![vec![t_a], vec![t_b]], vec![0, 1], 0.0);
|
let g = Game::new(vec![vec![t_a], vec![t_b]], vec![0.0, 1.0], vec![], 0.0);
|
||||||
let p = g.posteriors();
|
let p = g.posteriors();
|
||||||
|
|
||||||
let a = p[0][0];
|
let a = p[0][0];
|
||||||
let b = p[1][0];
|
let b = p[1][0];
|
||||||
|
|
||||||
assert_eq!(a.mu(), 28.896475351225412);
|
assert_eq!(a.mu, 28.896475351225412);
|
||||||
assert_eq!(a.sigma(), 0.9966043313004235);
|
assert_eq!(a.sigma, 0.9966043313004235);
|
||||||
assert_eq!(b.mu(), 32.18921172045737);
|
assert_eq!(b.mu, 32.18921172045737);
|
||||||
assert_eq!(b.sigma(), 6.062063735879715);
|
assert_eq!(b.sigma, 6.062063735879715);
|
||||||
|
|
||||||
let t_a = Player::new(Gaussian::new(1.139, 0.531), 1.0, 0.2125, N_INF);
|
let t_a = Player::new(Gaussian::new(1.139, 0.531), 1.0, 0.2125);
|
||||||
let t_b = Player::new(Gaussian::new(15.568, 0.51), 1.0, 0.2125, N_INF);
|
let t_b = Player::new(Gaussian::new(15.568, 0.51), 1.0, 0.2125);
|
||||||
|
|
||||||
let g = Game::new(vec![vec![t_a], vec![t_b]], vec![0, 1], 0.0);
|
let g = Game::new(vec![vec![t_a], vec![t_b]], vec![0.0, 1.0], vec![], 0.0);
|
||||||
|
|
||||||
assert_eq!(g.likelihoods[0][0].sigma(), f64::INFINITY);
|
assert_eq!(g.likelihoods[0][0].sigma, f64::INFINITY);
|
||||||
assert_eq!(g.likelihoods[1][0].sigma(), f64::INFINITY);
|
assert_eq!(g.likelihoods[1][0].sigma, f64::INFINITY);
|
||||||
assert_eq!(g.likelihoods[0][0].mu(), 0.0);
|
assert_eq!(g.likelihoods[0][0].mu, 0.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_1vs1vs1() {
|
fn test_1vs1vs1() {
|
||||||
let t_a = Player::new(
|
let teams = vec![
|
||||||
|
vec![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,
|
||||||
25.0 / 300.0,
|
25.0 / 300.0,
|
||||||
N_INF,
|
)],
|
||||||
);
|
vec![Player::new(
|
||||||
let t_b = 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,
|
||||||
25.0 / 300.0,
|
25.0 / 300.0,
|
||||||
N_INF,
|
)],
|
||||||
);
|
vec![Player::new(
|
||||||
let t_c = 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,
|
||||||
25.0 / 300.0,
|
25.0 / 300.0,
|
||||||
N_INF,
|
)],
|
||||||
);
|
];
|
||||||
|
|
||||||
let g = Game::new(vec![vec![t_a], vec![t_b], vec![t_c]], vec![1, 2, 0], 0.0);
|
let g = Game::new(teams.clone(), vec![1.0, 2.0, 0.0], vec![], 0.0);
|
||||||
|
let p = g.posteriors();
|
||||||
|
|
||||||
|
let a = p[0][0];
|
||||||
|
let b = p[1][0];
|
||||||
|
|
||||||
|
assert_eq!(a.mu, 25.00000000000592);
|
||||||
|
assert_eq!(a.sigma, 6.238469796269066);
|
||||||
|
assert_eq!(b.mu, 31.31135822129149);
|
||||||
|
assert_eq!(b.sigma, 6.69881865477675);
|
||||||
|
|
||||||
|
let g = Game::new(teams.clone(), vec![], vec![], 0.0);
|
||||||
|
let p = g.posteriors();
|
||||||
|
|
||||||
|
let a = p[0][0];
|
||||||
|
let b = p[1][0];
|
||||||
|
|
||||||
|
assert_eq!(a.mu, 31.31135822129149);
|
||||||
|
assert_eq!(a.sigma, 6.69881865477675);
|
||||||
|
assert_eq!(b.mu, 25.00000000000592);
|
||||||
|
assert_eq!(b.sigma, 6.238469796269066);
|
||||||
|
|
||||||
|
let g = Game::new(teams, vec![1.0, 2.0, 0.0], vec![], 0.5);
|
||||||
let p = g.posteriors();
|
let p = g.posteriors();
|
||||||
|
|
||||||
let a = p[0][0];
|
let a = p[0][0];
|
||||||
let b = p[1][0];
|
let b = p[1][0];
|
||||||
let c = p[2][0];
|
let c = p[2][0];
|
||||||
|
|
||||||
assert_eq!(a.mu(), 25.00000000000592);
|
assert_eq!(a.mu, 24.999999999511545);
|
||||||
assert_eq!(a.sigma(), 6.238469796269066);
|
assert_eq!(a.sigma, 6.092561128305945);
|
||||||
assert_eq!(b.mu(), 31.31135822129149);
|
assert_eq!(b.mu, 33.37931495595287);
|
||||||
assert_eq!(b.sigma(), 6.69881865477675);
|
assert_eq!(b.sigma, 6.483575782278924);
|
||||||
assert_eq!(c.mu(), 18.688641778702593);
|
assert_eq!(c.mu, 16.62068504453558);
|
||||||
assert_eq!(c.sigma(), 6.698818654778007);
|
assert_eq!(c.sigma, 6.483575782198122);
|
||||||
|
|
||||||
let t_a = Player::new(
|
|
||||||
Gaussian::new(25.0, 25.0 / 3.0),
|
|
||||||
25.0 / 6.0,
|
|
||||||
25.0 / 300.0,
|
|
||||||
N_INF,
|
|
||||||
);
|
|
||||||
let t_b = Player::new(
|
|
||||||
Gaussian::new(25.0, 25.0 / 3.0),
|
|
||||||
25.0 / 6.0,
|
|
||||||
25.0 / 300.0,
|
|
||||||
N_INF,
|
|
||||||
);
|
|
||||||
let t_c = Player::new(
|
|
||||||
Gaussian::new(25.0, 25.0 / 3.0),
|
|
||||||
25.0 / 6.0,
|
|
||||||
25.0 / 300.0,
|
|
||||||
N_INF,
|
|
||||||
);
|
|
||||||
|
|
||||||
let g = Game::new(vec![vec![t_a], vec![t_b], vec![t_c]], vec![2, 1, 0], 0.0);
|
|
||||||
let p = g.posteriors();
|
|
||||||
|
|
||||||
let a = p[0][0];
|
|
||||||
let b = p[1][0];
|
|
||||||
let c = p[2][0];
|
|
||||||
|
|
||||||
assert_eq!(a.mu(), 31.31135822129149);
|
|
||||||
assert_eq!(a.sigma(), 6.69881865477675);
|
|
||||||
assert_eq!(b.mu(), 25.00000000000592);
|
|
||||||
assert_eq!(b.sigma(), 6.238469796269066);
|
|
||||||
assert_eq!(c.mu(), 18.688641778702593);
|
|
||||||
assert_eq!(c.sigma(), 6.698818654778007);
|
|
||||||
|
|
||||||
let t_a = Player::new(
|
|
||||||
Gaussian::new(25.0, 25.0 / 3.0),
|
|
||||||
25.0 / 6.0,
|
|
||||||
25.0 / 300.0,
|
|
||||||
N_INF,
|
|
||||||
);
|
|
||||||
let t_b = Player::new(
|
|
||||||
Gaussian::new(25.0, 25.0 / 3.0),
|
|
||||||
25.0 / 6.0,
|
|
||||||
25.0 / 300.0,
|
|
||||||
N_INF,
|
|
||||||
);
|
|
||||||
let t_c = Player::new(
|
|
||||||
Gaussian::new(25.0, 25.0 / 3.0),
|
|
||||||
25.0 / 6.0,
|
|
||||||
25.0 / 300.0,
|
|
||||||
N_INF,
|
|
||||||
);
|
|
||||||
|
|
||||||
let g = Game::new(vec![vec![t_a], vec![t_b], vec![t_c]], vec![1, 2, 0], 0.5);
|
|
||||||
let p = g.posteriors();
|
|
||||||
|
|
||||||
let a = p[0][0];
|
|
||||||
let b = p[1][0];
|
|
||||||
let c = p[2][0];
|
|
||||||
|
|
||||||
assert_eq!(a.mu(), 24.999999999511545);
|
|
||||||
assert_eq!(a.sigma(), 6.092561128305945);
|
|
||||||
assert_eq!(b.mu(), 33.37931495595287);
|
|
||||||
assert_eq!(b.sigma(), 6.483575782278924);
|
|
||||||
assert_eq!(c.mu(), 16.62068504453558);
|
|
||||||
assert_eq!(c.sigma(), 6.483575782198122);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_1vs1_draw() {
|
fn test_1vs1_draw() {
|
||||||
let t_a = Player::new(
|
let t_a = Player::new(Gaussian::new(25.0, 25.0 / 3.0), 25.0 / 6.0, 25.0 / 300.0);
|
||||||
Gaussian::new(25.0, 25.0 / 3.0),
|
let t_b = Player::new(Gaussian::new(25.0, 25.0 / 3.0), 25.0 / 6.0, 25.0 / 300.0);
|
||||||
25.0 / 6.0,
|
|
||||||
25.0 / 300.0,
|
|
||||||
N_INF,
|
|
||||||
);
|
|
||||||
|
|
||||||
let t_b = Player::new(
|
let g = Game::new(vec![vec![t_a], vec![t_b]], vec![0.0, 0.0], vec![], 0.25);
|
||||||
Gaussian::new(25.0, 25.0 / 3.0),
|
|
||||||
25.0 / 6.0,
|
|
||||||
25.0 / 300.0,
|
|
||||||
N_INF,
|
|
||||||
);
|
|
||||||
|
|
||||||
let g = Game::new(vec![vec![t_a], vec![t_b]], vec![0, 0], 0.25);
|
|
||||||
let p = g.posteriors();
|
let p = g.posteriors();
|
||||||
|
|
||||||
let a = p[0][0];
|
let a = p[0][0];
|
||||||
let b = p[1][0];
|
let b = p[1][0];
|
||||||
|
|
||||||
assert_eq!(a.mu(), 25.0);
|
assert_eq!(a.mu, 24.999999999999996);
|
||||||
assert_eq!(a.sigma(), 6.469480769842277);
|
assert_eq!(a.sigma, 6.469480769842277);
|
||||||
assert_eq!(b.mu(), 25.0);
|
assert_eq!(b.mu, 24.999999999999996);
|
||||||
assert_eq!(b.sigma(), 6.469480769842277);
|
assert_eq!(b.sigma, 6.469480769842277);
|
||||||
|
|
||||||
let t_a = Player::new(Gaussian::new(25.0, 3.0), 25.0 / 6.0, 25.0 / 300.0, N_INF);
|
let t_a = Player::new(Gaussian::new(25.0, 3.0), 25.0 / 6.0, 25.0 / 300.0);
|
||||||
|
let t_b = Player::new(Gaussian::new(29.0, 2.0), 25.0 / 6.0, 25.0 / 300.0);
|
||||||
|
|
||||||
let t_b = Player::new(Gaussian::new(29.0, 2.0), 25.0 / 6.0, 25.0 / 300.0, N_INF);
|
let g = Game::new(vec![vec![t_a], vec![t_b]], vec![0.0, 0.0], vec![], 0.25);
|
||||||
|
|
||||||
let g = Game::new(vec![vec![t_a], vec![t_b]], vec![0, 0], 0.25);
|
|
||||||
let p = g.posteriors();
|
let p = g.posteriors();
|
||||||
|
|
||||||
let a = p[0][0];
|
let a = p[0][0];
|
||||||
let b = p[1][0];
|
let b = p[1][0];
|
||||||
|
|
||||||
assert_eq!(a.mu(), 25.736001810566616);
|
assert_eq!(a.mu, 25.73600181056662);
|
||||||
assert_eq!(a.sigma(), 2.709956162204711);
|
assert_eq!(a.sigma, 2.709956162204711);
|
||||||
assert_eq!(b.mu(), 28.67288808419261);
|
assert_eq!(b.mu, 28.67288808419261);
|
||||||
assert_eq!(b.sigma(), 1.9164711604544398);
|
assert_eq!(b.sigma, 1.9164711604544398);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_1vs1vs1_draw() {
|
fn test_1vs1vs1_draw() {
|
||||||
let t_a = Player::new(
|
let t_a = Player::new(Gaussian::new(25.0, 25.0 / 3.0), 25.0 / 6.0, 25.0 / 300.0);
|
||||||
Gaussian::new(25.0, 25.0 / 3.0),
|
let t_b = Player::new(Gaussian::new(25.0, 25.0 / 3.0), 25.0 / 6.0, 25.0 / 300.0);
|
||||||
25.0 / 6.0,
|
let t_c = Player::new(Gaussian::new(25.0, 25.0 / 3.0), 25.0 / 6.0, 25.0 / 300.0);
|
||||||
25.0 / 300.0,
|
|
||||||
N_INF,
|
|
||||||
);
|
|
||||||
let t_b = Player::new(
|
|
||||||
Gaussian::new(25.0, 25.0 / 3.0),
|
|
||||||
25.0 / 6.0,
|
|
||||||
25.0 / 300.0,
|
|
||||||
N_INF,
|
|
||||||
);
|
|
||||||
let t_c = Player::new(
|
|
||||||
Gaussian::new(25.0, 25.0 / 3.0),
|
|
||||||
25.0 / 6.0,
|
|
||||||
25.0 / 300.0,
|
|
||||||
N_INF,
|
|
||||||
);
|
|
||||||
|
|
||||||
let g = Game::new(vec![vec![t_a], vec![t_b], vec![t_c]], vec![0, 0, 0], 0.25);
|
let g = Game::new(
|
||||||
|
vec![vec![t_a], vec![t_b], vec![t_c]],
|
||||||
|
vec![0.0, 0.0, 0.0],
|
||||||
|
vec![],
|
||||||
|
0.25,
|
||||||
|
);
|
||||||
let p = g.posteriors();
|
let p = g.posteriors();
|
||||||
|
|
||||||
let a = p[0][0];
|
let a = p[0][0];
|
||||||
let b = p[1][0];
|
let b = p[1][0];
|
||||||
let c = p[2][0];
|
let c = p[2][0];
|
||||||
|
|
||||||
assert_eq!(a.mu(), 24.999999999999996);
|
assert_eq!(a.mu, 24.999999999999996);
|
||||||
assert_eq!(a.sigma(), 5.729068664890827);
|
assert_eq!(a.sigma, 5.729068664890827);
|
||||||
assert_eq!(b.mu(), 25.000000000000004);
|
assert_eq!(b.mu, 25.000000000000004);
|
||||||
assert_eq!(b.sigma(), 5.707423522433266);
|
assert_eq!(b.sigma, 5.707423522433266);
|
||||||
assert_eq!(c.mu(), 24.999999999999996);
|
assert_eq!(c.mu, 24.999999999999996);
|
||||||
assert_eq!(c.sigma(), 5.729068664890825);
|
assert_eq!(c.sigma, 5.729068664890825);
|
||||||
|
|
||||||
let t_a = Player::new(Gaussian::new(25.0, 3.0), 25.0 / 6.0, 25.0 / 300.0, N_INF);
|
let t_a = Player::new(Gaussian::new(25.0, 3.0), 25.0 / 6.0, 25.0 / 300.0);
|
||||||
let t_b = Player::new(Gaussian::new(25.0, 3.0), 25.0 / 6.0, 25.0 / 300.0, N_INF);
|
let t_b = Player::new(Gaussian::new(25.0, 3.0), 25.0 / 6.0, 25.0 / 300.0);
|
||||||
let t_c = Player::new(Gaussian::new(29.0, 2.0), 25.0 / 6.0, 25.0 / 300.0, N_INF);
|
let t_c = Player::new(Gaussian::new(29.0, 2.0), 25.0 / 6.0, 25.0 / 300.0);
|
||||||
|
|
||||||
let g = Game::new(vec![vec![t_a], vec![t_b], vec![t_c]], vec![0, 0, 0], 0.25);
|
let g = Game::new(
|
||||||
|
vec![vec![t_a], vec![t_b], vec![t_c]],
|
||||||
|
vec![0.0, 0.0, 0.0],
|
||||||
|
vec![],
|
||||||
|
0.25,
|
||||||
|
);
|
||||||
let p = g.posteriors();
|
let p = g.posteriors();
|
||||||
|
|
||||||
let a = p[0][0];
|
let a = p[0][0];
|
||||||
let b = p[1][0];
|
let b = p[1][0];
|
||||||
let c = p[2][0];
|
let c = p[2][0];
|
||||||
|
|
||||||
assert_eq!(a.mu(), 25.48850755025261);
|
assert_eq!(a.mu, 25.48850755025261);
|
||||||
assert_eq!(a.sigma(), 2.638208444298423);
|
assert_eq!(a.sigma, 2.6382084442984226);
|
||||||
assert_eq!(b.mu(), 25.51067170990121);
|
assert_eq!(b.mu, 25.510671709901217);
|
||||||
assert_eq!(b.sigma(), 2.6287517663583633);
|
assert_eq!(b.sigma, 2.6287517663583633);
|
||||||
assert_eq!(c.mu(), 28.555920328820523);
|
assert_eq!(c.mu, 28.555920328820527);
|
||||||
assert_eq!(c.sigma(), 1.8856891308577184);
|
assert_eq!(c.sigma, 1.8856891308577184);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
119
src/gaussian.rs
119
src/gaussian.rs
@@ -1,62 +1,58 @@
|
|||||||
use std::ops;
|
use std::ops;
|
||||||
|
|
||||||
use crate::{utils, MU, SIGMA};
|
use crate::{MU, N_INF, SIGMA};
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||||
pub struct Gaussian {
|
pub struct Gaussian {
|
||||||
mu: f64,
|
pub(crate) mu: f64,
|
||||||
sigma: f64,
|
pub(crate) sigma: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Gaussian {
|
impl Gaussian {
|
||||||
#[inline]
|
pub fn new(mu: f64, sigma: f64) -> Self {
|
||||||
pub const fn new(mu: f64, sigma: f64) -> Self {
|
debug_assert!(sigma >= 0.0, "sigma must be equal or larger than 0.0");
|
||||||
|
|
||||||
Gaussian { mu, sigma }
|
Gaussian { mu, sigma }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
fn pi(&self) -> f64 {
|
||||||
pub fn mu(&self) -> f64 {
|
if self.sigma > 0.0 {
|
||||||
self.mu
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn sigma(&self) -> f64 {
|
|
||||||
self.sigma
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn tau(&self) -> f64 {
|
|
||||||
self.mu * self.pi()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn pi(&self) -> f64 {
|
|
||||||
self.sigma.powi(-2)
|
self.sigma.powi(-2)
|
||||||
|
} else {
|
||||||
|
f64::INFINITY
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
fn tau(&self) -> f64 {
|
||||||
pub fn forget(&self, gamma: f64, t: f64) -> Self {
|
if self.sigma > 0.0 {
|
||||||
Self::new(self.mu, (self.sigma().powi(2) + t * gamma.powi(2)).sqrt())
|
self.mu * self.pi()
|
||||||
|
} else {
|
||||||
|
f64::INFINITY
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
pub(crate) fn delta(&self, m: Gaussian) -> (f64, f64) {
|
||||||
pub fn delta(&self, m: Gaussian) -> (f64, f64) {
|
((self.mu - m.mu).abs(), (self.sigma - m.sigma).abs())
|
||||||
((self.mu() - m.mu()).abs(), (self.sigma() - m.sigma()).abs())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
pub(crate) fn exclude(&self, m: Gaussian) -> Self {
|
||||||
pub fn exclude(&self, m: Gaussian) -> Self {
|
Self {
|
||||||
Self::new(
|
mu: self.mu - m.mu,
|
||||||
self.mu() - m.mu(),
|
sigma: (self.sigma.powi(2) - m.sigma.powi(2)).sqrt(),
|
||||||
(self.sigma().powi(2) - m.sigma().powi(2)).sqrt(),
|
}
|
||||||
)
|
}
|
||||||
|
|
||||||
|
pub(crate) fn forget(&self, gamma: f64, t: u64) -> Self {
|
||||||
|
Self {
|
||||||
|
mu: self.mu,
|
||||||
|
sigma: (self.sigma.powi(2) + t as f64 * gamma.powi(2)).sqrt(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Gaussian {
|
impl Default for Gaussian {
|
||||||
#[inline]
|
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Gaussian {
|
Self {
|
||||||
mu: MU,
|
mu: MU,
|
||||||
sigma: SIGMA,
|
sigma: SIGMA,
|
||||||
}
|
}
|
||||||
@@ -66,7 +62,6 @@ impl Default for Gaussian {
|
|||||||
impl ops::Add<Gaussian> for Gaussian {
|
impl ops::Add<Gaussian> for Gaussian {
|
||||||
type Output = Gaussian;
|
type Output = Gaussian;
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn add(self, rhs: Gaussian) -> Self::Output {
|
fn add(self, rhs: Gaussian) -> Self::Output {
|
||||||
Gaussian {
|
Gaussian {
|
||||||
mu: self.mu + rhs.mu,
|
mu: self.mu + rhs.mu,
|
||||||
@@ -78,7 +73,6 @@ impl ops::Add<Gaussian> for Gaussian {
|
|||||||
impl ops::Sub<Gaussian> for Gaussian {
|
impl ops::Sub<Gaussian> for Gaussian {
|
||||||
type Output = Gaussian;
|
type Output = Gaussian;
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn sub(self, rhs: Gaussian) -> Self::Output {
|
fn sub(self, rhs: Gaussian) -> Self::Output {
|
||||||
Gaussian {
|
Gaussian {
|
||||||
mu: self.mu - rhs.mu,
|
mu: self.mu - rhs.mu,
|
||||||
@@ -90,25 +84,66 @@ impl ops::Sub<Gaussian> for Gaussian {
|
|||||||
impl ops::Mul<Gaussian> for Gaussian {
|
impl ops::Mul<Gaussian> for Gaussian {
|
||||||
type Output = Gaussian;
|
type Output = Gaussian;
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn mul(self, rhs: Gaussian) -> Self::Output {
|
fn mul(self, rhs: Gaussian) -> Self::Output {
|
||||||
let (mu, sigma) = utils::mu_sigma(self.tau() + rhs.tau(), self.pi() + rhs.pi());
|
let (mu, sigma) = if self.sigma == 0.0 || rhs.sigma == 0.0 {
|
||||||
|
let mu = self.mu / (self.sigma.powi(2) / rhs.sigma.powi(2) + 1.0)
|
||||||
|
+ rhs.mu / (rhs.sigma.powi(2) / self.sigma.powi(2) + 1.0);
|
||||||
|
|
||||||
|
let sigma = (1.0 / ((1.0 / self.sigma.powi(2)) + (1.0 / rhs.sigma.powi(2)))).sqrt();
|
||||||
|
|
||||||
|
(mu, sigma)
|
||||||
|
} else {
|
||||||
|
mu_sigma(self.tau() + rhs.tau(), self.pi() + rhs.pi())
|
||||||
|
};
|
||||||
|
|
||||||
Gaussian { mu, sigma }
|
Gaussian { mu, sigma }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ops::Mul<f64> for Gaussian {
|
||||||
|
type Output = Gaussian;
|
||||||
|
|
||||||
|
fn mul(self, rhs: f64) -> Self::Output {
|
||||||
|
if rhs.is_finite() {
|
||||||
|
Self {
|
||||||
|
mu: self.mu * rhs,
|
||||||
|
sigma: self.sigma * rhs,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
N_INF
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ops::Div<Gaussian> for Gaussian {
|
impl ops::Div<Gaussian> for Gaussian {
|
||||||
type Output = Gaussian;
|
type Output = Gaussian;
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn div(self, rhs: Gaussian) -> Self::Output {
|
fn div(self, rhs: Gaussian) -> Self::Output {
|
||||||
let (mu, sigma) = utils::mu_sigma(self.tau() - rhs.tau(), self.pi() - rhs.pi());
|
let (mu, sigma) = if self.sigma == 0.0 || rhs.sigma == 0.0 {
|
||||||
|
let mu = self.mu / (1.0 - self.sigma.powi(2) / rhs.sigma.powi(2))
|
||||||
|
+ rhs.mu / (rhs.sigma.powi(2) / self.sigma.powi(2) - 1.0);
|
||||||
|
|
||||||
|
let sigma = (1.0 / ((1.0 / self.sigma.powi(2)) - (1.0 / rhs.sigma.powi(2)))).sqrt();
|
||||||
|
|
||||||
|
(mu, sigma)
|
||||||
|
} else {
|
||||||
|
mu_sigma(self.tau() - rhs.tau(), self.pi() - rhs.pi())
|
||||||
|
};
|
||||||
|
|
||||||
Gaussian { mu, sigma }
|
Gaussian { mu, sigma }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn mu_sigma(tau: f64, pi: f64) -> (f64, f64) {
|
||||||
|
if pi > 0.0 {
|
||||||
|
(tau / pi, (1.0 / pi).sqrt())
|
||||||
|
} else if (pi + 1e-5) < 0.0 {
|
||||||
|
panic!("precision should be greater than 0");
|
||||||
|
} else {
|
||||||
|
(0.0, f64::INFINITY)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|||||||
439
src/history.rs
439
src/history.rs
@@ -1,54 +1,76 @@
|
|||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
use crate::{utils, Agent, Batch, Gaussian, Player, PlayerIndex, N_INF};
|
use crate::{
|
||||||
|
agent::{self, Agent},
|
||||||
|
batch::Batch,
|
||||||
|
gaussian::Gaussian,
|
||||||
|
player::Player,
|
||||||
|
sort_time, tuple_gt, tuple_max,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct History {
|
pub struct History {
|
||||||
size: usize,
|
size: usize,
|
||||||
batches: Vec<Batch>,
|
batches: Vec<Batch>,
|
||||||
agents: HashMap<PlayerIndex, Agent>,
|
agents: HashMap<String, Agent>,
|
||||||
// mu: f64,
|
|
||||||
// sigma: f64,
|
|
||||||
// beta: f64,
|
|
||||||
// gamma: f64,
|
|
||||||
p_draw: f64,
|
|
||||||
time: bool,
|
time: bool,
|
||||||
pub epsilon: f64,
|
|
||||||
pub iterations: usize,
|
|
||||||
pub verbose: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl History {
|
|
||||||
pub fn new(
|
|
||||||
composition: &[Vec<Vec<PlayerIndex>>],
|
|
||||||
results: &[Vec<u16>],
|
|
||||||
times: &[f64],
|
|
||||||
priors: HashMap<PlayerIndex, Player>,
|
|
||||||
mu: f64,
|
mu: f64,
|
||||||
sigma: f64,
|
sigma: f64,
|
||||||
beta: f64,
|
beta: f64,
|
||||||
gamma: f64,
|
gamma: f64,
|
||||||
p_draw: f64,
|
p_draw: f64,
|
||||||
|
online: bool,
|
||||||
|
weights: Vec<Vec<Vec<f64>>>,
|
||||||
|
epsilon: f64,
|
||||||
|
iterations: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl History {
|
||||||
|
pub fn new(
|
||||||
|
composition: Vec<Vec<Vec<&str>>>,
|
||||||
|
results: Vec<Vec<f64>>,
|
||||||
|
times: Vec<u64>,
|
||||||
|
weights: Vec<Vec<Vec<f64>>>,
|
||||||
|
priors: HashMap<String, Player>,
|
||||||
|
mu: f64,
|
||||||
|
sigma: f64,
|
||||||
|
beta: f64,
|
||||||
|
gamma: f64,
|
||||||
|
p_draw: f64,
|
||||||
|
online: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
assert!(
|
||||||
|
results.is_empty() || results.len() == composition.len(),
|
||||||
|
"TODO: Add a comment here"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
times.is_empty() || times.len() == composition.len(),
|
||||||
|
"TODO: Add a comment here"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
weights.is_empty() || weights.len() == composition.len(),
|
||||||
|
"TODO: Add a comment here"
|
||||||
|
);
|
||||||
|
|
||||||
let this_agent = composition
|
let this_agent = composition
|
||||||
.iter()
|
.iter()
|
||||||
.flatten()
|
.flatten()
|
||||||
.flatten()
|
.flatten()
|
||||||
|
.cloned()
|
||||||
.collect::<HashSet<_>>();
|
.collect::<HashSet<_>>();
|
||||||
|
|
||||||
let agents = this_agent
|
let agents = this_agent
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|a| {
|
.map(|agent| {
|
||||||
let player = priors
|
let player = priors
|
||||||
.get(a)
|
.get(agent)
|
||||||
.cloned()
|
.cloned()
|
||||||
.unwrap_or_else(|| Player::new(Gaussian::new(mu, sigma), beta, gamma, N_INF));
|
.unwrap_or_else(|| Player::new(Gaussian::new(mu, sigma), beta, gamma));
|
||||||
|
|
||||||
(
|
(
|
||||||
*a,
|
agent.to_string(),
|
||||||
Agent {
|
Agent {
|
||||||
player,
|
player,
|
||||||
message: N_INF,
|
..Default::default()
|
||||||
last_time: f64::NEG_INFINITY,
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@@ -58,41 +80,45 @@ impl History {
|
|||||||
size: composition.len(),
|
size: composition.len(),
|
||||||
batches: Vec::new(),
|
batches: Vec::new(),
|
||||||
agents,
|
agents,
|
||||||
p_draw,
|
|
||||||
time: !times.is_empty(),
|
time: !times.is_empty(),
|
||||||
epsilon: 1e-6,
|
mu,
|
||||||
iterations: 30,
|
sigma,
|
||||||
verbose: true,
|
beta,
|
||||||
|
gamma,
|
||||||
|
p_draw,
|
||||||
|
online,
|
||||||
|
weights: weights.clone(),
|
||||||
|
epsilon: 0.0,
|
||||||
|
iterations: 10,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.trueskill(composition, results, times);
|
this.trueskill(composition, results, times, weights, online);
|
||||||
|
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
fn trueskill(
|
fn trueskill(
|
||||||
&mut self,
|
&mut self,
|
||||||
composition: &[Vec<Vec<PlayerIndex>>],
|
composition: Vec<Vec<Vec<&str>>>,
|
||||||
results: &[Vec<u16>],
|
results: Vec<Vec<f64>>,
|
||||||
times: &[f64],
|
times: Vec<u64>,
|
||||||
|
weights: Vec<Vec<Vec<f64>>>,
|
||||||
|
online: bool,
|
||||||
) {
|
) {
|
||||||
let o = if self.time {
|
let o = if self.time {
|
||||||
utils::sort_time(times)
|
sort_time(×, false)
|
||||||
} else {
|
} else {
|
||||||
(0..composition.len()).collect::<Vec<_>>()
|
(0..composition.len()).collect::<Vec<_>>()
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
|
let mut last = 0.0;
|
||||||
|
|
||||||
while i < self.size {
|
while i < self.size {
|
||||||
let mut j = i + 1;
|
let mut j = i + 1;
|
||||||
let time = if self.time {
|
let t = if self.time { times[o[i]] } else { i as u64 + 1 };
|
||||||
times[o[i]]
|
|
||||||
} else {
|
|
||||||
i as f64 + 1.0
|
|
||||||
};
|
|
||||||
|
|
||||||
while self.time && j < self.size && times[o[j]] == time {
|
while self.time && j < self.size && times[o[j]] == t {
|
||||||
j += 1;
|
j += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,30 +126,67 @@ impl History {
|
|||||||
.map(|e| composition[o[e]].clone())
|
.map(|e| composition[o[e]].clone())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let results = (i..j).map(|e| results[o[e]].clone()).collect::<Vec<_>>();
|
let results = if results.is_empty() {
|
||||||
|
Vec::new()
|
||||||
|
} else {
|
||||||
|
(i..j).map(|e| results[o[e]].clone()).collect::<Vec<_>>()
|
||||||
|
};
|
||||||
|
|
||||||
let b = Batch::new(composition, results, time, &mut self.agents, self.p_draw);
|
let weights = if weights.is_empty() {
|
||||||
|
Vec::new()
|
||||||
|
} else {
|
||||||
|
(i..j).map(|e| weights[o[e]].clone()).collect::<Vec<_>>()
|
||||||
|
};
|
||||||
|
|
||||||
|
let b = Batch::new(
|
||||||
|
composition,
|
||||||
|
results,
|
||||||
|
weights,
|
||||||
|
t,
|
||||||
|
self.p_draw,
|
||||||
|
&mut self.agents,
|
||||||
|
);
|
||||||
|
|
||||||
self.batches.push(b);
|
self.batches.push(b);
|
||||||
|
|
||||||
let idx = self.batches.len() - 1;
|
let idx = self.batches.len() - 1;
|
||||||
|
|
||||||
|
if online {
|
||||||
|
let new = 100.0 * (i as f64 / self.size as f64);
|
||||||
|
|
||||||
|
if new != last {
|
||||||
|
println!("{:.02}%", new);
|
||||||
|
last = new;
|
||||||
|
}
|
||||||
|
|
||||||
|
for skill in self.batches[idx].skills.values_mut() {
|
||||||
|
skill.online = skill.forward;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.convergence(self.iterations, self.epsilon, false);
|
||||||
|
}
|
||||||
|
|
||||||
let b = &mut self.batches[idx];
|
let b = &mut self.batches[idx];
|
||||||
|
|
||||||
for a in b.skills.keys() {
|
for a in b.skills.keys() {
|
||||||
let agent = self.agents.get_mut(a).unwrap();
|
let agent = self.agents.get_mut(a).unwrap();
|
||||||
|
|
||||||
agent.last_time = if self.time { time } else { f64::INFINITY };
|
agent.last_time = if self.time { t } else { u64::MAX };
|
||||||
agent.message = b.forward_prior_out(a);
|
agent.message = b.forward_prior_out(a);
|
||||||
}
|
}
|
||||||
|
|
||||||
i = j;
|
i = j;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if online {
|
||||||
|
println!("100.00%");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn iteration(&mut self) -> (f64, f64) {
|
fn iteration(&mut self) -> (f64, f64) {
|
||||||
let mut step = (0.0, 0.0);
|
let mut step = (0.0, 0.0);
|
||||||
|
|
||||||
clean(self.agents.values_mut(), false);
|
agent::clean(self.agents.values_mut(), false);
|
||||||
|
|
||||||
for j in (0..self.batches.len() - 1).rev() {
|
for j in (0..self.batches.len() - 1).rev() {
|
||||||
for agent in self.batches[j + 1].skills.keys() {
|
for agent in self.batches[j + 1].skills.keys() {
|
||||||
@@ -142,7 +205,7 @@ impl History {
|
|||||||
.fold(step, |step, (a, old)| tuple_max(step, old.delta(new[a])));
|
.fold(step, |step, (a, old)| tuple_max(step, old.delta(new[a])));
|
||||||
}
|
}
|
||||||
|
|
||||||
clean(self.agents.values_mut(), false);
|
agent::clean(self.agents.values_mut(), false);
|
||||||
|
|
||||||
for j in 1..self.batches.len() {
|
for j in 1..self.batches.len() {
|
||||||
for agent in self.batches[j - 1].skills.keys() {
|
for agent in self.batches[j - 1].skills.keys() {
|
||||||
@@ -164,7 +227,7 @@ impl History {
|
|||||||
if self.batches.len() == 1 {
|
if self.batches.len() == 1 {
|
||||||
let old = self.batches[0].posteriors();
|
let old = self.batches[0].posteriors();
|
||||||
|
|
||||||
self.batches[0].convergence(&mut self.agents);
|
self.batches[0].iteration(0, &mut self.agents);
|
||||||
|
|
||||||
let new = self.batches[0].posteriors();
|
let new = self.batches[0].posteriors();
|
||||||
|
|
||||||
@@ -176,12 +239,17 @@ impl History {
|
|||||||
step
|
step
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn convergence(&mut self) -> ((f64, f64), usize) {
|
pub fn convergence(
|
||||||
|
&mut self,
|
||||||
|
iterations: usize,
|
||||||
|
epsilon: f64,
|
||||||
|
verbose: bool,
|
||||||
|
) -> ((f64, f64), usize) {
|
||||||
let mut step = (f64::INFINITY, f64::INFINITY);
|
let mut step = (f64::INFINITY, f64::INFINITY);
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
|
|
||||||
while (step.0 > self.epsilon || step.1 > self.epsilon) && i < self.iterations {
|
while tuple_gt(step, epsilon) && i < iterations {
|
||||||
if self.verbose {
|
if verbose {
|
||||||
print!("Iteration = {}", i);
|
print!("Iteration = {}", i);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,20 +257,20 @@ impl History {
|
|||||||
|
|
||||||
i += 1;
|
i += 1;
|
||||||
|
|
||||||
if self.verbose {
|
if verbose {
|
||||||
println!(", step = {:?}", step);
|
println!(", step = {:?}", step);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.verbose {
|
if verbose {
|
||||||
println!("End");
|
println!("End");
|
||||||
}
|
}
|
||||||
|
|
||||||
(step, i)
|
(step, i)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn learning_curves(&self) -> HashMap<PlayerIndex, Vec<(f64, Gaussian)>> {
|
pub fn learning_curves(&self) -> HashMap<String, Vec<(u64, Gaussian)>> {
|
||||||
let mut data: HashMap<PlayerIndex, Vec<(f64, Gaussian)>> = HashMap::new();
|
let mut data: HashMap<String, Vec<(u64, 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() {
|
||||||
@@ -211,266 +279,234 @@ 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, vec![point]);
|
data.insert(agent.to_string(), vec![point]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data
|
data
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn log_evidence(&self) -> f64 {
|
|
||||||
self.batches
|
|
||||||
.iter()
|
|
||||||
.flat_map(|batch| batch.events.iter())
|
|
||||||
.map(|event| event.evidence.ln())
|
|
||||||
.sum()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn clean<'a, A: Iterator<Item = &'a mut Agent>>(agents: A, last_time: bool) {
|
|
||||||
for a in agents {
|
|
||||||
a.message = N_INF;
|
|
||||||
|
|
||||||
if last_time {
|
|
||||||
a.last_time = f64::NEG_INFINITY;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn tuple_max(a: (f64, f64), b: (f64, f64)) -> (f64, f64) {
|
|
||||||
(
|
|
||||||
if a.0 > b.0 { a.0 } else { b.0 },
|
|
||||||
if a.1 > b.1 { a.1 } else { b.1 },
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use approx::assert_ulps_eq;
|
use approx::assert_ulps_eq;
|
||||||
|
|
||||||
use crate::{Game, BETA, GAMMA, MU, P_DRAW, SIGMA};
|
use crate::{Game, Player, BETA, EPSILON, GAMMA, ITERATIONS, MU, P_DRAW, SIGMA};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[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, 0.0], vec![0.0, 1.0], vec![1.0, 0.0]];
|
||||||
|
|
||||||
let mut priors = HashMap::new();
|
let mut priors = HashMap::new();
|
||||||
|
|
||||||
for k in [a, b, c] {
|
for agent in ["a", "b", "c"] {
|
||||||
let player = Player::new(
|
priors.insert(
|
||||||
|
agent.to_string(),
|
||||||
|
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,
|
||||||
0.15 * 25.0 / 3.0,
|
0.15 * 25.0 / 3.0,
|
||||||
N_INF,
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
priors.insert(k, player);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut h = History::new(
|
let mut h = History::new(
|
||||||
&composition,
|
composition,
|
||||||
&results,
|
results,
|
||||||
&[1.0, 2.0, 3.0],
|
vec![1, 2, 3],
|
||||||
|
vec![],
|
||||||
priors,
|
priors,
|
||||||
MU,
|
MU,
|
||||||
BETA,
|
|
||||||
SIGMA,
|
SIGMA,
|
||||||
|
BETA,
|
||||||
GAMMA,
|
GAMMA,
|
||||||
P_DRAW,
|
P_DRAW,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
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, false, false, &mut h.agents),
|
||||||
vec![0, 1],
|
vec![0.0, 1.0],
|
||||||
|
vec![],
|
||||||
P_DRAW,
|
P_DRAW,
|
||||||
)
|
)
|
||||||
.posteriors();
|
.posteriors();
|
||||||
let expected = p[0][0];
|
let expected = p[0][0];
|
||||||
|
|
||||||
assert_ulps_eq!(observed.mu(), expected.mu(), epsilon = 0.000001);
|
assert_ulps_eq!(observed.mu, expected.mu, epsilon = 0.000001);
|
||||||
assert_ulps_eq!(observed.sigma(), expected.sigma(), epsilon = 0.000001);
|
assert_ulps_eq!(observed.sigma, expected.sigma, epsilon = 0.000001);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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![a], vec![b]],
|
vec![vec!["a"], vec!["b"]],
|
||||||
vec![vec![b], vec![c]],
|
vec![vec!["b"], vec!["c"]],
|
||||||
vec![vec![c], vec![a]],
|
vec![vec!["c"], vec!["a"]],
|
||||||
];
|
];
|
||||||
let results = vec![vec![1, 0], vec![1, 0], vec![1, 0]];
|
let results = vec![vec![1.0, 0.0], vec![1.0, 0.0], vec![1.0, 0.0]];
|
||||||
let times = vec![1.0, 1.0, 1.0];
|
let times = vec![1, 1, 1];
|
||||||
|
|
||||||
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,
|
||||||
0.15 * 25.0 / 3.0,
|
0.15 * 25.0 / 3.0,
|
||||||
N_INF,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
priors.insert(k, player);
|
priors.insert(k.to_string(), player);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut h1 = History::new(
|
let mut h1 = History::new(
|
||||||
&composition,
|
composition,
|
||||||
&results,
|
results,
|
||||||
×,
|
times,
|
||||||
|
vec![],
|
||||||
priors,
|
priors,
|
||||||
MU,
|
MU,
|
||||||
BETA,
|
|
||||||
SIGMA,
|
SIGMA,
|
||||||
|
BETA,
|
||||||
GAMMA,
|
GAMMA,
|
||||||
P_DRAW,
|
P_DRAW,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_ulps_eq!(
|
assert_ulps_eq!(
|
||||||
h1.batches[0].posterior(&a).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(&a).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(&c).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(&c).sigma(),
|
h1.batches[0].posterior("c").sigma,
|
||||||
5.866311348102563,
|
5.866311348102563,
|
||||||
epsilon = 0.000001
|
epsilon = 0.000001
|
||||||
);
|
);
|
||||||
|
|
||||||
let (_step, _i) = h1.convergence();
|
let (_step, _i) = h1.convergence(ITERATIONS, EPSILON, false);
|
||||||
|
|
||||||
assert_ulps_eq!(
|
assert_ulps_eq!(
|
||||||
h1.batches[0].posterior(&a).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(&a).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(&c).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(&c).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![a], vec![b]],
|
vec![vec!["a"], vec!["b"]],
|
||||||
vec![vec![b], vec![c]],
|
vec![vec!["b"], vec!["c"]],
|
||||||
vec![vec![c], vec![a]],
|
vec![vec!["c"], vec!["a"]],
|
||||||
];
|
];
|
||||||
let results = vec![vec![1, 0], vec![1, 0], vec![1, 0]];
|
let results = vec![vec![1.0, 0.0], vec![1.0, 0.0], vec![1.0, 0.0]];
|
||||||
let times = vec![1.0, 2.0, 3.0];
|
let times = vec![1, 2, 3];
|
||||||
|
|
||||||
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), 25.0 / 6.0, 25.0 / 300.0);
|
||||||
Gaussian::new(25.0, 25.0 / 3.0),
|
|
||||||
25.0 / 6.0,
|
|
||||||
25.0 / 300.0,
|
|
||||||
N_INF,
|
|
||||||
);
|
|
||||||
|
|
||||||
priors.insert(k, player);
|
priors.insert(k.to_string(), player);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut h2 = History::new(
|
let mut h2 = History::new(
|
||||||
&composition,
|
composition,
|
||||||
&results,
|
results,
|
||||||
×,
|
times,
|
||||||
|
vec![],
|
||||||
priors,
|
priors,
|
||||||
MU,
|
MU,
|
||||||
BETA,
|
|
||||||
SIGMA,
|
SIGMA,
|
||||||
|
BETA,
|
||||||
GAMMA,
|
GAMMA,
|
||||||
P_DRAW,
|
P_DRAW,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_ulps_eq!(
|
assert_ulps_eq!(
|
||||||
h2.batches[2].posterior(&a).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(&a).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(&c).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(&c).sigma(),
|
h2.batches[2].posterior("c").sigma,
|
||||||
5.866811597660157,
|
5.866811597660157,
|
||||||
epsilon = 0.000001
|
epsilon = 0.000001
|
||||||
);
|
);
|
||||||
|
|
||||||
let (_step, _i) = h2.convergence();
|
let (_step, _i) = h2.convergence(ITERATIONS, EPSILON, false);
|
||||||
|
|
||||||
assert_ulps_eq!(
|
assert_ulps_eq!(
|
||||||
h2.batches[2].posterior(&a).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(&a).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(&c).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(&c).sigma(),
|
h2.batches[2].posterior("c").sigma,
|
||||||
5.419827012784138,
|
5.419827012784138,
|
||||||
epsilon = 0.000001
|
epsilon = 0.000001
|
||||||
);
|
);
|
||||||
@@ -478,70 +514,63 @@ 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![a], vec![b]],
|
vec![vec!["a"], vec!["b"]],
|
||||||
vec![vec![b], vec![c]],
|
vec![vec!["b"], vec!["c"]],
|
||||||
vec![vec![c], vec![a]],
|
vec![vec!["c"], vec!["a"]],
|
||||||
];
|
];
|
||||||
let results = vec![vec![1, 0], vec![1, 0], vec![1, 0]];
|
let results = vec![vec![1.0, 0.0], vec![1.0, 0.0], vec![1.0, 0.0]];
|
||||||
let times = vec![5.0, 6.0, 7.0];
|
let times = vec![5, 6, 7];
|
||||||
|
|
||||||
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), 25.0 / 6.0, 25.0 / 300.0);
|
||||||
Gaussian::new(25.0, 25.0 / 3.0),
|
|
||||||
25.0 / 6.0,
|
|
||||||
25.0 / 300.0,
|
|
||||||
N_INF,
|
|
||||||
);
|
|
||||||
|
|
||||||
priors.insert(k, player);
|
priors.insert(k.to_string(), player);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut h = History::new(
|
let mut h = History::new(
|
||||||
&composition,
|
composition,
|
||||||
&results,
|
results,
|
||||||
×,
|
times,
|
||||||
|
vec![],
|
||||||
priors,
|
priors,
|
||||||
MU,
|
MU,
|
||||||
BETA,
|
|
||||||
SIGMA,
|
SIGMA,
|
||||||
|
BETA,
|
||||||
GAMMA,
|
GAMMA,
|
||||||
P_DRAW,
|
P_DRAW,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
h.convergence();
|
h.convergence(ITERATIONS, EPSILON, false);
|
||||||
|
|
||||||
let lc = h.learning_curves();
|
let lc = h.learning_curves();
|
||||||
|
|
||||||
let aj_e = lc[&a].len();
|
let aj_e = lc["a"].len();
|
||||||
let cj_e = lc[&c].len();
|
let cj_e = lc["c"].len();
|
||||||
|
|
||||||
assert_eq!(lc[&a][0].0, 5.0);
|
assert_eq!(lc["a"][0].0, 5);
|
||||||
assert_eq!(lc[&a][aj_e - 1].0, 7.0);
|
assert_eq!(lc["a"][aj_e - 1].0, 7);
|
||||||
|
|
||||||
assert_ulps_eq!(
|
assert_ulps_eq!(
|
||||||
lc[&a][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[&a][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[&c][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[&c][cj_e - 1].1.sigma(),
|
lc["c"][cj_e - 1].1.sigma,
|
||||||
5.419827012784138,
|
5.419827012784138,
|
||||||
epsilon = 0.000001
|
epsilon = 0.000001
|
||||||
);
|
);
|
||||||
@@ -549,61 +578,59 @@ 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, 0.0], vec![0.0, 1.0], vec![1.0, 0.0]];
|
||||||
|
|
||||||
let mut h = History::new(
|
let mut h = History::new(
|
||||||
&composition,
|
composition,
|
||||||
&results,
|
results,
|
||||||
&[],
|
vec![],
|
||||||
|
vec![],
|
||||||
HashMap::new(),
|
HashMap::new(),
|
||||||
25.0,
|
25.0,
|
||||||
25.0 / 3.0,
|
25.0 / 3.0,
|
||||||
25.0 / 6.0,
|
25.0 / 6.0,
|
||||||
25.0 / 300.0,
|
25.0 / 300.0,
|
||||||
0.0,
|
0.0,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
let (_step, _i) = h.convergence();
|
let (_step, _i) = h.convergence(ITERATIONS, EPSILON, false);
|
||||||
|
|
||||||
assert_eq!(h.batches[2].skills[&b].elapsed, 1.0);
|
assert_eq!(h.batches[2].skills["b"].elapsed, 1);
|
||||||
assert_eq!(h.batches[2].skills[&c].elapsed, 1.0);
|
assert_eq!(h.batches[2].skills["c"].elapsed, 1);
|
||||||
|
|
||||||
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
|
||||||
);
|
);
|
||||||
|
|||||||
214
src/lib.rs
214
src/lib.rs
@@ -1,25 +1,211 @@
|
|||||||
|
use std::cmp::Reverse;
|
||||||
|
use std::f64::consts::{FRAC_1_SQRT_2, FRAC_2_SQRT_PI, SQRT_2};
|
||||||
|
|
||||||
|
mod agent;
|
||||||
mod batch;
|
mod batch;
|
||||||
mod game;
|
mod game;
|
||||||
mod gaussian;
|
mod gaussian;
|
||||||
mod history;
|
mod history;
|
||||||
mod message;
|
mod message;
|
||||||
mod player;
|
mod player;
|
||||||
mod utils;
|
|
||||||
mod variable;
|
|
||||||
|
|
||||||
pub use batch::*;
|
use gaussian::Gaussian;
|
||||||
pub use game::*;
|
use message::DiffMessage;
|
||||||
pub use gaussian::*;
|
|
||||||
pub use history::*;
|
|
||||||
pub use player::*;
|
|
||||||
|
|
||||||
pub const BETA: f64 = 1.0;
|
pub use game::Game;
|
||||||
|
pub use history::History;
|
||||||
|
pub use player::Player;
|
||||||
|
|
||||||
|
const BETA: f64 = 1.0;
|
||||||
pub const MU: f64 = 0.0;
|
pub const MU: f64 = 0.0;
|
||||||
pub const SIGMA: f64 = BETA * 6.0;
|
pub const SIGMA: f64 = BETA * 6.0;
|
||||||
pub const GAMMA: f64 = BETA * 0.03;
|
const GAMMA: f64 = BETA * 0.03;
|
||||||
pub const P_DRAW: f64 = 0.0;
|
const P_DRAW: f64 = 0.0;
|
||||||
|
pub const EPSILON: f64 = 1e-6;
|
||||||
|
pub const ITERATIONS: usize = 30;
|
||||||
|
|
||||||
pub const N01: Gaussian = Gaussian::new(0.0, 1.0);
|
const SQRT_TAU: f64 = 2.5066282746310002;
|
||||||
pub const N00: Gaussian = Gaussian::new(0.0, 0.0);
|
|
||||||
pub const N_INF: Gaussian = Gaussian::new(0.0, f64::INFINITY);
|
const N01: Gaussian = Gaussian {
|
||||||
pub const N_MS: Gaussian = Gaussian::new(MU, SIGMA);
|
mu: 0.0,
|
||||||
|
sigma: 1.0,
|
||||||
|
};
|
||||||
|
pub(crate) const N00: Gaussian = Gaussian {
|
||||||
|
mu: 0.0,
|
||||||
|
sigma: 0.0,
|
||||||
|
};
|
||||||
|
pub(crate) const N_INF: Gaussian = Gaussian {
|
||||||
|
mu: 0.0,
|
||||||
|
sigma: f64::INFINITY,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn erfc(x: f64) -> f64 {
|
||||||
|
let z = x.abs();
|
||||||
|
let t = 1.0 / (1.0 + z / 2.0);
|
||||||
|
|
||||||
|
let a = -0.82215223 + t * 0.17087277;
|
||||||
|
let b = 1.48851587 + t * a;
|
||||||
|
let c = -1.13520398 + t * b;
|
||||||
|
let d = 0.27886807 + t * c;
|
||||||
|
let e = -0.18628806 + t * d;
|
||||||
|
let f = 0.09678418 + t * e;
|
||||||
|
let g = 0.37409196 + t * f;
|
||||||
|
let h = 1.00002368 + t * g;
|
||||||
|
|
||||||
|
let r = t * (-z * z - 1.26551223 + t * h).exp();
|
||||||
|
|
||||||
|
if x >= 0.0 {
|
||||||
|
r
|
||||||
|
} else {
|
||||||
|
2.0 - r
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn erfc_inv(mut y: f64) -> f64 {
|
||||||
|
if y >= 2.0 {
|
||||||
|
return f64::NEG_INFINITY;
|
||||||
|
}
|
||||||
|
|
||||||
|
debug_assert!(y >= 0.0, "y must be nonnegative");
|
||||||
|
|
||||||
|
if y == 0.0 {
|
||||||
|
return f64::INFINITY;
|
||||||
|
}
|
||||||
|
|
||||||
|
if y >= 1.0 {
|
||||||
|
y = 2.0 - y;
|
||||||
|
}
|
||||||
|
|
||||||
|
let t = (-2.0 * (y / 2.0).ln()).sqrt();
|
||||||
|
|
||||||
|
let mut x = FRAC_1_SQRT_2 * ((2.30753 + t * 0.27061) / (1.0 + t * (0.99229 + t * 0.04481)) - t);
|
||||||
|
|
||||||
|
for _ in 0..3 {
|
||||||
|
let err = erfc(x) - y;
|
||||||
|
|
||||||
|
x += err / (FRAC_2_SQRT_PI * (-(x.powi(2))).exp() - x * err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if y < 1.0 {
|
||||||
|
x
|
||||||
|
} else {
|
||||||
|
-x
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ppf(p: f64, mu: f64, sigma: f64) -> f64 {
|
||||||
|
mu - sigma * SQRT_2 * erfc_inv(2.0 * p)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compute_margin(p_draw: f64, sd: f64) -> f64 {
|
||||||
|
ppf(0.5 - p_draw / 2.0, 0.0, sd).abs()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cdf(x: f64, mu: f64, sigma: f64) -> f64 {
|
||||||
|
let z = -(x - mu) / (sigma * SQRT_2);
|
||||||
|
|
||||||
|
0.5 * erfc(z)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pdf(x: f64, mu: f64, sigma: f64) -> f64 {
|
||||||
|
let normalizer = (SQRT_TAU * sigma).powi(-1);
|
||||||
|
let functional = (-((x - mu).powi(2)) / (2.0 * sigma.powi(2))).exp();
|
||||||
|
|
||||||
|
normalizer * functional
|
||||||
|
}
|
||||||
|
|
||||||
|
fn v_w(mu: f64, sigma: f64, margin: f64, tie: bool) -> (f64, f64) {
|
||||||
|
if !tie {
|
||||||
|
let alpha = (margin - mu) / sigma;
|
||||||
|
|
||||||
|
let v = pdf(-alpha, 0.0, 1.0) / cdf(-alpha, 0.0, 1.0);
|
||||||
|
let w = v * (v + (-alpha));
|
||||||
|
|
||||||
|
(v, w)
|
||||||
|
} else {
|
||||||
|
let alpha = (-margin - mu) / sigma;
|
||||||
|
let beta = (margin - mu) / sigma;
|
||||||
|
|
||||||
|
let v = (pdf(alpha, 0.0, 1.0) - pdf(beta, 0.0, 1.0))
|
||||||
|
/ (cdf(beta, 0.0, 1.0) - cdf(alpha, 0.0, 1.0));
|
||||||
|
let u = (alpha * pdf(alpha, 0.0, 1.0) - beta * pdf(beta, 0.0, 1.0))
|
||||||
|
/ (cdf(beta, 0.0, 1.0) - cdf(alpha, 0.0, 1.0));
|
||||||
|
let w = -(u - v.powi(2));
|
||||||
|
|
||||||
|
(v, w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn trunc(mu: f64, sigma: f64, margin: f64, tie: bool) -> (f64, f64) {
|
||||||
|
let (v, w) = v_w(mu, sigma, margin, tie);
|
||||||
|
|
||||||
|
let mu_trunc = mu + sigma * v;
|
||||||
|
let sigma_trunc = sigma * (1.0 - w).sqrt();
|
||||||
|
|
||||||
|
(mu_trunc, sigma_trunc)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn approx(n: Gaussian, margin: f64, tie: bool) -> Gaussian {
|
||||||
|
let (mu, sigma) = trunc(n.mu, n.sigma, margin, tie);
|
||||||
|
|
||||||
|
Gaussian { mu, sigma }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn tuple_max(v1: (f64, f64), v2: (f64, f64)) -> (f64, f64) {
|
||||||
|
(
|
||||||
|
if v1.0 > v2.0 { v1.0 } else { v2.0 },
|
||||||
|
if v1.1 > v2.1 { v1.1 } else { v2.1 },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn tuple_gt(t: (f64, f64), e: f64) -> bool {
|
||||||
|
t.0 > e || t.1 > e
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn sort_perm(x: &[f64], reverse: bool) -> Vec<usize> {
|
||||||
|
let mut v = x.iter().enumerate().collect::<Vec<_>>();
|
||||||
|
|
||||||
|
if reverse {
|
||||||
|
v.sort_by(|(_, a), (_, b)| b.partial_cmp(a).unwrap());
|
||||||
|
} else {
|
||||||
|
v.sort_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
v.into_iter().map(|(i, _)| i).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn sort_time(xs: &[u64], reverse: bool) -> Vec<usize> {
|
||||||
|
let mut x = xs.iter().enumerate().collect::<Vec<_>>();
|
||||||
|
|
||||||
|
if reverse {
|
||||||
|
x.sort_by_key(|(_, &x)| Reverse(x));
|
||||||
|
} else {
|
||||||
|
x.sort_by_key(|(_, &x)| x);
|
||||||
|
}
|
||||||
|
|
||||||
|
x.into_iter().map(|(i, _)| i).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn evidence(d: &[DiffMessage], margin: &[f64], tie: &[bool], e: usize) -> f64 {
|
||||||
|
if tie[e] {
|
||||||
|
cdf(margin[e], d[e].prior.mu, d[e].prior.sigma)
|
||||||
|
- cdf(-margin[e], d[e].prior.mu, d[e].prior.sigma)
|
||||||
|
} else {
|
||||||
|
1.0 - cdf(margin[e], d[e].prior.mu, d[e].prior.sigma)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_sort_perm() {
|
||||||
|
assert_eq!(sort_perm(&[0.0, 1.0, 2.0, 0.0], true), vec![2, 1, 0, 3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_sort_time() {
|
||||||
|
assert_eq!(sort_time(&[0, 1, 2, 0], true), vec![2, 1, 0, 3]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,23 +1,62 @@
|
|||||||
use crate::{Gaussian, N_INF};
|
use crate::gaussian::Gaussian;
|
||||||
|
|
||||||
#[derive(Debug)]
|
pub(crate) struct TeamMessage {
|
||||||
pub struct DiffMessages {
|
pub(crate) prior: Gaussian,
|
||||||
pub prior: Gaussian,
|
pub(crate) likelihood_lose: Gaussian,
|
||||||
pub likelihood: Gaussian,
|
pub(crate) likelihood_win: Gaussian,
|
||||||
|
pub(crate) likelihood_draw: Gaussian,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DiffMessages {
|
impl TeamMessage {
|
||||||
#[inline]
|
pub(crate) fn p(&self) -> Gaussian {
|
||||||
pub fn p(&self) -> Gaussian {
|
self.prior * self.likelihood_lose * self.likelihood_win * self.likelihood_draw
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn posterior_win(&self) -> Gaussian {
|
||||||
|
self.prior * self.likelihood_lose * self.likelihood_draw
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn posterior_lose(&self) -> Gaussian {
|
||||||
|
self.prior * self.likelihood_win * self.likelihood_draw
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn likelihood(&self) -> Gaussian {
|
||||||
|
self.likelihood_win * self.likelihood_lose * self.likelihood_draw
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct DrawMessage {
|
||||||
|
pub(crate) prior: Gaussian,
|
||||||
|
pub(crate) prior_team: Gaussian,
|
||||||
|
pub(crate) likelihood_lose: Gaussian,
|
||||||
|
pub(crate) likelihood_win: Gaussian,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DrawMessage {
|
||||||
|
pub(crate) fn p(&self) -> Gaussian {
|
||||||
|
self.prior_team * self.likelihood_lose * self.likelihood_win
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn posterior_win(&self) -> Gaussian {
|
||||||
|
self.prior_team * self.likelihood_lose
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn posterior_lose(&self) -> Gaussian {
|
||||||
|
self.prior_team * self.likelihood_win
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn likelihood(&self) -> Gaussian {
|
||||||
|
self.likelihood_win * self.likelihood_lose
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct DiffMessage {
|
||||||
|
pub(crate) prior: Gaussian,
|
||||||
|
pub(crate) likelihood: Gaussian,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DiffMessage {
|
||||||
|
pub(crate) fn p(&self) -> Gaussian {
|
||||||
self.prior * self.likelihood
|
self.prior * self.likelihood
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for DiffMessages {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
prior: N_INF,
|
|
||||||
likelihood: N_INF,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,44 +1,35 @@
|
|||||||
use crate::{Gaussian, BETA, GAMMA, N_INF};
|
use crate::{gaussian::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(crate) prior: Gaussian,
|
||||||
pub beta: f64,
|
pub(crate) beta: f64,
|
||||||
pub gamma: f64,
|
pub(crate) gamma: f64,
|
||||||
pub draw: Gaussian,
|
pub(crate) draw: Gaussian,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Player {
|
impl Player {
|
||||||
pub fn new(prior: Gaussian, beta: f64, gamma: f64, draw: Gaussian) -> Self {
|
pub fn new(prior: Gaussian, beta: f64, gamma: f64) -> Self {
|
||||||
Player {
|
Self {
|
||||||
prior,
|
prior,
|
||||||
beta,
|
beta,
|
||||||
gamma,
|
gamma,
|
||||||
draw,
|
draw: N_INF,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl Player {
|
pub(crate) fn performance(&self) -> Gaussian {
|
||||||
pub fn performance(&self) -> Gaussian {
|
self.prior.forget(self.beta, 1)
|
||||||
Gaussian::new(
|
|
||||||
self.prior.mu(),
|
|
||||||
(self.prior.sigma().powi(2) + self.beta.powi(2)).sqrt(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Player {
|
impl Default for Player {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Player::new(Gaussian::default(), BETA, GAMMA, N_INF)
|
Self {
|
||||||
|
prior: Gaussian::default(),
|
||||||
|
beta: BETA,
|
||||||
|
gamma: GAMMA,
|
||||||
|
draw: N_INF,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
248
src/utils.rs
248
src/utils.rs
@@ -1,248 +0,0 @@
|
|||||||
use std::cmp::Reverse;
|
|
||||||
use std::f64::consts::{FRAC_1_SQRT_2, FRAC_2_SQRT_PI, SQRT_2};
|
|
||||||
|
|
||||||
use crate::Gaussian;
|
|
||||||
|
|
||||||
const SQRT_TAU: f64 = 2.5066282746310002;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn erfc(x: f64) -> f64 {
|
|
||||||
let z = x.abs();
|
|
||||||
let t = 1.0 / (1.0 + z / 2.0);
|
|
||||||
|
|
||||||
let a = -0.82215223 + t * 0.17087277;
|
|
||||||
let b = 1.48851587 + t * a;
|
|
||||||
let c = -1.13520398 + t * b;
|
|
||||||
let d = 0.27886807 + t * c;
|
|
||||||
let e = -0.18628806 + t * d;
|
|
||||||
let f = 0.09678418 + t * e;
|
|
||||||
let g = 0.37409196 + t * f;
|
|
||||||
let h = 1.00002368 + t * g;
|
|
||||||
|
|
||||||
let r = t * (-z * z - 1.26551223 + t * h).exp();
|
|
||||||
|
|
||||||
if x >= 0.0 {
|
|
||||||
r
|
|
||||||
} else {
|
|
||||||
2.0 - r
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn erfc_inv(mut y: f64) -> f64 {
|
|
||||||
if y >= 2.0 {
|
|
||||||
return f64::NEG_INFINITY;
|
|
||||||
}
|
|
||||||
|
|
||||||
debug_assert!(y >= 0.0, "argument must be nonnegative");
|
|
||||||
|
|
||||||
if y == 0.0 {
|
|
||||||
return f64::INFINITY;
|
|
||||||
}
|
|
||||||
|
|
||||||
if y >= 1.0 {
|
|
||||||
y = 2.0 - y;
|
|
||||||
}
|
|
||||||
|
|
||||||
let t = (-2.0 * (y / 2.0).ln()).sqrt();
|
|
||||||
|
|
||||||
let mut x = FRAC_1_SQRT_2 * ((2.30753 + t * 0.27061) / (1.0 + t * (0.99229 + t * 0.04481)) - t);
|
|
||||||
|
|
||||||
for _ in 0..3 {
|
|
||||||
let err = erfc(x) - y;
|
|
||||||
|
|
||||||
x += err / (FRAC_2_SQRT_PI * (-(x.powi(2))).exp() - x * err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if y < 1.0 {
|
|
||||||
x
|
|
||||||
} else {
|
|
||||||
-x
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn ppf(p: f64, mu: f64, sigma: f64) -> f64 {
|
|
||||||
mu - sigma * SQRT_2 * erfc_inv(2.0 * p)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn mu_sigma(tau: f64, pi: f64) -> (f64, f64) {
|
|
||||||
if pi > 0.0 {
|
|
||||||
return (tau / pi, (1.0 / pi).sqrt());
|
|
||||||
}
|
|
||||||
|
|
||||||
if pi + 1e-5 < 0.0 {
|
|
||||||
panic!("pi should be greater than 0, got: {}", pi + 1e-5);
|
|
||||||
}
|
|
||||||
|
|
||||||
(0.0, f64::INFINITY)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn cdf(x: f64, mu: f64, sigma: f64) -> f64 {
|
|
||||||
let z = -(x - mu) / (sigma * SQRT_2);
|
|
||||||
|
|
||||||
0.5 * erfc(z)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn pdf(x: f64, mu: f64, sigma: f64) -> f64 {
|
|
||||||
let normalizer = (SQRT_TAU * sigma).powi(-1);
|
|
||||||
let functional = (-((x - mu).powi(2)) / (2.0 * sigma.powi(2))).exp();
|
|
||||||
|
|
||||||
normalizer * functional
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
def ppf(p, mu, sigma):
|
|
||||||
return mu - sigma * sqrt2 * erfcinv(2 * p)
|
|
||||||
*/
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn v_w(mu: f64, sigma: f64, margin: f64, tie: bool) -> (f64, f64) {
|
|
||||||
if !tie {
|
|
||||||
let alpha = (margin - mu) / sigma;
|
|
||||||
|
|
||||||
let v = pdf(-alpha, 0.0, 1.0) / cdf(-alpha, 0.0, 1.0);
|
|
||||||
let w = v * (v + (-alpha));
|
|
||||||
|
|
||||||
(v, w)
|
|
||||||
} else {
|
|
||||||
let alpha = (-margin - mu) / sigma;
|
|
||||||
let beta = (margin - mu) / sigma;
|
|
||||||
|
|
||||||
let v = (pdf(alpha, 0.0, 1.0) - pdf(beta, 0.0, 1.0))
|
|
||||||
/ (cdf(beta, 0.0, 1.0) - cdf(alpha, 0.0, 1.0));
|
|
||||||
let u = (alpha * pdf(alpha, 0.0, 1.0) - beta * pdf(beta, 0.0, 1.0))
|
|
||||||
/ (cdf(beta, 0.0, 1.0) - cdf(alpha, 0.0, 1.0));
|
|
||||||
let w = -(u - v.powi(2));
|
|
||||||
|
|
||||||
(v, w)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn trunc(mu: f64, sigma: f64, margin: f64, tie: bool) -> (f64, f64) {
|
|
||||||
let (v, w) = v_w(mu, sigma, margin, tie);
|
|
||||||
|
|
||||||
let mu_trunc = mu + sigma * v;
|
|
||||||
let sigma_trunc = sigma * (1.0 - w).sqrt();
|
|
||||||
|
|
||||||
(mu_trunc, sigma_trunc)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn approx(n: Gaussian, margin: f64, tie: bool) -> Gaussian {
|
|
||||||
let (mu, sigma) = trunc(n.mu(), n.sigma(), margin, tie);
|
|
||||||
|
|
||||||
Gaussian::new(mu, sigma)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn compute_margin(p_draw: f64, sd: f64) -> f64 {
|
|
||||||
ppf(0.5 - p_draw / 2.0, 0.0, sd).abs()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn sort_perm(xs: &[u16]) -> Vec<usize> {
|
|
||||||
let mut x = xs.iter().enumerate().collect::<Vec<_>>();
|
|
||||||
|
|
||||||
x.sort_unstable_by_key(|(_, x)| Reverse(*x));
|
|
||||||
|
|
||||||
x.into_iter().map(|(i, _)| i).collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn sort_time(xs: &[f64]) -> Vec<usize> {
|
|
||||||
let mut x = xs.iter().enumerate().collect::<Vec<_>>();
|
|
||||||
|
|
||||||
x.sort_unstable_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap());
|
|
||||||
|
|
||||||
x.into_iter().map(|(i, _)| i).collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use crate::{Gaussian, N01};
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_ppf() {
|
|
||||||
assert_eq!(ppf(0.3, N01.mu(), N01.sigma()), -0.5244004458961101);
|
|
||||||
|
|
||||||
let n23 = Gaussian::new(2.0, 3.0);
|
|
||||||
assert_eq!(ppf(0.3, n23.mu(), n23.sigma()), 0.4267986623116695);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_cdf() {
|
|
||||||
assert_eq!(cdf(0.3, N01.mu(), N01.sigma()), 0.6179114097962345);
|
|
||||||
|
|
||||||
let n23 = Gaussian::new(2.0, 3.0);
|
|
||||||
assert_eq!(cdf(0.3, n23.mu(), n23.sigma()), 0.28547031198297773);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_pdf() {
|
|
||||||
assert_eq!(pdf(0.3, N01.mu(), N01.sigma()), 0.38138781546052414);
|
|
||||||
|
|
||||||
let n23 = Gaussian::new(2.0, 3.0);
|
|
||||||
assert_eq!(pdf(0.3, n23.mu(), n23.sigma()), 0.11325579143491937);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_compute_margin() {
|
|
||||||
assert_eq!(
|
|
||||||
compute_margin(0.25, 2.0f64.sqrt() * (25.0 / 6.0)),
|
|
||||||
1.8776005988640154
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
compute_margin(0.25, 3.0f64.sqrt() * (25.0 / 6.0)),
|
|
||||||
2.2995817039804787
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
compute_margin(0.0, 3.0f64.sqrt() * (25.0 / 6.0)),
|
|
||||||
2.71348758713328e-7
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
compute_margin(1.0, 3.0f64.sqrt() * (25.0 / 6.0)),
|
|
||||||
f64::INFINITY
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_trunc() {
|
|
||||||
let g = Gaussian::new(0.0, 1.0);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
trunc(g.mu(), g.sigma(), 0.0, false),
|
|
||||||
(0.7978845368663289, 0.6028103066716792)
|
|
||||||
);
|
|
||||||
|
|
||||||
let g = Gaussian::new(0.0, SQRT_2 * (25.0 / 6.0));
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
trunc(g.mu(), g.sigma(), 1.8776005988, true),
|
|
||||||
(0.0, 1.0767055018086311)
|
|
||||||
);
|
|
||||||
|
|
||||||
let g = Gaussian::new(12.0, SQRT_2 * (25.0 / 6.0));
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
trunc(g.mu(), g.sigma(), 1.8776005988, true),
|
|
||||||
(0.39009949143595435, 1.034397855300721)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_sortperm() {
|
|
||||||
assert_eq!(sort_perm(&[0, 1, 2, 0]), vec![2, 1, 0, 3]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_sort_time() {
|
|
||||||
assert_eq!(sort_time(&[1.0, 1.0, 1.0]), vec![0, 1, 2]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
use crate::{Gaussian, N_INF};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct TeamVariable {
|
|
||||||
pub prior: Gaussian,
|
|
||||||
pub likelihood_lose: Gaussian,
|
|
||||||
pub likelihood_win: Gaussian,
|
|
||||||
pub likelihood_draw: Gaussian,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TeamVariable {
|
|
||||||
#[inline]
|
|
||||||
pub fn p(&self) -> Gaussian {
|
|
||||||
self.prior * self.likelihood_lose * self.likelihood_win * self.likelihood_draw
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn posterior_win(&self) -> Gaussian {
|
|
||||||
self.prior * self.likelihood_lose * self.likelihood_draw
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn posterior_lose(&self) -> Gaussian {
|
|
||||||
self.prior * self.likelihood_win * self.likelihood_draw
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn likelihood(&self) -> Gaussian {
|
|
||||||
self.likelihood_win * self.likelihood_lose * self.likelihood_draw
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for TeamVariable {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
prior: N_INF,
|
|
||||||
likelihood_lose: N_INF,
|
|
||||||
likelihood_win: N_INF,
|
|
||||||
likelihood_draw: N_INF,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user