glicko2 0.1.1 → 0.1.2

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3264135c077789c8ce7d6f49a216c3ac50dafeb4
4
+ data.tar.gz: c621d811f861685e08a85c60d5252e4868231842
5
+ SHA512:
6
+ metadata.gz: 8d34d1bab836561f0fefb5f58807387af31d7d21adfd31344b547b8d804f75ba16c8ffd5a45d6982a620aca49c9e8374fb5ed93744b9050dea158536c4be0b61
7
+ data.tar.gz: 27fbeaf711881f18500a5277f2a9571d71bad500976949f73e873c0196cd29347723b382597c8d54e5961ff2751128f6591babb088a1efa60bcdb542da65468a
data/.gitignore CHANGED
@@ -2,6 +2,7 @@
2
2
  *.rbc
3
3
  .bundle
4
4
  .config
5
+ .ruby-version
5
6
  .yardoc
6
7
  Gemfile.lock
7
8
  InstalledFiles
@@ -47,5 +47,6 @@ module Glicko2
47
47
  end
48
48
 
49
49
  require "glicko2/version"
50
+ require "glicko2/normal_distribution"
50
51
  require "glicko2/player"
51
52
  require "glicko2/rating_period"
@@ -0,0 +1,58 @@
1
+ module Glicko2
2
+ # Glicko ratings are represented with a rating and rating deviation. For this
3
+ # gem it is assumed that ratings are normally distributed where rating and
4
+ # rating deviation correspond to mean and standard deviation.
5
+ class NormalDistribution
6
+ attr_reader :mean, :standard_deviation
7
+ alias_method :sd, :standard_deviation
8
+
9
+ def initialize(mean, standard_deviation)
10
+ @mean = mean
11
+ @standard_deviation = standard_deviation
12
+ end
13
+
14
+ # Calculate the distribution variance
15
+ #
16
+ # @return [Numeric]
17
+ def variance
18
+ standard_deviation ** 2.0
19
+ end
20
+
21
+ # Calculate the sum
22
+ #
23
+ # @param [NormalDistribution] other
24
+ # @return [NormalDistribution]
25
+ def +(other)
26
+ self.class.new(mean + other.mean, Math.sqrt(variance + other.variance))
27
+ end
28
+
29
+ # Calculate the difference
30
+ #
31
+ # @param [NormalDistribution] other
32
+ # @return [NormalDistribution]
33
+ def -(other)
34
+ self.class.new(mean - other.mean, Math.sqrt(variance + other.variance))
35
+ end
36
+
37
+ # Calculate the probability density at `x`
38
+ #
39
+ # @param [Numeric] x
40
+ # @return [Numeric]
41
+ def pdf(x)
42
+ 1.0 / (sd * Math.sqrt(2.0 * Math::PI)) *
43
+ Math.exp(-(x - mean) ** 2.0 / 2.0 * variance)
44
+ end
45
+
46
+ # Calculate the cumulative distribution at `x`
47
+ #
48
+ # @param [Numeric] x
49
+ # @return [Numeric]
50
+ def cdf(x)
51
+ 0.5 * (1.0 + Math.erf((x - mean) / (sd * Math.sqrt(2.0))))
52
+ end
53
+
54
+ def to_s
55
+ "#<NormalDistribution mean=#{mean}, sd=#{sd}>"
56
+ end
57
+ end
58
+ end
@@ -22,11 +22,11 @@ module Glicko2
22
22
  #
23
23
  # puts player_seed
24
24
  #
25
- class Player
25
+ class Player < NormalDistribution
26
26
  TOLERANCE = 0.0000001
27
27
  MIN_SD = DEFAULT_GLICKO_RATING_DEVIATION / GLICKO_GRADIENT
28
28
 
29
- attr_reader :mean, :sd, :volatility, :obj
29
+ attr_reader :volatility, :obj
30
30
 
31
31
  # Create a {Player} from a seed object, converting from Glicko
32
32
  # ratings to Glicko2.
@@ -43,8 +43,7 @@ module Glicko2
43
43
  # @param [Numeric] volatility player volatility
44
44
  # @param [#rating,#rating_deviation,#volatility] obj seed values object
45
45
  def initialize(mean, sd, volatility, obj=nil, config=DEFAULT_CONFIG)
46
- @mean = mean
47
- @sd = sd
46
+ super(mean, sd)
48
47
  @volatility = volatility
49
48
  @obj = obj
50
49
  @config = config
@@ -55,7 +54,7 @@ module Glicko2
55
54
  #
56
55
  # @return [Numeric]
57
56
  def g
58
- @g ||= 1 / Math.sqrt(1 + 3 * sd ** 2 / Math::PI ** 2)
57
+ @g ||= 1 / Math.sqrt(1 + 3 * variance / Math::PI ** 2)
59
58
  end
60
59
 
61
60
  # Calculate `E(mu, mu_j, phi_j)` as defined in the Glicko2 paper
@@ -71,7 +70,7 @@ module Glicko2
71
70
  #
72
71
  # @param [Array<Player>] others other participating players.
