trueskill 0.9.2 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/HISTORY.md +6 -0
- data/lib/saulabs/gauss.rb +5 -2
- data/lib/saulabs/gauss/distribution.rb +20 -1
- data/lib/saulabs/trueskill.rb +37 -3
- data/lib/saulabs/trueskill/factor_graph.rb +1 -1
- data/spec/saulabs/gauss/distribution_spec.rb +32 -4
- data/spec/saulabs/gauss/truncated_correction_spec.rb +3 -1
- data/spec/saulabs/trueskill/factor_graph_spec.rb +73 -21
- data/spec/saulabs/trueskill/factors/greater_than_spec.rb +1 -1
- data/spec/saulabs/trueskill/factors/likelihood_spec.rb +1 -1
- data/spec/saulabs/trueskill/factors/prior_spec.rb +1 -1
- data/spec/saulabs/trueskill/factors/weighted_sum_spec.rb +1 -1
- data/spec/saulabs/trueskill/factors/within_spec.rb +1 -1
- data/spec/saulabs/trueskill/layers/prior_to_skills_spec.rb +1 -1
- data/spec/saulabs/trueskill/schedules_spec.rb +1 -1
- data/spec/spec_helper.rb +3 -2
- data/spec/true_skill_matchers.rb +39 -0
- metadata +7 -5
data/HISTORY.md
CHANGED
data/lib/saulabs/gauss.rb
CHANGED
@@ -74,10 +74,29 @@ module Saulabs
|
|
74
74
|
|
75
75
|
# The inverse of the cummulative Gaussian distribution function
|
76
76
|
def quantile_function(x)
|
77
|
-
-@@sqrt2 *
|
77
|
+
-@@sqrt2 * inv_erf(2.0 * x)
|
78
78
|
end
|
79
79
|
alias_method :inv_cdf, :quantile_function
|
80
80
|
|
81
|
+
def inv_erf(p)
|
82
|
+
return -100 if p >= 2.0
|
83
|
+
return 100 if p <= 0.0
|
84
|
+
|
85
|
+
pp = p < 1.0 ? p : 2 - p
|
86
|
+
t = Math.sqrt(-2*Math.log(pp/2.0)) # Initial guess
|
87
|
+
x = -0.70711*((2.30753 + t*0.27061)/(1.0 + t*(0.99229 + t*0.04481)) - t)
|
88
|
+
|
89
|
+
[0,1].each do |j|
|
90
|
+
err = erf(x) - pp
|
91
|
+
x += err/(1.12837916709551257*Math.exp(-(x*x)) - x*err) # Halley
|
92
|
+
end
|
93
|
+
p < 1.0 ? x : -x
|
94
|
+
end
|
95
|
+
|
96
|
+
def erf(x)
|
97
|
+
Math.erfc(x)
|
98
|
+
end
|
99
|
+
|
81
100
|
end
|
82
101
|
|
83
102
|
def value_at(x)
|
data/lib/saulabs/trueskill.rb
CHANGED
@@ -1,8 +1,42 @@
|
|
1
1
|
# -*- encoding : utf-8 -*-
|
2
2
|
require 'pp'
|
3
3
|
|
4
|
-
require
|
4
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "gauss.rb"))
|
5
5
|
|
6
|
-
|
7
|
-
|
6
|
+
%w(
|
7
|
+
base greater_than
|
8
|
+
likelihood
|
9
|
+
prior
|
10
|
+
weighted_sum
|
11
|
+
within
|
12
|
+
).each do |name|
|
13
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "trueskill", "factors", "#{name}.rb"))
|
14
|
+
end
|
15
|
+
|
16
|
+
%w(
|
17
|
+
base
|
18
|
+
iterated_team_performances
|
19
|
+
performances_to_team_performances
|
20
|
+
prior_to_skills
|
21
|
+
skills_to_performances
|
22
|
+
team_difference_comparision
|
23
|
+
team_performance_differences
|
24
|
+
).each do |name|
|
25
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "trueskill", "layers", "#{name}.rb"))
|
26
|
+
end
|
27
|
+
|
28
|
+
%w(
|
29
|
+
base
|
30
|
+
loop
|
31
|
+
sequence
|
32
|
+
step
|
33
|
+
).each do |name|
|
34
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "trueskill", "schedules", "#{name}.rb"))
|
35
|
+
end
|
36
|
+
|
37
|
+
%w(
|
38
|
+
rating
|
39
|
+
factor_graph
|
40
|
+
).each do |name|
|
41
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "trueskill", "#{name}.rb"))
|
8
42
|
end
|
@@ -46,7 +46,7 @@ module Saulabs
|
|
46
46
|
# include Saulabs::TrueSkill
|
47
47
|
#
|
48
48
|
# # team 1 has just one player with a mean skill of 27.1, a skill-deviation of 2.13
|
49
|
-
# # and
|
49
|
+
# # and a play activity of 100 %
|
50
50
|
# team1 = [Rating.new(27.1, 2.13, 1.0)]
|
51
51
|
#
|
52
52
|
# # team 2 has two players
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# -*- encoding : utf-8 -*-
|
2
|
-
require File.
|
2
|
+
require File.expand_path('spec/spec_helper.rb')
|
3
3
|
|
4
4
|
describe Gauss::Distribution, "#initialize" do
|
5
5
|
|
@@ -130,7 +130,7 @@ describe Gauss::Distribution, "functions" do
|
|
130
130
|
|
131
131
|
describe 'value = 0.27' do
|
132
132
|
|
133
|
-
it "#cumulative_distribution_function should return 0.6064198" do
|
133
|
+
it "#cumulative_distribution_function should return 0.6064198 for 0.27" do
|
134
134
|
Gauss::Distribution.cumulative_distribution_function(0.27).should be_close(0.6064198, 0.00001)
|
135
135
|
Gauss::Distribution.cdf(2.0).should be_close(0.9772498, 0.00001)
|
136
136
|
end
|
@@ -139,8 +139,36 @@ describe Gauss::Distribution, "functions" do
|
|
139
139
|
Gauss::Distribution.probability_density_function(0.27).should be_close(0.384662, 0.0001)
|
140
140
|
end
|
141
141
|
|
142
|
-
it "#quantile_function should return -0.
|
143
|
-
Gauss::Distribution.quantile_function(0.27).should be_close(-0.
|
142
|
+
it "#quantile_function should return ~ -0.6128123 at 0.27" do
|
143
|
+
Gauss::Distribution.quantile_function(0.27).should be_close(-0.6128123, 0.00001)
|
144
|
+
end
|
145
|
+
|
146
|
+
it "#quantile_function should return ~ 1.281551 at 0.9" do
|
147
|
+
Gauss::Distribution.quantile_function(0.9).should be_close(1.281551, 0.00001)
|
148
|
+
end
|
149
|
+
|
150
|
+
it "#erf_inv should return 0.0888559 at 0.9" do
|
151
|
+
Gauss::Distribution.inv_erf(0.9).should be_close(0.0888559, 0.00001)
|
152
|
+
end
|
153
|
+
|
154
|
+
it "#erf_inv should return 0.779983 at 0.27" do
|
155
|
+
Gauss::Distribution.inv_erf(0.27).should be_close(0.779983, 0.00001)
|
156
|
+
end
|
157
|
+
|
158
|
+
it "#erf_inv should return 100 at -0.5" do
|
159
|
+
Gauss::Distribution.inv_erf(-0.5).should be_close(100, 0.00001)
|
160
|
+
end
|
161
|
+
|
162
|
+
it "#erf should return 0.203091 at 0.9" do
|
163
|
+
Gauss::Distribution.erf(0.9).should be_close(0.203091, 0.00001)
|
164
|
+
end
|
165
|
+
|
166
|
+
it "#erf should return 0.702581 at 0.27" do
|
167
|
+
Gauss::Distribution.erf(0.27).should be_close(0.702581, 0.00001)
|
168
|
+
end
|
169
|
+
|
170
|
+
it "#erf should return 1.520499 at -0.5" do
|
171
|
+
Gauss::Distribution.erf(-0.5).should be_close(1.520499, 0.00001)
|
144
172
|
end
|
145
173
|
|
146
174
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# -*- encoding : utf-8 -*-
|
2
|
-
require File.
|
2
|
+
require File.expand_path('spec/spec_helper.rb')
|
3
3
|
|
4
4
|
describe Gauss::TruncatedCorrection do
|
5
5
|
|
@@ -24,6 +24,7 @@ describe Gauss::TruncatedCorrection do
|
|
24
24
|
describe "#w_exceeds_margin" do
|
25
25
|
|
26
26
|
it "should return 0.657847 for (0.2, 0.3)" do
|
27
|
+
Gauss::TruncatedCorrection.w_exceeds_margin(0.0, 0.740466).should be_close(0.76774506, tolerance)
|
27
28
|
Gauss::TruncatedCorrection.w_exceeds_margin(0.2, 0.3).should be_close(0.657847, tolerance)
|
28
29
|
Gauss::TruncatedCorrection.w_exceeds_margin(0.1, 0.03).should be_close(0.621078, tolerance)
|
29
30
|
end
|
@@ -33,6 +34,7 @@ describe Gauss::TruncatedCorrection do
|
|
33
34
|
describe "#v_exceeds_margin" do
|
34
35
|
|
35
36
|
it "should return 0.8626174 for (0.2, 0.3)" do
|
37
|
+
Gauss::TruncatedCorrection.v_exceeds_margin(0.0, 0.740466).should be_close(1.32145197, tolerance)
|
36
38
|
Gauss::TruncatedCorrection.v_exceeds_margin(0.2, 0.3).should be_close(0.8626174, tolerance)
|
37
39
|
Gauss::TruncatedCorrection.v_exceeds_margin(0.1, 0.03).should be_close(0.753861, tolerance)
|
38
40
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# -*- encoding : utf-8 -*-
|
2
|
-
require File.
|
2
|
+
require File.expand_path('spec/spec_helper.rb')
|
3
3
|
|
4
4
|
describe Saulabs::TrueSkill::FactorGraph do
|
5
5
|
|
@@ -11,48 +11,100 @@ describe Saulabs::TrueSkill::FactorGraph do
|
|
11
11
|
|
12
12
|
describe "#update_skills" do
|
13
13
|
|
14
|
-
it "should update the mean of the first player in team1 to
|
14
|
+
it "should update the mean of the first player in team1 to 30.38345" do
|
15
15
|
@graph.update_skills
|
16
|
-
@skill.mean.should be_close(
|
16
|
+
@skill.mean.should be_close(30.38345, tolerance)
|
17
17
|
end
|
18
18
|
|
19
|
-
it "should update the deviation of the first player in team1 to 3.
|
19
|
+
it "should update the deviation of the first player in team1 to 3.46421" do
|
20
20
|
@graph.update_skills
|
21
|
-
@skill.deviation.should be_close(3.
|
21
|
+
@skill.deviation.should be_close(3.46421, tolerance)
|
22
22
|
end
|
23
23
|
|
24
24
|
end
|
25
25
|
|
26
26
|
describe "#draw_margin" do
|
27
27
|
|
28
|
-
it "should be -0.998291" do
|
29
|
-
@graph.draw_margin.should be_close(
|
28
|
+
it "should be -0.998291 for diff 0.740466" do
|
29
|
+
@graph.draw_margin.should be_close(0.740466, tolerance)
|
30
30
|
end
|
31
31
|
|
32
32
|
end
|
33
33
|
|
34
34
|
end
|
35
35
|
|
36
|
-
describe Saulabs::TrueSkill::FactorGraph do
|
36
|
+
describe Saulabs::TrueSkill::FactorGraph, "two players" do
|
37
|
+
|
38
|
+
before :each do
|
39
|
+
@teams = [
|
40
|
+
[TrueSkill::Rating.new(25.0, 25.0/3.0, 1.0, 25.0/300.0)],
|
41
|
+
[TrueSkill::Rating.new(25.0, 25.0/3.0, 1.0, 25.0/300.0)]
|
42
|
+
]
|
43
|
+
end
|
44
|
+
|
45
|
+
describe 'win with standard rating' do
|
46
|
+
|
47
|
+
before :each do
|
48
|
+
TrueSkill::FactorGraph.new(@teams, [1,2]).update_skills
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should change first players rating to [29.395832, 7.1714755]" do
|
52
|
+
@teams[0][0].should eql_rating(29.395832, 7.1714755)
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should change second players rating to [20.6041679, 7.1714755]" do
|
56
|
+
@teams[1][0].should eql_rating(20.6041679, 7.1714755)
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
describe 'draw with standard rating' do
|
62
|
+
|
63
|
+
before :each do
|
64
|
+
TrueSkill::FactorGraph.new(@teams, [1,1]).update_skills
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should change first players rating to [25.0, 6.4575196]" do
|
68
|
+
@teams[0][0].should eql_rating(25.0, 6.4575196)
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should change second players rating to [25.0, 6.4575196]" do
|
72
|
+
@teams[1][0].should eql_rating(25.0, 6.4575196)
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
describe 'draw with different ratings' do
|
78
|
+
|
79
|
+
before :each do
|
80
|
+
@teams[1][0] = TrueSkill::Rating.new(50.0, 12.5, 1.0, 25.0/300.0)
|
81
|
+
TrueSkill::FactorGraph.new(@teams, [1,1]).update_skills
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should change first players rating to [31.6623, 7.1374]" do
|
85
|
+
@teams[0][0].should eql_rating(31.662301, 7.1374459)
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should change second players mean to [35.0107, 7.9101]" do
|
89
|
+
@teams[1][0].should eql_rating(35.010653, 7.910077)
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
describe Saulabs::TrueSkill::FactorGraph, "two on two" do
|
37
97
|
|
38
98
|
before :each do
|
39
99
|
@teams = [
|
40
|
-
[
|
41
|
-
|
42
|
-
TrueSkill::Rating.new(3.0, 3.1, 0.8, 0.02),
|
43
|
-
TrueSkill::Rating.new(2.2, 3.2, 0.8, 0.02)
|
44
|
-
],
|
45
|
-
[
|
46
|
-
TrueSkill::Rating.new(25.0, 8.3333, 0.8, 0.02)
|
47
|
-
]
|
100
|
+
[TrueSkill::Rating.new(25.0, 25.0/3.0, 1.0, 25.0/300.0),TrueSkill::Rating.new(25.0, 25.0/3.0, 1.0, 25.0/300.0)],
|
101
|
+
[TrueSkill::Rating.new(25.0, 25.0/3.0, 1.0, 25.0/300.0),TrueSkill::Rating.new(25.0, 25.0/3.0, 1.0, 25.0/300.0)]
|
48
102
|
]
|
49
|
-
@skill = @teams.last.first
|
50
|
-
@graph = TrueSkill::FactorGraph.new(@teams, [1,2], :beta => 25.0, :draw_probability => 0.0)
|
51
103
|
end
|
52
104
|
|
53
|
-
|
54
|
-
|
55
|
-
|
105
|
+
describe 'win with standard rating' do
|
106
|
+
|
107
|
+
|
56
108
|
end
|
57
109
|
|
58
110
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -2,12 +2,13 @@
|
|
2
2
|
require 'rubygems'
|
3
3
|
require 'spec'
|
4
4
|
require 'spec/autorun'
|
5
|
-
require
|
5
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "..", "lib", "saulabs", "trueskill.rb"))
|
6
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "true_skill_matchers.rb"))
|
6
7
|
|
7
8
|
include Saulabs
|
8
9
|
|
9
10
|
Spec::Runner.configure do |config|
|
10
|
-
|
11
|
+
config.include(TrueSkillMatchers)
|
11
12
|
end
|
12
13
|
|
13
14
|
def tolerance
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
module TrueSkillMatchers
|
3
|
+
|
4
|
+
class EqualRating
|
5
|
+
|
6
|
+
def initialize(mean, deviation, precision)
|
7
|
+
@expected = TrueSkill::Rating.new(mean, deviation)
|
8
|
+
@precision = 10**precision
|
9
|
+
end
|
10
|
+
|
11
|
+
def matches?(target)
|
12
|
+
@target = target
|
13
|
+
@mean_diff = @expected.mean - @target.mean
|
14
|
+
@deviation_diff = @expected.deviation - @target.deviation
|
15
|
+
(@mean_diff*@precision).to_i + (@deviation_diff*@precision).to_i == 0
|
16
|
+
end
|
17
|
+
|
18
|
+
def failure_message
|
19
|
+
"expected rating #{@target.to_s} to be equal to #{@expected.to_s} #{failure_info}"
|
20
|
+
end
|
21
|
+
|
22
|
+
def negative_failure_message
|
23
|
+
"expected rating #{@target.to_s} not to be equal to #{@expected.to_s} #{failure_info}"
|
24
|
+
end
|
25
|
+
|
26
|
+
def failure_info
|
27
|
+
msg = []
|
28
|
+
msg << "mean differs by #{@mean_diff}" if @mean_diff != 0.0
|
29
|
+
msg << "deviation differs by #{@deviation_diff}" if @deviation_diff != 0.0
|
30
|
+
" (#{msg.join(', ')})"
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
def eql_rating(target_mean, target_deviation, precision = 5)
|
36
|
+
EqualRating.new(target_mean, target_deviation, precision)
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
metadata
CHANGED
@@ -3,10 +3,10 @@ name: trueskill
|
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
|
+
- 1
|
6
7
|
- 0
|
7
|
-
-
|
8
|
-
|
9
|
-
version: 0.9.2
|
8
|
+
- 0
|
9
|
+
version: 1.0.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Lars Kuhnt
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date:
|
17
|
+
date: 2011-01-19 00:00:00 +01:00
|
18
18
|
default_executable:
|
19
19
|
dependencies: []
|
20
20
|
|
@@ -65,8 +65,9 @@ files:
|
|
65
65
|
- spec/saulabs/trueskill/layers/prior_to_skills_spec.rb
|
66
66
|
- spec/saulabs/trueskill/schedules_spec.rb
|
67
67
|
- spec/spec_helper.rb
|
68
|
+
- spec/true_skill_matchers.rb
|
68
69
|
- spec/spec.opts
|
69
|
-
has_rdoc:
|
70
|
+
has_rdoc: false
|
70
71
|
homepage: http://github.com/saulabs/trueskill
|
71
72
|
licenses: []
|
72
73
|
|
@@ -110,4 +111,5 @@ test_files:
|
|
110
111
|
- spec/saulabs/trueskill/layers/prior_to_skills_spec.rb
|
111
112
|
- spec/saulabs/trueskill/schedules_spec.rb
|
112
113
|
- spec/spec_helper.rb
|
114
|
+
- spec/true_skill_matchers.rb
|
113
115
|
- spec/spec.opts
|