Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 45
0.00% covered (danger)
0.00%
0 / 13
CRAP
0.00% covered (danger)
0.00%
0 / 1
TrueSkill
0.00% covered (danger)
0.00%
0 / 45
0.00% covered (danger)
0.00%
0 / 13
182
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 drawProbability
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 drawMargin
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 vWin
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 vDraw
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
2
 wWin
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 wDraw
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 buildRatingLayer
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 buildPerformanceLayer
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 buildTeamPerformanceLayer
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 buildTruncLayer
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 factorGraphBuilders
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 rating
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 * Jingga
4 *
5 * PHP Version 8.1
6 *
7 * @package   phpOMS\Algorithm\Rating
8 * @copyright Microsoft
9 * @license   This algorithm may be patented by Microsoft, verify and acquire a license if necessary
10 * @version   1.0.0
11 * @link      https://jingga.app
12 */
13declare(strict_types=1);
14
15namespace phpOMS\Algorithm\Rating;
16
17use phpOMS\Math\Stochastic\Distribution\NormalDistribution;
18
19/**
20 * Elo rating calculation using Elo rating
21 *
22 * @package phpOMS\Algorithm\Rating
23 * @license OMS License 2.0
24 * @link    https://jingga.app
25 * @since   1.0.0
26 * @see     https://www.moserware.com/assets/computing-your-skill/The%20Math%20Behind%20TrueSkill.pdf
27 *
28 * @todo implement https://github.com/sublee/trueskill/blob/master/trueskill/__init__.py
29 */
30class TrueSkill
31{
32    public int $DEFAULT_MU = 25;
33
34    public float $DEFAULT_SIGMA = 25 / 3;
35
36    public float $DEFAULT_BETA = 25 / 3 / 2;
37
38    public float $DEFAULT_TAU = 25 / 3 / 100;
39
40    public float $DEFAULT_DRAW_PROBABILITY = 0.1;
41
42    public function __construct()
43    {
44    }
45
46    // Draw margin = epsilon
47    /**
48     * P_{draw} = 2\Phi\left(\dfrac{\epsilon}{\sqrt{n_1 + n_2} * \beta}\right) - 1
49     */
50    public function drawProbability(float $drawMargin, int $n1, int $n2, float $beta)
51    {
52        return 2 * NormalDistribution::getCdf($drawMargin / (\sqrt($n1 + $n2) * $beta), 0.0, 1.0) - 1;
53    }
54
55    /**
56     * \epsilon = \Phi^{-1}\left(\dfrac{P_{draw} + 1}{2}\right) * \sqrt{n_1 + n_2} * \beta
57     */
58    public function drawMargin(float $drawProbability, int $n1, int $n2, float $beta)
59    {
60        return NormalDistribution::getIcdf(($drawProbability + 1) / 2.0, 0.0, 1.0) * \sqrt($n1 + $n2) * $beta;
61    }
62
63    /**
64     * Mean additive truncated gaussion function "v" for wins
65     *
66     * @latex c = \sqrt{2 * \beta^2 + \sigma_{winner}^2 + \sigma_{loser}^2}
67     * @latex \mu_{winner} = \mu_{winner} + \dfrac{\sigma_{winner}^2}{c} * \nu \left(\dfrac{\mu_{winner} - \mu_{loser}}{c}, \dfrac{\epsilon}{c}\right)
68     * @latex \mu_{loser} = \mu_{loser} + \dfrac{\sigma_{loser}^2}{c} * \nu \left(\dfrac{\mu_{winner} - \mu_{loser}}{c}, \dfrac{\epsilon}{c}\right)
69     * @latex t = \dfrac{\mu_{winner} - \mu_{loser}}{c}
70     *
71     * @latex \nu = \dfrac{\mathcal{N}(t - \epsilon)}{\Phi(t - \epsilon)}
72     *
73     * @param float $t       Difference winner and loser mu
74     * @param float $epsilon Draw margin
75     *
76     * @return float
77     *
78     * @since 1.0.0
79     */
80    private function vWin(float $t, float $epsilon) : float
81    {
82        return NormalDistribution::getPdf($t - $epsilon, 0, 1.0) / NormalDistribution::getCdf($t - $epsilon, 0.0, 1.0);
83    }
84
85    /**
86     * Mean additive truncated gaussion function "v" for draws
87     *
88     * @latex c = \sqrt{2 * \beta^2 + \sigma_{winner}^2 + \sigma_{loser}^2}
89     * @latex \mu_{winner} = \mu_{winner} + \dfrac{\sigma_{winner}^2}{c} * \nu \left(\dfrac{\mu_{winner} - \mu_{loser}}{c}, \dfrac{\epsilon}{c}\right)
90     * @latex \mu_{loser} = \mu_{loser} + \dfrac{\sigma_{loser}^2}{c} * \nu \left(\dfrac{\mu_{winner} - \mu_{loser}}{c}, \dfrac{\epsilon}{c}\right)
91     * @latex t = \dfrac{\mu_{winner} - \mu_{loser}}{c}
92     * @latex \dfrac{\mathcal{N}(t - \epsilon)}{\Phi(t - \epsilon)}
93     *
94     * @latex \nu = \dfrac{\mathcal{N}(-\epsilon - t) - \mathcal{N}(\epsilon - t)}{\Phi(\epsilon - t) - \Phi(-\epsilon - t)}
95     *
96     * @param float $t       Difference winner and loser mu
97     * @param float $epsilon Draw margin
98     *
99     * @return float
100     *
101     * @since 1.0.0
102     */
103    private function vDraw(float $t, float $epsilon) : float
104    {
105        $tAbs = \abs($t);
106        $a    = $epsilon - $tAbs;
107        $b    = -$epsilon - $tAbs;
108
109        $aPdf  = NormalDistribution::getPdf($a, 0.0, 1.0);
110        $bPdf  = NormalDistribution::getPdf($b, 0.0, 1.0);
111        $numer = $bPdf - $aPdf;
112
113        $aCdf  = NormalDistribution::getCdf($a, 0.0, 1.0);
114        $bCdf  = NormalDistribution::getCdf($b, 0.0, 1.0);
115        $denom = $aCdf - $bCdf;
116
117        return $numer / $denom;
118    }
119
120    /**
121     * Variance multiplicative function "w" for draws
122     *
123     * @latex w = \nu * (\nu + t - \epsilon)
124     *
125     * @param float $t       Difference winner and loser mu
126     * @param float $epsilon Draw margin
127     *
128     * @return float
129     *
130     * @since 1.0.0
131     */
132    private function wWin(float $t, float $epsilon) : float
133    {
134        $v = $this->vWin($t, $epsilon);
135
136        return $v * ($v + $t - $epsilon);
137    }
138
139    /**
140     * Variance multiplicative function "w" for draws
141     *
142     * @latex w = \nu^2 + \dfrac{(\epsilon - t) * \mathcal{N}(\epsilon - t) + (\epsilon + t) * \mathcal{N}(\epsilon + t)}{\Phi(\epsilon - t) - \Phi(-\epsilon - t)}
143     *
144     * @param float $t       Difference winner and loser mu
145     * @param float $epsilon Draw margin
146     *
147     * @return float
148     *
149     * @since 1.0.0
150     */
151    private function wDraw(float $t, float $epsilon) : float
152    {
153        $tAbs = \abs($t);
154
155        $v = $this->vDraw($t, $epsilon);
156
157        return $v * $v
158            + (($epsilon - $t) * NormalDistribution::getPdf($epsilon - $tAbs, 0.0, 1.0) + ($epsilon + $tAbs) * NormalDistribution::getPdf($epsilon + $tAbs, 0.0, 1.0))
159                / (NormalDistribution::getCdf($epsilon - $tAbs, 0.0, 1.0) - NormalDistribution::getCdf(-$epsilon - $tAbs, 0.0, 1.0));
160    }
161
162    private function buildRatingLayer() : void
163    {
164    }
165
166    private function buildPerformanceLayer() : void
167    {
168    }
169
170    private function buildTeamPerformanceLayer() : void
171    {
172    }
173
174    private function buildTruncLayer() : void
175    {
176    }
177
178    private function factorGraphBuilders()
179    {
180        // Rating layer
181
182        // Performance layer
183
184        // Team Performance layer
185
186        // Trunc layer
187
188        return [
189            'rating_layer'           => $ratingLayer,
190            'performance_layer'      => $ratingLayer,
191            'team_performance_layer' => $ratingLayer,
192            'trunc_layer'            => $ratingLayer,
193        ];
194    }
195
196    public function rating() : void
197    {
198        // Start values
199        $mu    = 25;
200        $sigma = $mu / 3;
201        $beta  = $sigma / 2;
202        $tau   = $sigma / 100;
203        $Pdraw = 0.1;
204
205        $alpha = 0.25;
206
207        // Partial update
208        $sigmaPartial = $sigmaOld * $sigmaNew / \sqrt($alpha * $sigmaOld * $sigmaOld - ($alpha - 1) * $sigmaNew * $sigmaNew);
209        $muPartial    = $muOld * ($alpha - 1) * $sigmaNew * $sigmaNew - $muNew * $alpha * $sigmaOld * $sigmaOld
210            / (($alpha - 1) * $sigmaNew * $sigmaNew - $alpha * $sigmaOld * $sigmaOld);
211
212        // New
213        $tau = $pi * $mu;
214
215        $P     = NormalDistribution::getCdf(($s1 - $s2) / (\sqrt(2) * $beta));
216        $Delta = $alpha * $beta * \sqrt($pi) * (($y + 1) / 2 - $P);
217
218        $K = NormalDistribution::getCdf();
219
220        $pi = 1 / ($sigma * $sigma);
221    }
222}