73
72
  # @return [Numeric]
74
- def variance(others)
73
+ def estimated_variance(others)
75
74
  return 0.0 if others.length < 1
76
75
  others.reduce(0) do |v, other|
77
76
  e_other = e(other)
@@ -89,14 +88,14 @@ module Glicko2
89
88
  def delta(others, scores)
90
89
  others.zip(scores).reduce(0) do |d, (other, score)|
91
90
  d + other.g * (score - e(other))
92
- end * variance(others)
91
+ end * estimated_variance(others)
93
92
  end
94
93
 
95
94
  # Calculate `f(x)` as defined in the Glicko2 paper
96
95
  #
97
96
  # @param [Numeric] x
98
97
  # @param [Numeric] d the result of calculating {#delta}
99
- # @param [Numeric] v the result of calculating {#variance}
98
+ # @param [Numeric] v the result of calculating {#estimated_variance}
100
99
  # @return [Numeric]
101
100
  def f(x, d, v)
102
101
  f_part1(x, d, v) - f_part2(x)
@@ -105,12 +104,12 @@ module Glicko2
105
104
  # Calculate the new value of the volatility
106
105
  #
107
106
  # @param [Numeric] d the result of calculating {#delta}
108
- # @param [Numeric] v the result of calculating {#variance}
107
+ # @param [Numeric] v the result of calculating {#estimated_variance}
109
108
  # @return [Numeric]
110
109
  def volatility1(d, v)
111
110
  a = Math::log(volatility ** 2)
112
- if d > sd ** 2 + v
113
- b = Math.log(d - sd ** 2 - v)
111
+ if d > variance + v
112
+ b = Math.log(d - variance - v)
114
113
  else
115
114
  k = 1
116
115
  k += 1 while f(a - k * @config[:volatility_change], d, v) < 0
@@ -162,15 +161,15 @@ module Glicko2
162
161
  private
163
162
 
164
163
  def generate_next_without_games
165
- sd_pre = [Math.sqrt(sd ** 2 + volatility ** 2), MIN_SD].min
164
+ sd_pre = [Math.sqrt(variance + volatility ** 2), MIN_SD].min
166
165
  self.class.new(mean, sd_pre, volatility, obj)
167
166
  end
168
167
 
169
168
  def generate_next_with_games(others, scores)
170
- _v = variance(others)
169
+ _v = estimated_variance(others)
171
170
  _d = delta(others, scores)
172
171
  _volatility = volatility1(_d, _v)
173
- sd_pre = Math.sqrt(sd ** 2 + _volatility ** 2)
172
+ sd_pre = [Math.sqrt(variance + volatility ** 2), MIN_SD].min
174
173
  _sd = 1 / Math.sqrt(1 / sd_pre ** 2 + 1 / _v)
