Port from julia version instead

This commit is contained in:
2022-06-18 22:27:38 +02:00
parent 36c3366990
commit dc10504b80
13 changed files with 1141 additions and 1213 deletions

37
src/agent.rs Normal file
View 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;
}
}
}

View File

@@ -1,13 +1,15 @@
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 struct Skill {
pub forward: Gaussian,
pub backward: Gaussian,
pub likelihood: Gaussian,
pub elapsed: f64,
pub(crate) struct Skill {
pub(crate) forward: Gaussian,
backward: Gaussian,
likelihood: Gaussian,
pub(crate) elapsed: u64,
pub(crate) online: Gaussian,
}
impl Default for Skill {
@@ -16,67 +18,30 @@ impl Default for Skill {
forward: N_INF,
backward: N_INF,
likelihood: N_INF,
elapsed: 0.0,
elapsed: 0,
online: N_INF,
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct Agent {
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,
struct Item {
agent: String,
likelihood: Gaussian,
}
#[derive(Clone, Debug)]
pub struct Team {
struct Team {
items: Vec<Item>,
output: u16,
output: f64,
}
#[derive(Clone, Debug)]
pub struct Event {
struct Event {
teams: Vec<Team>,
pub evidence: f64,
evidence: f64,
weights: Vec<Vec<f64>>,
}
impl Event {
/*
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> {
fn outputs(&self) -> Vec<f64> {
self.teams
.iter()
.map(|team| team.output)
@@ -84,75 +49,126 @@ impl Event {
}
}
fn compute_elapsed(last_time: f64, actual_time: f64) -> f64 {
if last_time == f64::NEG_INFINITY {
0.0
} else if last_time == f64::INFINITY {
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,
pub(crate) struct Batch {
events: Vec<Event>,
pub(crate) skills: HashMap<String, Skill>,
pub(crate) time: u64,
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 {
pub fn new(
composition: Vec<Vec<Vec<PlayerIndex>>>,
results: Vec<Vec<u16>>,
time: f64,
agents: &mut HashMap<PlayerIndex, Agent>,
pub(crate) fn new(
composition: Vec<Vec<Vec<&str>>>,
results: Vec<Vec<f64>>,
weights: Vec<Vec<Vec<f64>>>,
time: u64,
p_draw: f64,
agents: &mut HashMap<String, Agent>,
) -> 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 {
skills: HashMap::new(),
events: Vec::new(),
time,
events,
skills,
p_draw,
};
this.add_events(composition, results, agents);
this.iteration(0, agents);
this
}
pub fn add_events(
pub(crate) fn add_events(
&mut self,
composition: Vec<Vec<Vec<PlayerIndex>>>,
results: Vec<Vec<u16>>,
agents: &mut HashMap<PlayerIndex, Agent>,
composition: Vec<Vec<Vec<&str>>>,
results: Vec<Vec<f64>>,
weights: Vec<Vec<Vec<f64>>>,
agents: &mut HashMap<String, Agent>,
) {
let this_agent = composition
.iter()
.flatten()
.flatten()
.cloned()
.collect::<HashSet<_>>();
for a in this_agent {
let elapsed = compute_elapsed(agents[a].last_time, self.time);
if let Some(skill) = self.skills.get_mut(a) {
skill.forward = agents[a].receive(elapsed);
skill.elapsed = elapsed;
skill.forward = agents[a].receive(elapsed);
} else {
self.skills.insert(
*a,
a.to_string(),
Skill {
forward: agents[a].receive(elapsed),
elapsed,
@@ -169,14 +185,18 @@ impl Batch {
.map(|t| {
let items = (0..composition[e][t].len())
.map(|a| Item {
index: composition[e][t][a],
agent: composition[e][t][a].to_string(),
likelihood: N_INF,
})
.collect::<Vec<_>>();
Team {
items,
output: results[e][t],
output: if results.is_empty() {
(composition[e].len() - (t + 1)) as f64
} else {
results[e][t]
},
}
})
.collect::<Vec<_>>();
@@ -184,6 +204,11 @@ impl Batch {
let event = Event {
teams,
evidence: 0.0,
weights: if weights.is_empty() {
Vec::new()
} else {
weights[e].clone()
},
};
self.events.push(event);
@@ -192,33 +217,45 @@ impl Batch {
self.iteration(from, agents);
}
#[inline]
pub fn posterior(&self, agent: &PlayerIndex) -> Gaussian {
pub(crate) fn posterior(&self, agent: &str) -> Gaussian {
let skill = &self.skills[agent];
skill.likelihood * skill.backward * skill.forward
}
#[inline]
pub fn posteriors(&self) -> HashMap<PlayerIndex, Gaussian> {
pub(crate) fn posteriors(&self) -> HashMap<String, Gaussian> {
self.skills
.keys()
.map(|a| (*a, self.posterior(a)))
.map(|a| (a.to_string(), self.posterior(a)))
.collect::<HashMap<_, _>>()
}
#[inline]
fn within_prior(&self, item: &Item, agents: &mut HashMap<PlayerIndex, Agent>) -> Player {
let r = &agents[&item.index].player;
let g = self.posterior(&item.index) / item.likelihood;
fn within_prior(
&self,
item: &Item,
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,
event: usize,
agents: &mut HashMap<PlayerIndex, Agent>,
online: bool,
forward: bool,
agents: &mut HashMap<String, Agent>,
) -> Vec<Vec<Player>> {
self.events[event]
.teams
@@ -226,23 +263,23 @@ impl Batch {
.map(|team| {
team.items
.iter()
.map(|item| self.within_prior(item, agents))
.map(|item| self.within_prior(item, online, forward, agents))
.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() {
let teams = self.within_priors(e, agents);
let result = self.events[e].result();
let teams = self.within_priors(e, false, false, agents);
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 (i, item) in team.items.iter_mut().enumerate() {
self.skills.get_mut(&item.index).unwrap().likelihood =
(self.skills[&item.index].likelihood / item.likelihood)
self.skills.get_mut(&item.agent).unwrap().likelihood =
(self.skills[&item.agent].likelihood / 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 iterations = 20;
let mut step = (f64::INFINITY, f64::INFINITY);
let mut i = 0;
while (step.0 > epsilon || step.1 > epsilon) && i < iterations {
while tuple_gt(step, epsilon) && i < iterations {
let old = self.posteriors();
self.iteration(0, agents);
let new = self.posteriors();
step = old.iter().fold((0.0, 0.0), |(o_l, o_r), (a, old)| {
let (n_l, n_r) = old.delta(new[a]);
(
if n_l > o_l { n_l } else { o_l },
if n_r > o_r { n_r } else { o_r },
)
step = old.iter().fold((0.0, 0.0), |step, (a, old)| {
tuple_max(step, old.delta(new[a]))
});
i += 1;
@@ -282,18 +314,16 @@ impl Batch {
i
}
#[inline]
pub fn forward_prior_out(&self, agent: &PlayerIndex) -> Gaussian {
pub(crate) fn forward_prior_out(&self, agent: &str) -> Gaussian {
let skill = &self.skills[agent];
skill.forward * skill.likelihood
}
#[inline]
pub fn backward_prior_out(
pub(crate) fn backward_prior_out(
&self,
agent: &PlayerIndex,
agents: &mut HashMap<PlayerIndex, Agent>,
agent: &str,
agents: &mut HashMap<String, Agent>,
) -> Gaussian {
let skill = &self.skills[agent];
let n = skill.likelihood * skill.backward;
@@ -301,8 +331,7 @@ impl Batch {
n.forget(agents[agent].player.gamma, skill.elapsed)
}
#[inline]
pub fn new_backward_info(&mut self, agents: &mut HashMap<PlayerIndex, Agent>) {
pub(crate) fn new_backward_info(&mut self, agents: &mut HashMap<String, Agent>) {
for (agent, skill) in self.skills.iter_mut() {
skill.backward = agents[agent].message;
}
@@ -310,8 +339,7 @@ impl Batch {
self.iteration(0, agents);
}
#[inline]
pub fn new_forward_info(&mut self, agents: &mut HashMap<PlayerIndex, Agent>) {
pub(crate) fn new_forward_info(&mut self, agents: &mut HashMap<String, Agent>) {
for (agent, skill) in self.skills.iter_mut() {
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)]
mod tests {
use approx::assert_ulps_eq;
use crate::{agent::Agent, player::Player};
use super::*;
#[test]
fn test_one_event_each() {
let mut agents = HashMap::new();
let a = PlayerIndex::new(0);
let b = PlayerIndex::new(1);
let c = PlayerIndex::new(2);
let d = PlayerIndex::new(3);
let e = PlayerIndex::new(4);
let f = PlayerIndex::new(5);
for k in [a, b, c, d, e, f] {
let agent = Agent::new(
Player::new(
Gaussian::new(25.0, 25.0 / 3.0),
25.0 / 6.0,
25.0 / 300.0,
N_INF,
),
N_INF,
f64::NEG_INFINITY,
for agent in ["a", "b", "c", "d", "e", "f"] {
agents.insert(
agent.to_string(),
Agent {
player: Player::new(Gaussian::new(25.0, 25.0 / 3.0), 25.0 / 6.0, 25.0 / 300.0),
..Default::default()
},
);
agents.insert(k, agent);
}
let mut batch = Batch::new(
vec![
vec![vec![a], vec![b]],
vec![vec![c], vec![d]],
vec![vec![e], vec![f]],
vec![vec!["a"], vec!["b"]],
vec![vec!["c"], vec!["d"]],
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,
&mut agents,
0.0,
);
let post = batch.posteriors();
assert_eq!(post[&a].mu(), 29.205220743876975);
assert_eq!(post[&a].sigma(), 7.194481422570443);
assert_ulps_eq!(post["a"].mu, 29.205220743876975, epsilon = 0.000001);
assert_ulps_eq!(post["a"].sigma, 7.194481422570443, epsilon = 0.000001);
assert_eq!(post[&b].mu(), 20.79477925612302);
assert_eq!(post[&b].sigma(), 7.194481422570443);
assert_ulps_eq!(post["b"].mu, 20.79477925612302, epsilon = 0.000001);
assert_ulps_eq!(post["b"].sigma, 7.194481422570443, epsilon = 0.000001);
assert_eq!(post[&c].mu(), 20.79477925612302);
assert_eq!(post[&c].sigma(), 7.194481422570443);
assert_ulps_eq!(post["c"].mu, 20.79477925612302, epsilon = 0.000001);
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);
}
@@ -382,123 +419,102 @@ mod tests {
fn test_same_strength() {
let mut agents = HashMap::new();
let a = PlayerIndex::new(0);
let b = PlayerIndex::new(1);
let c = PlayerIndex::new(2);
let d = PlayerIndex::new(3);
let e = PlayerIndex::new(4);
let f = PlayerIndex::new(5);
for k in [a, b, c, d, e, f] {
let agent = Agent::new(
Player::new(
Gaussian::new(25.0, 25.0 / 3.0),
25.0 / 6.0,
25.0 / 300.0,
N_INF,
),
N_INF,
f64::NEG_INFINITY,
for agent in ["a", "b", "c", "d", "e", "f"] {
agents.insert(
agent.to_string(),
Agent {
player: Player::new(Gaussian::new(25.0, 25.0 / 3.0), 25.0 / 6.0, 25.0 / 300.0),
..Default::default()
},
);
agents.insert(k, agent);
}
let mut batch = Batch::new(
vec![
vec![vec![a], vec![b]],
vec![vec![a], vec![c]],
vec![vec![b], vec![c]],
vec![vec!["a"], vec!["b"]],
vec![vec!["a"], vec!["c"]],
vec![vec!["b"], vec!["c"]],
],
vec![vec![1, 0], vec![0, 1], vec![1, 0]],
2.0,
&mut agents,
vec![vec![1.0, 0.0], vec![0.0, 1.0], vec![1.0, 0.0]],
vec![],
0,
0.0,
&mut agents,
);
let post = batch.posteriors();
assert_eq!(post[&a].mu(), 24.96097857478182);
assert_eq!(post[&a].sigma(), 6.298544763358269);
assert_ulps_eq!(post["a"].mu, 24.96097857478182, epsilon = 0.000001);
assert_ulps_eq!(post["a"].sigma, 6.298544763358269, epsilon = 0.000001);
assert_eq!(post[&b].mu(), 27.095590669107086);
assert_eq!(post[&b].sigma(), 6.010330439043099);
assert_ulps_eq!(post["b"].mu, 27.095590669107086, epsilon = 0.000001);
assert_ulps_eq!(post["b"].sigma, 6.010330439043099, epsilon = 0.000001);
assert_eq!(post[&c].mu(), 24.88968178743119);
assert_eq!(post[&c].sigma(), 5.866311348102562);
assert_ulps_eq!(post["c"].mu, 24.88968178743119, epsilon = 0.000001);
assert_ulps_eq!(post["c"].sigma, 5.866311348102562, epsilon = 0.000001);
assert!(batch.convergence(&mut agents) > 1);
let post = batch.posteriors();
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"].mu, 25.000000, 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].sigma(), 5.4192120, 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[&c].mu(), 25.000000, epsilon = 0.000001);
assert_ulps_eq!(post[&c].sigma(), 5.4192120, 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);
}
#[test]
fn test_add_events() {
let mut agents = HashMap::new();
let a = PlayerIndex::new(0);
let b = PlayerIndex::new(1);
let c = PlayerIndex::new(2);
let d = PlayerIndex::new(3);
let e = PlayerIndex::new(4);
let f = PlayerIndex::new(5);
for k in [a, b, c, d, e, f] {
let agent = Agent::new(
Player::new(
Gaussian::new(25.0, 25.0 / 3.0),
25.0 / 6.0,
25.0 / 300.0,
N_INF,
),
N_INF,
f64::NEG_INFINITY,
for agent in ["a", "b", "c", "d", "e", "f"] {
agents.insert(
agent.to_string(),
Agent {
player: Player::new(Gaussian::new(25.0, 25.0 / 3.0), 25.0 / 6.0, 25.0 / 300.0),
..Default::default()
},
);
agents.insert(k, agent);
}
let mut batch = Batch::new(
vec![
vec![vec![a], vec![b]],
vec![vec![a], vec![c]],
vec![vec![b], vec![c]],
vec![vec!["a"], vec!["b"]],
vec![vec!["a"], 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,
&mut agents,
0.0,
);
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!(p[&a].sigma(), 5.4192120, 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!(p[&b].mu(), 25.000000, epsilon = 0.000001);
assert_ulps_eq!(p[&b].sigma(), 5.4192120, 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!(p[&c].mu(), 25.000000, epsilon = 0.000001);
assert_ulps_eq!(p[&c].sigma(), 5.4192120, 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);
batch.add_events(
vec![
vec![vec![a], vec![b]],
vec![vec![a], vec![c]],
vec![vec![b], vec![c]],
vec![vec!["a"], vec!["b"]],
vec![vec!["a"], vec!["c"]],
vec![vec!["b"], vec!["c"]],
],
vec![vec![1, 0], vec![0, 1], vec![1, 0]],
vec![vec![1.0, 0.0], vec![0.0, 1.0], vec![1.0, 0.0]],
vec![],
&mut agents,
);
@@ -506,15 +522,15 @@ mod tests {
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!(p[&a].sigma(), 3.880150268080797, epsilon = 0.000001);
assert_ulps_eq!(post["a"].mu, 25.00000315330858, 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!(p[&b].sigma(), 3.880150268080797, epsilon = 0.000001);
assert_ulps_eq!(post["b"].mu, 25.00000315330858, 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!(p[&c].sigma(), 3.880150268080797, epsilon = 0.000001);
assert_ulps_eq!(post["c"].mu, 25.00000315330858, epsilon = 0.000001);
assert_ulps_eq!(post["c"].sigma, 3.880150268080797, epsilon = 0.000001);
}
}

View File

@@ -1,270 +1,222 @@
use std::collections::HashSet;
use crate::{message::DiffMessages, utils, variable::TeamVariable, Gaussian, Player, N00};
use crate::{
approx, compute_margin, evidence,
gaussian::Gaussian,
message::{DiffMessage, TeamMessage},
player::Player,
sort_perm, tuple_gt, tuple_max, N00, N_INF,
};
pub struct Game {
teams: Vec<Vec<Player>>,
result: Vec<u16>,
result: Vec<f64>,
weights: Vec<Vec<f64>>,
p_draw: f64,
pub likelihoods: Vec<Vec<Gaussian>>,
pub evidence: f64,
pub(crate) likelihoods: Vec<Vec<Gaussian>>,
pub(crate) evidence: f64,
}
impl Game {
pub fn new(teams: Vec<Vec<Player>>, result: Vec<u16>, p_draw: f64) -> Self {
if !result.is_empty() {
debug_assert!(
teams.len() == result.len(),
"len(result) and (len(teams) != len(result))"
);
pub fn new(
teams: Vec<Vec<Player>>,
mut result: Vec<f64>,
mut weights: Vec<Vec<f64>>,
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 p_draw == 0.0 {
debug_assert!(
result.iter().collect::<HashSet<_>>().len() == result.len(),
"(p_draw == 0.0) and (len(result) > 0) and (len(set(result)) != len(result))"
);
if weights.is_empty() {
weights = teams
.iter()
.map(|team| vec![1.0; team.len()])
.collect::<Vec<_>>();
}
let mut this = Self {
teams,
result,
weights,
p_draw,
likelihoods: Vec::new(),
evidence: 0.0,
};
this.compute_likelihoods();
this.likelihoods();
this
}
fn performance(&self, index: usize) -> Gaussian {
self.teams[index]
fn likelihoods(&mut self) -> &Vec<Vec<Gaussian>> {
let m_t_ft = self.likelihood_teams();
self.likelihoods = self
.teams
.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)
});
p.iter()
.zip(w.iter())
.map(|(p, &w)| {
((m - performance.exclude(p.performance() * w)) * (1.0 / w))
.forget(p.beta, 1)
})
.collect::<Vec<_>>()
})
.collect::<Vec<_>>();
&self.likelihoods
}
fn partial_evidence(&mut self, d: &[DiffMessages], margin: &[f64], tie: &[bool], e: usize) {
let mu = d[e].prior.mu();
let sigma = d[e].prior.sigma();
fn likelihood_teams(&mut self) -> Vec<Gaussian> {
let o = sort_perm(&self.result, true);
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);
}
}
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]]
let mut t = o
.iter()
.map(|&e| {
let performance = self.teams[e]
.iter()
.fold(N00, |sum, p| sum + p.performance()),
..Default::default()
})
.collect::<Vec<_>>();
.zip(self.weights[e].iter())
.fold(N00, |p, (player, &weight)| {
p + (player.performance() * weight)
});
let d = t
.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())
TeamMessage {
prior: performance,
likelihood_lose: N_INF,
likelihood_win: N_INF,
likelihood_draw: N_INF,
}
})
.collect::<Vec<_>>();
self.evidence = 1.0;
let mut d = t
.windows(2)
.map(|w| DiffMessage {
prior: w[0].prior - w[1].prior,
likelihood: N_INF,
})
.collect::<Vec<_>>();
(o, t, d, tie, margin)
}
let tie = o
.windows(2)
.map(|e| self.result[e[0]] == self.result[e[1]])
.collect::<Vec<_>>();
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,
)
let margin = if self.p_draw == 0.0 {
vec![0.0; o.len() - 1]
} 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)),
)
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 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> {
let (o, mut t, mut d, tie, margin) = self.graphical_model();
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);
for e in 0..d.len() - 1 {
d[e].prior = t[e].posterior_win() - t[e + 1].posterior_lose();
if i == 0 {
let mu = d[e].prior.mu();
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);
}
if iter == 0 {
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_lose = t[e].posterior_win() - d[e].likelihood;
let delta = 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 },
);
step = tuple_max(step, t[e + 1].likelihood_lose.delta(likelihood_lose));
t[e + 1].likelihood_lose = likelihood_lose;
}
for e in (1..d.len()).rev() {
d[e].prior = t[e].posterior_win() - t[e + 1].posterior_lose();
if i == 0 && e == d.len() - 1 {
self.partial_evidence(&d, &margin, &tie, e);
if iter == 0 && e == d.len() - 1 {
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 delta = 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 },
);
step = tuple_max(step, t[e].likelihood_win.delta(likelihood_win));
t[e].likelihood_win = likelihood_win;
}
i += 1;
iter += 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].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 d_e = d.len();
let t_end = t.len() - 1;
let d_end = d.len() - 1;
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())
.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();
}
o.iter().map(|&e| t[e].likelihood()).collect::<Vec<_>>()
}
pub fn posteriors(&self) -> Vec<Vec<Gaussian>> {
(0..self.teams.len())
.map(|e| {
(0..self.teams[e].len())
.map(|i| self.likelihoods[e][i] * self.teams[e][i].prior)
self.likelihoods
.iter()
.zip(self.teams.iter())
.map(|(l, t)| {
l.iter()
.zip(t.iter())
.map(|(&l, p)| l * p.prior)
.collect::<Vec<_>>()
})
.collect::<Vec<_>>()
@@ -273,256 +225,183 @@ impl Game {
#[cfg(test)]
mod tests {
use crate::{Gaussian, Player, GAMMA, N_INF};
use crate::{Gaussian, Player, GAMMA};
use super::*;
#[test]
fn test_1vs1() {
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_a = Player::new(Gaussian::new(25.0, 25.0 / 3.0), 25.0 / 6.0, 25.0 / 300.0);
let t_b = Player::new(Gaussian::new(25.0, 25.0 / 3.0), 25.0 / 6.0, 25.0 / 300.0);
let t_b = 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![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 a = p[0][0];
let b = p[1][0];
assert_eq!(a.mu(), 20.79477925612302);
assert_eq!(b.mu(), 29.205220743876975);
assert_eq!(a.sigma(), 7.194481422570443);
assert_eq!(a.mu, 20.79477925612302);
assert_eq!(b.mu, 29.205220743876975);
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_b = Player::new(Gaussian::new(25.0, 25.0 / 3.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);
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 a = p[0][0];
let b = p[1][0];
assert_eq!(a.mu(), 28.896475351225412);
assert_eq!(a.sigma(), 0.9966043313004235);
assert_eq!(b.mu(), 32.18921172045737);
assert_eq!(b.sigma(), 6.062063735879715);
assert_eq!(a.mu, 28.896475351225412);
assert_eq!(a.sigma, 0.9966043313004235);
assert_eq!(b.mu, 32.18921172045737);
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_b = Player::new(Gaussian::new(15.568, 0.51), 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);
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[1][0].sigma(), f64::INFINITY);
assert_eq!(g.likelihoods[0][0].mu(), 0.0);
assert_eq!(g.likelihoods[0][0].sigma, f64::INFINITY);
assert_eq!(g.likelihoods[1][0].sigma, f64::INFINITY);
assert_eq!(g.likelihoods[0][0].mu, 0.0);
}
#[test]
fn test_1vs1vs1() {
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 teams = vec![
vec![Player::new(
Gaussian::new(25.0, 25.0 / 3.0),
25.0 / 6.0,
25.0 / 300.0,
)],
vec![Player::new(
Gaussian::new(25.0, 25.0 / 3.0),
25.0 / 6.0,
25.0 / 300.0,
)],
vec![Player::new(
Gaussian::new(25.0, 25.0 / 3.0),
25.0 / 6.0,
25.0 / 300.0,
)],
];
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 a = p[0][0];
let b = p[1][0];
let c = p[2][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);
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![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);
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]
fn test_1vs1_draw() {
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_a = Player::new(Gaussian::new(25.0, 25.0 / 3.0), 25.0 / 6.0, 25.0 / 300.0);
let t_b = Player::new(Gaussian::new(25.0, 25.0 / 3.0), 25.0 / 6.0, 25.0 / 300.0);
let t_b = 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![0, 0], 0.25);
let g = Game::new(vec![vec![t_a], vec![t_b]], vec![0.0, 0.0], vec![], 0.25);
let p = g.posteriors();
let a = p[0][0];
let b = p[1][0];
assert_eq!(a.mu(), 25.0);
assert_eq!(a.sigma(), 6.469480769842277);
assert_eq!(b.mu(), 25.0);
assert_eq!(b.sigma(), 6.469480769842277);
assert_eq!(a.mu, 24.999999999999996);
assert_eq!(a.sigma, 6.469480769842277);
assert_eq!(b.mu, 24.999999999999996);
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.25);
let g = Game::new(vec![vec![t_a], vec![t_b]], vec![0.0, 0.0], vec![], 0.25);
let p = g.posteriors();
let a = p[0][0];
let b = p[1][0];
assert_eq!(a.mu(), 25.736001810566616);
assert_eq!(a.sigma(), 2.709956162204711);
assert_eq!(b.mu(), 28.67288808419261);
assert_eq!(b.sigma(), 1.9164711604544398);
assert_eq!(a.mu, 25.73600181056662);
assert_eq!(a.sigma, 2.709956162204711);
assert_eq!(b.mu, 28.67288808419261);
assert_eq!(b.sigma, 1.9164711604544398);
}
#[test]
fn test_1vs1vs1_draw() {
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 t_a = Player::new(Gaussian::new(25.0, 25.0 / 3.0), 25.0 / 6.0, 25.0 / 300.0);
let t_b = Player::new(Gaussian::new(25.0, 25.0 / 3.0), 25.0 / 6.0, 25.0 / 300.0);
let t_c = Player::new(Gaussian::new(25.0, 25.0 / 3.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 a = p[0][0];
let b = p[1][0];
let c = p[2][0];
assert_eq!(a.mu(), 24.999999999999996);
assert_eq!(a.sigma(), 5.729068664890827);
assert_eq!(b.mu(), 25.000000000000004);
assert_eq!(b.sigma(), 5.707423522433266);
assert_eq!(c.mu(), 24.999999999999996);
assert_eq!(c.sigma(), 5.729068664890825);
assert_eq!(a.mu, 24.999999999999996);
assert_eq!(a.sigma, 5.729068664890827);
assert_eq!(b.mu, 25.000000000000004);
assert_eq!(b.sigma, 5.707423522433266);
assert_eq!(c.mu, 24.999999999999996);
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_b = Player::new(Gaussian::new(25.0, 3.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, 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);
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 a = p[0][0];
let b = p[1][0];
let c = p[2][0];
assert_eq!(a.mu(), 25.48850755025261);
assert_eq!(a.sigma(), 2.638208444298423);
assert_eq!(b.mu(), 25.51067170990121);
assert_eq!(b.sigma(), 2.6287517663583633);
assert_eq!(c.mu(), 28.555920328820523);
assert_eq!(c.sigma(), 1.8856891308577184);
assert_eq!(a.mu, 25.48850755025261);
assert_eq!(a.sigma, 2.6382084442984226);
assert_eq!(b.mu, 25.510671709901217);
assert_eq!(b.sigma, 2.6287517663583633);
assert_eq!(c.mu, 28.555920328820527);
assert_eq!(c.sigma, 1.8856891308577184);
}
}

View File

@@ -1,62 +1,58 @@
use std::ops;
use crate::{utils, MU, SIGMA};
use crate::{MU, N_INF, SIGMA};
#[derive(Clone, Copy, PartialEq, Debug)]
pub struct Gaussian {
mu: f64,
sigma: f64,
pub(crate) mu: f64,
pub(crate) sigma: f64,
}
impl Gaussian {
#[inline]
pub const fn new(mu: f64, sigma: f64) -> Self {
pub fn new(mu: f64, sigma: f64) -> Self {
debug_assert!(sigma >= 0.0, "sigma must be equal or larger than 0.0");
Gaussian { mu, sigma }
}
#[inline]
pub fn mu(&self) -> f64 {
self.mu
fn pi(&self) -> f64 {
if self.sigma > 0.0 {
self.sigma.powi(-2)
} else {
f64::INFINITY
}
}
#[inline]
pub fn sigma(&self) -> f64 {
self.sigma
fn tau(&self) -> f64 {
if self.sigma > 0.0 {
self.mu * self.pi()
} else {
f64::INFINITY
}
}
#[inline]
pub fn tau(&self) -> f64 {
self.mu * self.pi()
pub(crate) fn delta(&self, m: Gaussian) -> (f64, f64) {
((self.mu - m.mu).abs(), (self.sigma - m.sigma).abs())
}
#[inline]
pub fn pi(&self) -> f64 {
self.sigma.powi(-2)
pub(crate) fn exclude(&self, m: Gaussian) -> Self {
Self {
mu: self.mu - m.mu,
sigma: (self.sigma.powi(2) - m.sigma.powi(2)).sqrt(),
}
}
#[inline]
pub fn forget(&self, gamma: f64, t: f64) -> Self {
Self::new(self.mu, (self.sigma().powi(2) + t * gamma.powi(2)).sqrt())
}
#[inline]
pub fn delta(&self, m: Gaussian) -> (f64, f64) {
((self.mu() - m.mu()).abs(), (self.sigma() - m.sigma()).abs())
}
#[inline]
pub fn exclude(&self, m: Gaussian) -> Self {
Self::new(
self.mu() - m.mu(),
(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 {
#[inline]
fn default() -> Self {
Gaussian {
Self {
mu: MU,
sigma: SIGMA,
}
@@ -66,7 +62,6 @@ impl Default for Gaussian {
impl ops::Add<Gaussian> for Gaussian {
type Output = Gaussian;
#[inline]
fn add(self, rhs: Gaussian) -> Self::Output {
Gaussian {
mu: self.mu + rhs.mu,
@@ -78,7 +73,6 @@ impl ops::Add<Gaussian> for Gaussian {
impl ops::Sub<Gaussian> for Gaussian {
type Output = Gaussian;
#[inline]
fn sub(self, rhs: Gaussian) -> Self::Output {
Gaussian {
mu: self.mu - rhs.mu,
@@ -90,25 +84,66 @@ impl ops::Sub<Gaussian> for Gaussian {
impl ops::Mul<Gaussian> for Gaussian {
type Output = Gaussian;
#[inline]
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 }
}
}
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 {
type Output = Gaussian;
#[inline]
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 }
}
}
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)]
mod tests {
use super::*;

View File

@@ -1,54 +1,76 @@
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 {
size: usize,
batches: Vec<Batch>,
agents: HashMap<PlayerIndex, Agent>,
// mu: f64,
// sigma: f64,
// beta: f64,
// gamma: f64,
p_draw: f64,
agents: HashMap<String, Agent>,
time: bool,
pub epsilon: f64,
pub iterations: usize,
pub verbose: bool,
mu: f64,
sigma: f64,
beta: f64,
gamma: f64,
p_draw: f64,
online: bool,
weights: Vec<Vec<Vec<f64>>>,
epsilon: f64,
iterations: usize,
}
impl History {
pub fn new(
composition: &[Vec<Vec<PlayerIndex>>],
results: &[Vec<u16>],
times: &[f64],
priors: HashMap<PlayerIndex, Player>,
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 {
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
.iter()
.flatten()
.flatten()
.cloned()
.collect::<HashSet<_>>();
let agents = this_agent
.into_iter()
.map(|a| {
.map(|agent| {
let player = priors
.get(a)
.get(agent)
.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 {
player,
message: N_INF,
last_time: f64::NEG_INFINITY,
..Default::default()
},
)
})
@@ -58,41 +80,45 @@ impl History {
size: composition.len(),
batches: Vec::new(),
agents,
p_draw,
time: !times.is_empty(),
epsilon: 1e-6,
iterations: 30,
verbose: true,
mu,
sigma,
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
}
fn trueskill(
&mut self,
composition: &[Vec<Vec<PlayerIndex>>],
results: &[Vec<u16>],
times: &[f64],
composition: Vec<Vec<Vec<&str>>>,
results: Vec<Vec<f64>>,
times: Vec<u64>,
weights: Vec<Vec<Vec<f64>>>,
online: bool,
) {
let o = if self.time {
utils::sort_time(times)
sort_time(&times, false)
} else {
(0..composition.len()).collect::<Vec<_>>()
};
let mut i = 0;
let mut last = 0.0;
while i < self.size {
let mut j = i + 1;
let time = if self.time {
times[o[i]]
} else {
i as f64 + 1.0
};
let t = if self.time { times[o[i]] } else { i as u64 + 1 };
while self.time && j < self.size && times[o[j]] == time {
while self.time && j < self.size && times[o[j]] == t {
j += 1;
}
@@ -100,30 +126,67 @@ impl History {
.map(|e| composition[o[e]].clone())
.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);
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];
for a in b.skills.keys() {
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);
}
i = j;
}
if online {
println!("100.00%");
}
}
fn iteration(&mut self) -> (f64, f64) {
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 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])));
}
clean(self.agents.values_mut(), false);
agent::clean(self.agents.values_mut(), false);
for j in 1..self.batches.len() {
for agent in self.batches[j - 1].skills.keys() {
@@ -164,7 +227,7 @@ impl History {
if self.batches.len() == 1 {
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();
@@ -176,12 +239,17 @@ impl History {
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 i = 0;
while (step.0 > self.epsilon || step.1 > self.epsilon) && i < self.iterations {
if self.verbose {
while tuple_gt(step, epsilon) && i < iterations {
if verbose {
print!("Iteration = {}", i);
}
@@ -189,20 +257,20 @@ impl History {
i += 1;
if self.verbose {
if verbose {
println!(", step = {:?}", step);
}
}
if self.verbose {
if verbose {
println!("End");
}
(step, i)
}
pub fn learning_curves(&self) -> HashMap<PlayerIndex, Vec<(f64, Gaussian)>> {
let mut data: HashMap<PlayerIndex, Vec<(f64, Gaussian)>> = HashMap::new();
pub fn learning_curves(&self) -> HashMap<String, Vec<(u64, Gaussian)>> {
let mut data: HashMap<String, Vec<(u64, Gaussian)>> = HashMap::new();
for b in &self.batches {
for agent in b.skills.keys() {
@@ -211,266 +279,234 @@ impl History {
if let Some(entry) = data.get_mut(agent) {
entry.push(point);
} else {
data.insert(*agent, vec![point]);
data.insert(agent.to_string(), vec![point]);
}
}
}
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)]
mod tests {
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::*;
#[test]
fn test_init() {
let a = PlayerIndex::new(0);
let b = PlayerIndex::new(1);
let c = PlayerIndex::new(2);
let composition = vec![
vec![vec![a], vec![b]],
vec![vec![a], vec![c]],
vec![vec![b], vec![c]],
vec![vec!["a"], vec!["b"]],
vec![vec!["a"], vec!["c"]],
vec![vec!["b"], vec!["c"]],
];
let results = vec![vec![1, 0], vec![0, 1], vec![1, 0]];
let results = vec![vec![1.0, 0.0], vec![0.0, 1.0], vec![1.0, 0.0]];
let mut priors = HashMap::new();
for k in [a, b, c] {
let player = Player::new(
Gaussian::new(25.0, 25.0 / 3.0),
25.0 / 6.0,
0.15 * 25.0 / 3.0,
N_INF,
for agent in ["a", "b", "c"] {
priors.insert(
agent.to_string(),
Player::new(
Gaussian::new(25.0, 25.0 / 3.0),
25.0 / 6.0,
0.15 * 25.0 / 3.0,
),
);
priors.insert(k, player);
}
let mut h = History::new(
&composition,
&results,
&[1.0, 2.0, 3.0],
composition,
results,
vec![1, 2, 3],
vec![],
priors,
MU,
BETA,
SIGMA,
BETA,
GAMMA,
P_DRAW,
false,
);
let p0 = h.batches[0].posteriors();
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"].mu, 29.205220743876975, 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 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);
let observed = h.batches[1].posterior(&a);
let observed = h.batches[1].posterior("a");
let p = Game::new(
h.batches[1].within_priors(0, &mut h.agents),
vec![0, 1],
h.batches[1].within_priors(0, false, false, &mut h.agents),
vec![0.0, 1.0],
vec![],
P_DRAW,
)
.posteriors();
let expected = p[0][0];
assert_ulps_eq!(observed.mu(), expected.mu(), epsilon = 0.000001);
assert_ulps_eq!(observed.sigma(), expected.sigma(), epsilon = 0.000001);
assert_ulps_eq!(observed.mu, expected.mu, epsilon = 0.000001);
assert_ulps_eq!(observed.sigma, expected.sigma, epsilon = 0.000001);
}
#[test]
fn test_one_batch() {
let a = PlayerIndex::new(0);
let b = PlayerIndex::new(1);
let c = PlayerIndex::new(2);
let composition = vec![
vec![vec![a], vec![b]],
vec![vec![b], vec![c]],
vec![vec![c], vec![a]],
vec![vec!["a"], vec!["b"]],
vec![vec!["b"], vec!["c"]],
vec![vec!["c"], vec!["a"]],
];
let results = vec![vec![1, 0], vec![1, 0], vec![1, 0]];
let times = vec![1.0, 1.0, 1.0];
let results = vec![vec![1.0, 0.0], vec![1.0, 0.0], vec![1.0, 0.0]];
let times = vec![1, 1, 1];
let mut priors = HashMap::new();
for k in [a, b, c] {
for k in ["a", "b", "c"] {
let player = Player::new(
Gaussian::new(25.0, 25.0 / 3.0),
25.0 / 6.0,
0.15 * 25.0 / 3.0,
N_INF,
);
priors.insert(k, player);
priors.insert(k.to_string(), player);
}
let mut h1 = History::new(
&composition,
&results,
&times,
composition,
results,
times,
vec![],
priors,
MU,
BETA,
SIGMA,
BETA,
GAMMA,
P_DRAW,
false,
);
assert_ulps_eq!(
h1.batches[0].posterior(&a).mu(),
h1.batches[0].posterior("a").mu,
22.904409330892914,
epsilon = 0.000001
);
assert_ulps_eq!(
h1.batches[0].posterior(&a).sigma(),
h1.batches[0].posterior("a").sigma,
6.0103304390431,
epsilon = 0.000001
);
assert_ulps_eq!(
h1.batches[0].posterior(&c).mu(),
h1.batches[0].posterior("c").mu,
25.110318212568806,
epsilon = 0.000001
);
assert_ulps_eq!(
h1.batches[0].posterior(&c).sigma(),
h1.batches[0].posterior("c").sigma,
5.866311348102563,
epsilon = 0.000001
);
let (_step, _i) = h1.convergence();
let (_step, _i) = h1.convergence(ITERATIONS, EPSILON, false);
assert_ulps_eq!(
h1.batches[0].posterior(&a).mu(),
h1.batches[0].posterior("a").mu,
25.00000000,
epsilon = 0.000001
);
assert_ulps_eq!(
h1.batches[0].posterior(&a).sigma(),
h1.batches[0].posterior("a").sigma,
5.41921200,
epsilon = 0.000001
);
assert_ulps_eq!(
h1.batches[0].posterior(&c).mu(),
h1.batches[0].posterior("c").mu,
25.00000000,
epsilon = 0.000001
);
assert_ulps_eq!(
h1.batches[0].posterior(&c).sigma(),
h1.batches[0].posterior("c").sigma,
5.41921200,
epsilon = 0.000001
);
let composition = vec![
vec![vec![a], vec![b]],
vec![vec![b], vec![c]],
vec![vec![c], vec![a]],
vec![vec!["a"], vec!["b"]],
vec![vec!["b"], vec!["c"]],
vec![vec!["c"], vec!["a"]],
];
let results = vec![vec![1, 0], vec![1, 0], vec![1, 0]];
let times = vec![1.0, 2.0, 3.0];
let results = vec![vec![1.0, 0.0], vec![1.0, 0.0], vec![1.0, 0.0]];
let times = vec![1, 2, 3];
let mut priors = HashMap::new();
for k in [a, b, c] {
let player = Player::new(
Gaussian::new(25.0, 25.0 / 3.0),
25.0 / 6.0,
25.0 / 300.0,
N_INF,
);
for k in ["a", "b", "c"] {
let player = Player::new(Gaussian::new(25.0, 25.0 / 3.0), 25.0 / 6.0, 25.0 / 300.0);
priors.insert(k, player);
priors.insert(k.to_string(), player);
}
let mut h2 = History::new(
&composition,
&results,
&times,
composition,
results,
times,
vec![],
priors,
MU,
BETA,
SIGMA,
BETA,
GAMMA,
P_DRAW,
false,
);
assert_ulps_eq!(
h2.batches[2].posterior(&a).mu(),
h2.batches[2].posterior("a").mu,
22.90352227792141,
epsilon = 0.000001
);
assert_ulps_eq!(
h2.batches[2].posterior(&a).sigma(),
h2.batches[2].posterior("a").sigma,
6.011017301320632,
epsilon = 0.000001
);
assert_ulps_eq!(
h2.batches[2].posterior(&c).mu(),
h2.batches[2].posterior("c").mu,
25.110702468366718,
epsilon = 0.000001
);
assert_ulps_eq!(
h2.batches[2].posterior(&c).sigma(),
h2.batches[2].posterior("c").sigma,
5.866811597660157,
epsilon = 0.000001
);
let (_step, _i) = h2.convergence();
let (_step, _i) = h2.convergence(ITERATIONS, EPSILON, false);
assert_ulps_eq!(
h2.batches[2].posterior(&a).mu(),
h2.batches[2].posterior("a").mu,
24.99866831022851,
epsilon = 0.000001
);
assert_ulps_eq!(
h2.batches[2].posterior(&a).sigma(),
h2.batches[2].posterior("a").sigma,
5.420053708148435,
epsilon = 0.000001
);
assert_ulps_eq!(
h2.batches[2].posterior(&c).mu(),
h2.batches[2].posterior("c").mu,
25.000532179593538,
epsilon = 0.000001
);
assert_ulps_eq!(
h2.batches[2].posterior(&c).sigma(),
h2.batches[2].posterior("c").sigma,
5.419827012784138,
epsilon = 0.000001
);
@@ -478,70 +514,63 @@ mod tests {
#[test]
fn test_learning_curves() {
let a = PlayerIndex::new(0);
let b = PlayerIndex::new(1);
let c = PlayerIndex::new(2);
let composition = vec![
vec![vec![a], vec![b]],
vec![vec![b], vec![c]],
vec![vec![c], vec![a]],
vec![vec!["a"], vec!["b"]],
vec![vec!["b"], vec!["c"]],
vec![vec!["c"], vec!["a"]],
];
let results = vec![vec![1, 0], vec![1, 0], vec![1, 0]];
let times = vec![5.0, 6.0, 7.0];
let results = vec![vec![1.0, 0.0], vec![1.0, 0.0], vec![1.0, 0.0]];
let times = vec![5, 6, 7];
let mut priors = HashMap::new();
for k in [a, b, c] {
let player = Player::new(
Gaussian::new(25.0, 25.0 / 3.0),
25.0 / 6.0,
25.0 / 300.0,
N_INF,
);
for k in ["a", "b", "c"] {
let player = Player::new(Gaussian::new(25.0, 25.0 / 3.0), 25.0 / 6.0, 25.0 / 300.0);
priors.insert(k, player);
priors.insert(k.to_string(), player);
}
let mut h = History::new(
&composition,
&results,
&times,
composition,
results,
times,
vec![],
priors,
MU,
BETA,
SIGMA,
BETA,
GAMMA,
P_DRAW,
false,
);
h.convergence();
h.convergence(ITERATIONS, EPSILON, false);
let lc = h.learning_curves();
let aj_e = lc[&a].len();
let cj_e = lc[&c].len();
let aj_e = lc["a"].len();
let cj_e = lc["c"].len();
assert_eq!(lc[&a][0].0, 5.0);
assert_eq!(lc[&a][aj_e - 1].0, 7.0);
assert_eq!(lc["a"][0].0, 5);
assert_eq!(lc["a"][aj_e - 1].0, 7);
assert_ulps_eq!(
lc[&a][aj_e - 1].1.mu(),
lc["a"][aj_e - 1].1.mu,
24.99866831022851,
epsilon = 0.000001
);
assert_ulps_eq!(
lc[&a][aj_e - 1].1.sigma(),
lc["a"][aj_e - 1].1.sigma,
5.420053708148435,
epsilon = 0.000001
);
assert_ulps_eq!(
lc[&c][cj_e - 1].1.mu(),
lc["c"][cj_e - 1].1.mu,
25.000532179593538,
epsilon = 0.000001
);
assert_ulps_eq!(
lc[&c][cj_e - 1].1.sigma(),
lc["c"][cj_e - 1].1.sigma,
5.419827012784138,
epsilon = 0.000001
);
@@ -549,61 +578,59 @@ mod tests {
#[test]
fn test_env_ttt() {
let a = PlayerIndex::new(0);
let b = PlayerIndex::new(1);
let c = PlayerIndex::new(2);
let composition = vec![
vec![vec![a], vec![b]],
vec![vec![a], vec![c]],
vec![vec![b], vec![c]],
vec![vec!["a"], vec!["b"]],
vec![vec!["a"], vec!["c"]],
vec![vec!["b"], vec!["c"]],
];
let results = vec![vec![1, 0], vec![0, 1], vec![1, 0]];
let results = vec![vec![1.0, 0.0], vec![0.0, 1.0], vec![1.0, 0.0]];
let mut h = History::new(
&composition,
&results,
&[],
composition,
results,
vec![],
vec![],
HashMap::new(),
25.0,
25.0 / 3.0,
25.0 / 6.0,
25.0 / 300.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[&c].elapsed, 1.0);
assert_eq!(h.batches[2].skills["b"].elapsed, 1);
assert_eq!(h.batches[2].skills["c"].elapsed, 1);
assert_ulps_eq!(
h.batches[0].posterior(&a).mu(),
h.batches[0].posterior("a").mu,
25.0002673,
epsilon = 0.000001
);
assert_ulps_eq!(
h.batches[0].posterior(&a).sigma(),
h.batches[0].posterior("a").sigma,
5.41938162,
epsilon = 0.000001
);
assert_ulps_eq!(
h.batches[0].posterior(&b).mu(),
h.batches[0].posterior("b").mu,
24.999465,
epsilon = 0.000001
);
assert_ulps_eq!(
h.batches[0].posterior(&b).sigma(),
h.batches[0].posterior("b").sigma,
5.419425831,
epsilon = 0.000001
);
assert_ulps_eq!(
h.batches[2].posterior(&b).mu(),
h.batches[2].posterior("b").mu,
25.00053219,
epsilon = 0.000001
);
assert_ulps_eq!(
h.batches[2].posterior(&b).sigma(),
h.batches[2].posterior("b").sigma,
5.419696790,
epsilon = 0.000001
);

View File

@@ -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 game;
mod gaussian;
mod history;
mod message;
mod player;
mod utils;
mod variable;
pub use batch::*;
pub use game::*;
pub use gaussian::*;
pub use history::*;
pub use player::*;
use gaussian::Gaussian;
use message::DiffMessage;
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 SIGMA: f64 = BETA * 6.0;
pub const GAMMA: f64 = BETA * 0.03;
pub const P_DRAW: f64 = 0.0;
const GAMMA: f64 = BETA * 0.03;
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);
pub const N00: Gaussian = Gaussian::new(0.0, 0.0);
pub const N_INF: Gaussian = Gaussian::new(0.0, f64::INFINITY);
pub const N_MS: Gaussian = Gaussian::new(MU, SIGMA);
const SQRT_TAU: f64 = 2.5066282746310002;
const N01: Gaussian = Gaussian {
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]);
}
}

View File

@@ -1,23 +1,62 @@
use crate::{Gaussian, N_INF};
use crate::gaussian::Gaussian;
#[derive(Debug)]
pub struct DiffMessages {
pub prior: Gaussian,
pub likelihood: Gaussian,
pub(crate) struct TeamMessage {
pub(crate) prior: Gaussian,
pub(crate) likelihood_lose: Gaussian,
pub(crate) likelihood_win: Gaussian,
pub(crate) likelihood_draw: Gaussian,
}
impl DiffMessages {
#[inline]
pub fn p(&self) -> Gaussian {
impl TeamMessage {
pub(crate) 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
}
}
impl Default for DiffMessages {
fn default() -> Self {
Self {
prior: N_INF,
likelihood: N_INF,
}
}
}

View File

@@ -1,44 +1,35 @@
use crate::{Gaussian, BETA, GAMMA, N_INF};
#[derive(Copy, Clone, Debug, Default, PartialEq, PartialOrd, Eq, Ord, Hash)]
pub struct PlayerIndex(usize);
impl PlayerIndex {
pub fn new(index: usize) -> Self {
Self(index)
}
}
use crate::{gaussian::Gaussian, BETA, GAMMA, N_INF};
#[derive(Clone, Copy, Debug)]
pub struct Player {
pub prior: Gaussian,
pub beta: f64,
pub gamma: f64,
pub draw: Gaussian,
pub(crate) prior: Gaussian,
pub(crate) beta: f64,
pub(crate) gamma: f64,
pub(crate) draw: Gaussian,
}
impl Player {
pub fn new(prior: Gaussian, beta: f64, gamma: f64, draw: Gaussian) -> Self {
Player {
pub fn new(prior: Gaussian, beta: f64, gamma: f64) -> Self {
Self {
prior,
beta,
gamma,
draw,
draw: N_INF,
}
}
}
impl Player {
pub fn performance(&self) -> Gaussian {
Gaussian::new(
self.prior.mu(),
(self.prior.sigma().powi(2) + self.beta.powi(2)).sqrt(),
)
pub(crate) fn performance(&self) -> Gaussian {
self.prior.forget(self.beta, 1)
}
}
impl Default for Player {
fn default() -> Self {
Player::new(Gaussian::default(), BETA, GAMMA, N_INF)
Self {
prior: Gaussian::default(),
beta: BETA,
gamma: GAMMA,
draw: N_INF,
}
}
}

View File

@@ -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]);
}
}

View File

@@ -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,
}
}
}