trueskill 0.1.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/.document +5 -0
- data/.gitignore +21 -0
- data/CHANGELOG +0 -0
- data/LICENSE +20 -0
- data/README.rdoc +17 -0
- data/Rakefile +46 -0
- data/VERSION +1 -0
- data/lib/saulabs/gauss.rb +3 -0
- data/lib/saulabs/gauss/distribution.rb +125 -0
- data/lib/saulabs/gauss/truncated_correction.rb +62 -0
- data/lib/saulabs/trueskill.rb +7 -0
- data/lib/saulabs/trueskill/factor_graph.rb +75 -0
- data/lib/saulabs/trueskill/factors/base.rb +50 -0
- data/lib/saulabs/trueskill/factors/greater_than.rb +45 -0
- data/lib/saulabs/trueskill/factors/likelihood.rb +45 -0
- data/lib/saulabs/trueskill/factors/prior.rb +35 -0
- data/lib/saulabs/trueskill/factors/weighted_sum.rb +80 -0
- data/lib/saulabs/trueskill/factors/within.rb +45 -0
- data/lib/saulabs/trueskill/layers/base.rb +32 -0
- data/lib/saulabs/trueskill/layers/iterated_team_performances.rb +72 -0
- data/lib/saulabs/trueskill/layers/performances_to_team_performances.rb +31 -0
- data/lib/saulabs/trueskill/layers/prior_to_skills.rb +32 -0
- data/lib/saulabs/trueskill/layers/skills_to_performances.rb +31 -0
- data/lib/saulabs/trueskill/layers/team_difference_comparision.rb +27 -0
- data/lib/saulabs/trueskill/layers/team_performance_differences.rb +22 -0
- data/lib/saulabs/trueskill/rating.rb +18 -0
- data/lib/saulabs/trueskill/schedules/base.rb +15 -0
- data/lib/saulabs/trueskill/schedules/loop.rb +26 -0
- data/lib/saulabs/trueskill/schedules/sequence.rb +23 -0
- data/lib/saulabs/trueskill/schedules/step.rb +20 -0
- data/spec/saulabs/gauss/distribution_spec.rb +162 -0
- data/spec/saulabs/gauss/truncated_correction_spec.rb +41 -0
- data/spec/saulabs/trueskill/factor_graph_spec.rb +29 -0
- data/spec/saulabs/trueskill/factors/greater_than_spec.rb +26 -0
- data/spec/saulabs/trueskill/factors/likelihood_spec.rb +32 -0
- data/spec/saulabs/trueskill/factors/prior_spec.rb +26 -0
- data/spec/saulabs/trueskill/factors/weighted_sum_spec.rb +67 -0
- data/spec/saulabs/trueskill/factors/within_spec.rb +26 -0
- data/spec/saulabs/trueskill/layers/prior_to_skills_spec.rb +39 -0
- data/spec/saulabs/trueskill/schedules_spec.rb +14 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +29 -0
- data/trueskill.gemspec +99 -0
- metadata +143 -0
@@ -0,0 +1,27 @@
|
|
1
|
+
module Saulabs
|
2
|
+
module TrueSkill
|
3
|
+
module Layers
|
4
|
+
|
5
|
+
class TeamDifferenceComparision < Base
|
6
|
+
|
7
|
+
def initialize(graph, ranks)
|
8
|
+
super(graph)
|
9
|
+
@ranks = ranks
|
10
|
+
@epsilon = graph.draw_margin
|
11
|
+
end
|
12
|
+
|
13
|
+
def build
|
14
|
+
(0..@input.size-1).each do |i|
|
15
|
+
if @ranks[i] == @ranks[i+1]
|
16
|
+
@factors << Factors::Within.new(@epsilon, @input[i][0])
|
17
|
+
else
|
18
|
+
@factors << Factors::GreaterThan.new(@epsilon, @input[i][0])
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Saulabs
|
2
|
+
module TrueSkill
|
3
|
+
module Layers
|
4
|
+
|
5
|
+
class TeamPerformanceDifferences < Base
|
6
|
+
|
7
|
+
def initialize(graph)
|
8
|
+
super(graph)
|
9
|
+
end
|
10
|
+
|
11
|
+
def build
|
12
|
+
(0..@input.size-2).each do |i|
|
13
|
+
variable = Gauss::Distribution.new
|
14
|
+
@factors << Factors::WeightedSum.new(variable, [@input[i][0], @input[i+1][0]], [1.0, -1.0])
|
15
|
+
@output << [variable]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Saulabs
|
2
|
+
module TrueSkill
|
3
|
+
|
4
|
+
class Rating < Gauss::Distribution
|
5
|
+
|
6
|
+
attr_reader :activity, :tau, :tau_squared
|
7
|
+
|
8
|
+
def initialize(mean, deviation, activity = 1.0, tau = 25/300.0)
|
9
|
+
super(mean, deviation)
|
10
|
+
@activity = activity
|
11
|
+
@tau = tau
|
12
|
+
@tau_squared = @tau**2
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Saulabs
|
2
|
+
module TrueSkill
|
3
|
+
module Schedules
|
4
|
+
|
5
|
+
class Loop < Base
|
6
|
+
|
7
|
+
def initialize(schedule, max_delta)
|
8
|
+
@schedule = schedule
|
9
|
+
@max_delta = max_delta
|
10
|
+
end
|
11
|
+
|
12
|
+
def visit(depth = -1, max_depth = 0)
|
13
|
+
iterations = 1
|
14
|
+
delta = @schedule.visit(depth + 1, max_depth)
|
15
|
+
while delta > @max_delta
|
16
|
+
delta = @schedule.visit(depth + 1, max_depth)
|
17
|
+
iterations += 1
|
18
|
+
end
|
19
|
+
delta
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Saulabs
|
2
|
+
module TrueSkill
|
3
|
+
module Schedules
|
4
|
+
|
5
|
+
class Sequence < Base
|
6
|
+
|
7
|
+
def initialize(schedules)
|
8
|
+
@schedules = schedules
|
9
|
+
end
|
10
|
+
|
11
|
+
def visit(depth = -1, max_depth = 0)
|
12
|
+
max_delta = 0
|
13
|
+
@schedules.each do |schedule|
|
14
|
+
max_delta = [schedule.visit(depth + 1, max_depth), max_delta].max
|
15
|
+
end
|
16
|
+
max_delta
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Saulabs
|
2
|
+
module TrueSkill
|
3
|
+
module Schedules
|
4
|
+
|
5
|
+
class Step < Base
|
6
|
+
|
7
|
+
def initialize(factor, index)
|
8
|
+
@factor = factor
|
9
|
+
@index = index
|
10
|
+
end
|
11
|
+
|
12
|
+
def visit(depth = -1, max_depth = 0)
|
13
|
+
@factor.update_message_at(@index)
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,162 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../spec_helper'
|
2
|
+
|
3
|
+
describe Gauss::Distribution, "#initialize" do
|
4
|
+
|
5
|
+
it "should set the mean to 10.1" do
|
6
|
+
Gauss::Distribution.new(10.1, 0.4).mean.should == 10.1
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should set the deviation to 0.4" do
|
10
|
+
Gauss::Distribution.new(10.1, 0.4).deviation.should == 0.4
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should set the mean to 0.0 if the given mean is not finite" do
|
14
|
+
Gauss::Distribution.new(1 / 0.0, 0.4).mean.should == 0.0
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should set the deviation to 0.0 if the given deviation is not finite" do
|
18
|
+
Gauss::Distribution.new(10.1, 1 / 0.0).deviation.should == 0.0
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
describe Gauss::Distribution, "#with_deviation" do
|
24
|
+
|
25
|
+
before :each do
|
26
|
+
@dist = Gauss::Distribution.with_deviation(25.0, 8.333333)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should have a default mean value of 25.0" do
|
30
|
+
@dist.mean.should == 25.0
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should have a default deviation of 8.333333" do
|
34
|
+
@dist.deviation.should be_close(8.333333, 0.000001)
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should set the variance to 69.444438" do
|
38
|
+
@dist.variance.should be_close(69.4444, 0.0001)
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should set the precision to 0.0144" do
|
42
|
+
@dist.precision.should be_close(0.0144, 0.0001)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should set the precision_mean to 0.36" do
|
46
|
+
@dist.precision_mean.should be_close(0.36, 0.0001)
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
describe Gauss::Distribution, "#with_precision" do
|
52
|
+
|
53
|
+
before :each do
|
54
|
+
@dist = Gauss::Distribution.with_precision(0.36, 0.0144)
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should have a default mean value of 25.0" do
|
58
|
+
@dist.mean.should == 25.0
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should have a default deviation of 8.333333" do
|
62
|
+
@dist.deviation.should be_close(8.333333, 0.000001)
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should set the variance to 69.444438" do
|
66
|
+
@dist.variance.should be_close(69.4444, 0.0001)
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should set the precision to 0.0144" do
|
70
|
+
@dist.precision.should be_close(0.0144, 0.0001)
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should set the precision_mean to 0.36" do
|
74
|
+
@dist.precision_mean.should be_close(0.36, 0.0001)
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
describe Gauss::Distribution, "absolute difference (-)" do
|
80
|
+
|
81
|
+
before :each do
|
82
|
+
@dist = Gauss::Distribution.with_deviation(25.0, 8.333333)
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should be 0.0 for the same distribution" do
|
86
|
+
(@dist - @dist).should == 0.0
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should equal the precision mean if the 0-distribution is subtracted" do
|
90
|
+
(@dist - Gauss::Distribution.new).should == @dist.precision_mean
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should be 130.399408 for (22, 0.4) - (12, 1.3)" do
|
94
|
+
(Gauss::Distribution.new(22, 0.4) - Gauss::Distribution.new(12, 1.3)).should be_close(130.399408, tolerance)
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
describe Gauss::Distribution, "#value_at" do
|
100
|
+
|
101
|
+
it "should have a value of 0.073654 for x = 2" do
|
102
|
+
Gauss::Distribution.new(4,5).value_at(2).should be_close(0.073654, tolerance)
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
|
107
|
+
describe Gauss::Distribution, "multiplication (*)" do
|
108
|
+
|
109
|
+
it "should have a mean of 0.2" do
|
110
|
+
(Gauss::Distribution.new(0,1) * Gauss::Distribution.new(2,3)).mean.should be_close(0.2, 0.00001)
|
111
|
+
end
|
112
|
+
|
113
|
+
it "should have a deviation of 3.0 / Math.sqrt(10)" do
|
114
|
+
(Gauss::Distribution.new(0,1) * Gauss::Distribution.new(2,3)).deviation.should be_close(3.0 / Math.sqrt(10), 0.00001)
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
|
119
|
+
describe Gauss::Distribution, "#log_product_normalization" do
|
120
|
+
|
121
|
+
it "should have calculate -3.0979981" do
|
122
|
+
lp = Gauss::Distribution.log_product_normalization(Gauss::Distribution.new(4,5), Gauss::Distribution.new(6,7))
|
123
|
+
lp.should be_close(-3.0979981, 0.000001)
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
127
|
+
|
128
|
+
describe Gauss::Distribution, "functions" do
|
129
|
+
|
130
|
+
describe 'value = 0.27' do
|
131
|
+
|
132
|
+
it "#cumulative_distribution_function should return 0.6064198" do
|
133
|
+
Gauss::Distribution.cumulative_distribution_function(0.27).should be_close(0.6064198, 0.00001)
|
134
|
+
Gauss::Distribution.cdf(2.0).should be_close(0.9772498, 0.00001)
|
135
|
+
end
|
136
|
+
|
137
|
+
it "#probability_density_function should return 0.384662" do
|
138
|
+
Gauss::Distribution.probability_density_function(0.27).should be_close(0.384662, 0.0001)
|
139
|
+
end
|
140
|
+
|
141
|
+
it "#quantile_function should return -0.62941" do
|
142
|
+
Gauss::Distribution.quantile_function(0.27).should be_close(-0.62941, 0.00001)
|
143
|
+
Gauss::Distribution.quantile_function(0.9).should be_close(1.281551, tolerance)
|
144
|
+
end
|
145
|
+
|
146
|
+
end
|
147
|
+
|
148
|
+
end
|
149
|
+
|
150
|
+
describe Gauss::Distribution, "#replace" do
|
151
|
+
|
152
|
+
before :each do
|
153
|
+
@dist1 = Gauss::Distribution.with_deviation(25.0, 8.333333)
|
154
|
+
@dist2 = Gauss::Distribution.with_deviation(9.0, 4)
|
155
|
+
end
|
156
|
+
|
157
|
+
it "should be equal to the replaced distribution" do
|
158
|
+
@dist1.replace(@dist2)
|
159
|
+
@dist1.should == @dist2
|
160
|
+
end
|
161
|
+
|
162
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../spec_helper'
|
2
|
+
|
3
|
+
describe Gauss::TruncatedCorrection do
|
4
|
+
|
5
|
+
describe "#w_within_margin" do
|
6
|
+
|
7
|
+
it "should return 0.970397 for (0.2, 0.3)" do
|
8
|
+
Gauss::TruncatedCorrection.w_within_margin(0.2, 0.3).should be_close(0.970397, tolerance)
|
9
|
+
Gauss::TruncatedCorrection.w_within_margin(0.1, 0.03).should be_close(0.9997, tolerance)
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "#v_within_margin" do
|
15
|
+
|
16
|
+
it "should return -0.194073 for (0.2, 0.3)" do
|
17
|
+
Gauss::TruncatedCorrection.v_within_margin(0.2, 0.3).should be_close(-0.194073, tolerance)
|
18
|
+
Gauss::TruncatedCorrection.v_within_margin(0.1, 0.03).should be_close(-0.09997, tolerance)
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "#w_exceeds_margin" do
|
24
|
+
|
25
|
+
it "should return 0.657847 for (0.2, 0.3)" do
|
26
|
+
Gauss::TruncatedCorrection.w_exceeds_margin(0.2, 0.3).should be_close(0.657847, tolerance)
|
27
|
+
Gauss::TruncatedCorrection.w_exceeds_margin(0.1, 0.03).should be_close(0.621078, tolerance)
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "#v_exceeds_margin" do
|
33
|
+
|
34
|
+
it "should return 0.8626174 for (0.2, 0.3)" do
|
35
|
+
Gauss::TruncatedCorrection.v_exceeds_margin(0.2, 0.3).should be_close(0.8626174, tolerance)
|
36
|
+
Gauss::TruncatedCorrection.v_exceeds_margin(0.1, 0.03).should be_close(0.753861, tolerance)
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../spec_helper'
|
2
|
+
|
3
|
+
describe Saulabs::TrueSkill::FactorGraph do
|
4
|
+
|
5
|
+
before :each do
|
6
|
+
@teams = create_teams
|
7
|
+
@graph = TrueSkill::FactorGraph.new(@teams, [1,2,3])
|
8
|
+
end
|
9
|
+
|
10
|
+
describe "#evaluate" do
|
11
|
+
|
12
|
+
it "should do something" do
|
13
|
+
result = @graph.evaluate
|
14
|
+
puts "[#{result.last.flatten.map(&:to_s).join(', ')}]<br>"
|
15
|
+
result.last[0][0].mean.should be_close(29.61452, tolerance)
|
16
|
+
result.last[0][0].deviation.should be_close(3.5036, tolerance)
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "#draw_margin" do
|
22
|
+
|
23
|
+
it "should be " do
|
24
|
+
@graph.draw_margin.should be_close(-0.998291, tolerance)
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../../spec_helper'
|
2
|
+
|
3
|
+
describe TrueSkill::Factors::GreaterThan do
|
4
|
+
|
5
|
+
before :each do
|
6
|
+
@variable = Gauss::Distribution.new(0.1, 1.1)
|
7
|
+
@factor = TrueSkill::Factors::GreaterThan.new(0.1, @variable)
|
8
|
+
end
|
9
|
+
|
10
|
+
describe "#update_message_at" do
|
11
|
+
|
12
|
+
it "should return a difference of 2.1409" do
|
13
|
+
@factor.update_message_at(0).should be_close(2.1409, tolerance)
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "#log_normalization" do
|
19
|
+
|
20
|
+
it "should be -0.69314" do
|
21
|
+
@factor.log_normalization.should be_close(-0.69314, tolerance)
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../../spec_helper'
|
2
|
+
|
3
|
+
describe TrueSkill::Factors::Likelihood do
|
4
|
+
|
5
|
+
before :each do
|
6
|
+
@variable1 = Gauss::Distribution.new(26, 1.1)
|
7
|
+
@variable2 = Gauss::Distribution.new
|
8
|
+
@factor = TrueSkill::Factors::Likelihood.new(30, @variable1, @variable2)
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "#update_message_at" do
|
12
|
+
|
13
|
+
it "should return a difference of 0.0" do
|
14
|
+
@factor.update_message_at(0).should be_close(0.0, tolerance)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should return a difference of 0.833066 for the second message" do
|
18
|
+
@factor.update_message_at(0)
|
19
|
+
@factor.update_message_at(1).should be_close(0.833066, tolerance)
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "#log_normalization" do
|
25
|
+
|
26
|
+
it "should be 0.0" do
|
27
|
+
@factor.log_normalization.should == 0.0
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|