175
174
  _mean = mean + _sd ** 2 * others.zip(scores).reduce(0) {
176
175
  |x, (other, score)| x + other.g * (score - e(other))
@@ -180,7 +179,7 @@ module Glicko2
180
179
 
181
180
  def f_part1(x, d, v)
182
181
  exp_x = Math.exp(x)
183
- sd_sq = sd ** 2
182
+ sd_sq = variance
184
183
  (exp_x * (d ** 2 - sd_sq - v - exp_x)) / (2 * (sd_sq + v + exp_x) ** 2)
185
184
  end
186
185
 
@@ -1,3 +1,3 @@
1
1
  module Glicko2
2
- VERSION = "0.1.1"
2
+ VERSION = "0.1.2"
3
3
  end
@@ -0,0 +1,69 @@
1
+ require 'minitest_helper'
2
+
3
+ describe Glicko2::NormalDistribution do
4
+ describe "#variance" do
5
+ it "must return the square of the standard deviation" do
6
+ Glicko2::NormalDistribution.new(1.0, 1.0).variance.must_equal 1.0 ** 2.0
7
+ Glicko2::NormalDistribution.new(1.0, 2.0).variance.must_equal 2.0 ** 2.0
8
+ Glicko2::NormalDistribution.new(1.0, 10.0).variance.must_equal 10.0 ** 2.0
9
+ end
10
+ end
11
+
12
+ describe "#+" do
13
+ let(:dist1) { Glicko2::NormalDistribution.new(10.0, 0.5) }
14
+ let(:dist2) { Glicko2::NormalDistribution.new(5.0, 1.0) }
15
+
16
+ it "must sum the means" do
17
+ (dist1 + dist2).mean.must_equal 15.0
18
+ end
19
+
20
+ it "must sqrt the sum of the variances" do
21
+ (dist1 + dist2).sd.must_equal Math.sqrt(0.5 ** 2.0 + 1.0 ** 2.0)
22
+ end
23
+ end
24
+
25
+ describe "#-" do
26
+ let(:dist1) { Glicko2::NormalDistribution.new(10.0, 0.5) }
27
+ let(:dist2) { Glicko2::NormalDistribution.new(5.0, 1.0) }
28
+
29
+ it "must subtract the means" do
30
+ (dist1 - dist2).mean.must_equal 5.0
31
+ end
32
+
33
+ it "must sqrt the sum of the variances" do
34
+ (dist1 - dist2).sd.must_equal Math.sqrt(0.5 ** 2.0 + 1.0 ** 2.0)
35
+ end
36
+ end
37
+
38
+ describe "#pdf" do
39
+ describe "standard normal" do
40
+ let(:dist) { Glicko2::NormalDistribution.new(0.0, 1.0) }
41
+
42
+ it "must calculate PDF at x" do
43
+ dist.pdf(-5.0).must_be_close_to 0.00000149, 0.00000001
44
+ dist.pdf(-2.5).must_be_close_to 0.01752830, 0.00000001
45
+ dist.pdf(-1.0).must_be_close_to 0.24197072, 0.00000001
46
+ dist.pdf(0.0).must_be_close_to 0.39894228, 0.00000001
47
+ dist.pdf(1.0).must_be_close_to 0.24197072, 0.00000001
48
+ dist.pdf(2.5).must_be_close_to 0.01752830, 0.00000001
49
+ dist.pdf(5.0).must_be_close_to 0.00000149, 0.00000001
50
+ end
51
+ end
52
+ end
53
+
54
+ describe "#cdf" do
55
+ describe "standard normal" do
56
+ let(:dist) { Glicko2::NormalDistribution.new(0.0, 1.0) }
57
+
58
+ it "must calculate CDF at x" do
59
+ dist.cdf(-5.0).must_be_close_to 0.00000029, 0.00000001
60
+ dist.cdf(-2.5).must_be_close_to 0.00620967, 0.00000001
61
+ dist.cdf(-1.0).must_be_close_to 0.15865525, 0.00000001
62
+ dist.cdf(0.0).must_be_close_to 0.50000000, 0.00000001
63
+ dist.cdf(1.0).must_be_close_to 0.84134475, 0.00000001
64
+ dist.cdf(2.5).must_be_close_to 0.99379033, 0.00000001
65
+ dist.cdf(5.0).must_be_close_to 0.99999971, 0.00000001
66
+ end
67
+ end
68
+ end
69
+ end
@@ -66,9 +66,9 @@ describe Glicko2::Player do
66
66
  end
67
67
  end
68
68
 
69
- describe "#variance" do
69
+ describe "#estimated_variance" do
70
70
  it "must be close to example" do
71
- @player.variance(@others).must_be_close_to 1.7785
71
+ @player.estimated_variance(@others).must_be_close_to 1.7785
72
72
  end
73
73
  end
74
74
 
metadata CHANGED
@@ -1,30 +1,27 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: glicko2
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
5
- prerelease:
4
+ version: 0.1.2
6
5
  platform: ruby
7
6
  authors:
8
7
  - James Fargher
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-01-18 00:00:00.000000000 Z
11
+ date: 2013-03-04 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: minitest
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
- - - ! '>='
17
+ - - '>='
20
18
  - !ruby/object:Gem::Version
21
19
  version: '0'
22
20
  type: :development
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
- - - ! '>='
24
+ - - '>='
28
25
  - !ruby/object:Gem::Version
29
26
  version: '0'
30
27
  description: Implementation of Glicko2 ratings
@@ -41,39 +38,42 @@ files:
41
38
  - Rakefile
42
39
  - glicko2.gemspec
43
40
  - lib/glicko2.rb
41
+ - lib/glicko2/normal_distribution.rb
44
42
  - lib/glicko2/player.rb
45
43
  - lib/glicko2/rating_period.rb
46
44
  - lib/glicko2/version.rb
47
45
  - spec/minitest_helper.rb
46
+ - spec/normal_distribution_spec.rb
48
47
  - spec/player_spec.rb
49
48
  - spec/rating_period_spec.rb
50
49
  - spec/util_spec.rb
51
50
  homepage: https://github.com/proglottis/glicko2
52
51
  licenses: []
52
+ metadata: {}
53
53
  post_install_message:
54
54
  rdoc_options: []
55
55
  require_paths:
56
56
  - lib
57
57
  required_ruby_version: !ruby/object:Gem::Requirement
58
- none: false
59
58
  requirements:
60
- - - ! '>='
59
+ - - '>='
61
60
  - !ruby/object:Gem::Version
62
61
  version: '0'
63
62
  required_rubygems_version: !ruby/object:Gem::Requirement
64
- none: false
65
63
  requirements:
66
- - - ! '>='
64
+ - - '>='
67
65
  - !ruby/object:Gem::Version
68
66
  version: '0'
69
67
  requirements: []
70
68
  rubyforge_project:
71
- rubygems_version: 1.8.23
69
+ rubygems_version: 2.0.0
72
70
  signing_key:
73
- specification_version: 3
71
+ specification_version: 4
74
72
  summary: Implementation of Glicko2 ratings
75
73
  test_files:
76
74
  - spec/minitest_helper.rb
75
+ - spec/normal_distribution_spec.rb
77
76
  - spec/player_spec.rb
78
77
  - spec/rating_period_spec.rb
79
78
  - spec/util_spec.rb
79
+ has_rdoc: