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.
Files changed (44) hide show
  1. data/.document +5 -0
  2. data/.gitignore +21 -0
  3. data/CHANGELOG +0 -0
  4. data/LICENSE +20 -0
  5. data/README.rdoc +17 -0
  6. data/Rakefile +46 -0
  7. data/VERSION +1 -0
  8. data/lib/saulabs/gauss.rb +3 -0
  9. data/lib/saulabs/gauss/distribution.rb +125 -0
  10. data/lib/saulabs/gauss/truncated_correction.rb +62 -0
  11. data/lib/saulabs/trueskill.rb +7 -0
  12. data/lib/saulabs/trueskill/factor_graph.rb +75 -0
  13. data/lib/saulabs/trueskill/factors/base.rb +50 -0
  14. data/lib/saulabs/trueskill/factors/greater_than.rb +45 -0
  15. data/lib/saulabs/trueskill/factors/likelihood.rb +45 -0
  16. data/lib/saulabs/trueskill/factors/prior.rb +35 -0
  17. data/lib/saulabs/trueskill/factors/weighted_sum.rb +80 -0
  18. data/lib/saulabs/trueskill/factors/within.rb +45 -0
  19. data/lib/saulabs/trueskill/layers/base.rb +32 -0
  20. data/lib/saulabs/trueskill/layers/iterated_team_performances.rb +72 -0
  21. data/lib/saulabs/trueskill/layers/performances_to_team_performances.rb +31 -0
  22. data/lib/saulabs/trueskill/layers/prior_to_skills.rb +32 -0
  23. data/lib/saulabs/trueskill/layers/skills_to_performances.rb +31 -0
  24. data/lib/saulabs/trueskill/layers/team_difference_comparision.rb +27 -0
  25. data/lib/saulabs/trueskill/layers/team_performance_differences.rb +22 -0
  26. data/lib/saulabs/trueskill/rating.rb +18 -0
  27. data/lib/saulabs/trueskill/schedules/base.rb +15 -0
  28. data/lib/saulabs/trueskill/schedules/loop.rb +26 -0
  29. data/lib/saulabs/trueskill/schedules/sequence.rb +23 -0
  30. data/lib/saulabs/trueskill/schedules/step.rb +20 -0
  31. data/spec/saulabs/gauss/distribution_spec.rb +162 -0
  32. data/spec/saulabs/gauss/truncated_correction_spec.rb +41 -0
  33. data/spec/saulabs/trueskill/factor_graph_spec.rb +29 -0
  34. data/spec/saulabs/trueskill/factors/greater_than_spec.rb +26 -0
  35. data/spec/saulabs/trueskill/factors/likelihood_spec.rb +32 -0
  36. data/spec/saulabs/trueskill/factors/prior_spec.rb +26 -0
  37. data/spec/saulabs/trueskill/factors/weighted_sum_spec.rb +67 -0
  38. data/spec/saulabs/trueskill/factors/within_spec.rb +26 -0
  39. data/spec/saulabs/trueskill/layers/prior_to_skills_spec.rb +39 -0
  40. data/spec/saulabs/trueskill/schedules_spec.rb +14 -0
  41. data/spec/spec.opts +1 -0
  42. data/spec/spec_helper.rb +29 -0
  43. data/trueskill.gemspec +99 -0
  44. 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,15 @@
1
+ module Saulabs
2
+ module TrueSkill
3
+ module Schedules
4
+
5
+ class Base
6
+
7
+ def visit(depth = -1, max_depth = 0)
8
+ raise "Abstract method Schedules::Base#visit(depth, max_depth) called"
9
+ end
10
+
11
+ end
12
+
13
+ end
14
+ end
15
+ 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