elo-ratings 0.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.
Files changed (91) hide show
  1. data/.document +5 -0
  2. data/Gemfile +14 -0
  3. data/Gemfile.lock +22 -0
  4. data/LICENSE.txt +20 -0
  5. data/README.rdoc +23 -0
  6. data/Rakefile +54 -0
  7. data/VERSION +1 -0
  8. data/elo-ratings.gemspec +148 -0
  9. data/ext/bayeselo/.gitignore +4 -0
  10. data/ext/bayeselo/CBradleyTerry.cpp +670 -0
  11. data/ext/bayeselo/CBradleyTerry.h +137 -0
  12. data/ext/bayeselo/CCDistribution.cpp +135 -0
  13. data/ext/bayeselo/CCDistribution.h +44 -0
  14. data/ext/bayeselo/CCDistributionCUI.cpp +104 -0
  15. data/ext/bayeselo/CCDistributionCUI.h +38 -0
  16. data/ext/bayeselo/CCondensedResults.cpp +253 -0
  17. data/ext/bayeselo/CCondensedResults.h +64 -0
  18. data/ext/bayeselo/CDiscretization.h +40 -0
  19. data/ext/bayeselo/CDistribution.cpp +123 -0
  20. data/ext/bayeselo/CDistribution.h +58 -0
  21. data/ext/bayeselo/CDistributionCollection.cpp +34 -0
  22. data/ext/bayeselo/CDistributionCollection.h +29 -0
  23. data/ext/bayeselo/CEloRatingCUI.cpp +1046 -0
  24. data/ext/bayeselo/CEloRatingCUI.h +67 -0
  25. data/ext/bayeselo/CIndirectCompare.h +28 -0
  26. data/ext/bayeselo/CJointBayesian.cpp +111 -0
  27. data/ext/bayeselo/CJointBayesian.h +38 -0
  28. data/ext/bayeselo/CLUDecomposition.cpp +154 -0
  29. data/ext/bayeselo/CLUDecomposition.h +36 -0
  30. data/ext/bayeselo/CMatrix.cpp +48 -0
  31. data/ext/bayeselo/CMatrix.h +35 -0
  32. data/ext/bayeselo/CMatrixIO.cpp +27 -0
  33. data/ext/bayeselo/CMatrixIO.h +17 -0
  34. data/ext/bayeselo/CPredictionCUI.cpp +393 -0
  35. data/ext/bayeselo/CPredictionCUI.h +50 -0
  36. data/ext/bayeselo/CResultSet.cpp +245 -0
  37. data/ext/bayeselo/CResultSet.h +50 -0
  38. data/ext/bayeselo/CResultSetCUI.cpp +355 -0
  39. data/ext/bayeselo/CResultSetCUI.h +44 -0
  40. data/ext/bayeselo/CTimeIO.cpp +58 -0
  41. data/ext/bayeselo/CTimeIO.h +19 -0
  42. data/ext/bayeselo/CVector.cpp +110 -0
  43. data/ext/bayeselo/CVector.h +42 -0
  44. data/ext/bayeselo/EloDataFromFile.cpp +120 -0
  45. data/ext/bayeselo/EloDataFromFile.h +21 -0
  46. data/ext/bayeselo/README +8 -0
  47. data/ext/bayeselo/ReadLineToString.cpp +32 -0
  48. data/ext/bayeselo/ReadLineToString.h +18 -0
  49. data/ext/bayeselo/chtime.cpp +56 -0
  50. data/ext/bayeselo/chtime.h +61 -0
  51. data/ext/bayeselo/chtimer.h +27 -0
  52. data/ext/bayeselo/clktimer.cpp +178 -0
  53. data/ext/bayeselo/clktimer.h +29 -0
  54. data/ext/bayeselo/consolui.cpp +538 -0
  55. data/ext/bayeselo/consolui.h +154 -0
  56. data/ext/bayeselo/const.cpp +79 -0
  57. data/ext/bayeselo/const.h +42 -0
  58. data/ext/bayeselo/date.cpp +96 -0
  59. data/ext/bayeselo/date.h +44 -0
  60. data/ext/bayeselo/debug.h +58 -0
  61. data/ext/bayeselo/elomain.cpp +26 -0
  62. data/ext/bayeselo/extconf.rb +6 -0
  63. data/ext/bayeselo/list.h +367 -0
  64. data/ext/bayeselo/listi.h +125 -0
  65. data/ext/bayeselo/move.cpp +249 -0
  66. data/ext/bayeselo/move.h +139 -0
  67. data/ext/bayeselo/pgn.h +62 -0
  68. data/ext/bayeselo/pgnlex.cpp +432 -0
  69. data/ext/bayeselo/pgnlex.h +105 -0
  70. data/ext/bayeselo/pgnstr.cpp +126 -0
  71. data/ext/bayeselo/piece.h +44 -0
  72. data/ext/bayeselo/player.h +31 -0
  73. data/ext/bayeselo/position.h +89 -0
  74. data/ext/bayeselo/random.cpp +114 -0
  75. data/ext/bayeselo/random.h +63 -0
  76. data/ext/bayeselo/rb_bayeselo.cpp +76 -0
  77. data/ext/bayeselo/readstr.cpp +51 -0
  78. data/ext/bayeselo/readstr.h +19 -0
  79. data/ext/bayeselo/square.h +61 -0
  80. data/ext/bayeselo/str.cpp +81 -0
  81. data/ext/bayeselo/str.h +71 -0
  82. data/ext/bayeselo/version.cpp +30 -0
  83. data/ext/bayeselo/version.h +28 -0
  84. data/ext/bayeselo/version_number.h +1 -0
  85. data/lib/bayeselo.rb +6 -0
  86. data/lib/bayeselo/bayeselo.rb +11 -0
  87. data/lib/bayeselo/c_bayeselo.rb +7 -0
  88. data/test/bayeselo/test_bayeselo.rb +33 -0
  89. data/test/bayeselo/test_c_bayeselo.rb +84 -0
  90. data/test/helper.rb +18 -0
  91. metadata +226 -0
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ # gem "activesupport", ">= 2.3.5"
5
+
6
+ # Add dependencies to develop your gem here.
7
+ # Include everything needed to run rake, tests, features, etc.
8
+ group :development do
9
+ gem "shoulda", ">= 0"
10
+ gem "bundler", "~> 1.0.0"
11
+ gem "jeweler", "~> 1.5.2"
12
+ gem "rcov", ">= 0"
13
+ gem "rice"
14
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,22 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ git (1.2.5)
5
+ jeweler (1.5.2)
6
+ bundler (~> 1.0.0)
7
+ git (>= 1.2.5)
8
+ rake
9
+ rake (0.8.7)
10
+ rcov (0.9.9)
11
+ rice (1.4.0)
12
+ shoulda (2.11.3)
13
+
14
+ PLATFORMS
15
+ ruby
16
+
17
+ DEPENDENCIES
18
+ bundler (~> 1.0.0)
19
+ jeweler (~> 1.5.2)
20
+ rcov
21
+ rice
22
+ shoulda
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 phillc
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,23 @@
1
+ = EloRatings
2
+
3
+ This is a ruby interface to the c++ classes of Remi Coulom's Bayeselo, a tool for estimating Elo ratings.
4
+
5
+ = Bayeselo
6
+
7
+ For more information or information on Bayeselo, see http://remi.coulom.free.fr/Bayesian-Elo/
8
+ You can reach the website of Remi Coulom, the author of Bayeselo, at http://remi.coulom.free.fr
9
+
10
+ Bayeselo is protected under the terms of the GNU GPL. See http://www.gnu.org/copyleft/gpl.html
11
+ It is bundled under the ext directory of this project
12
+
13
+ = EloRatings
14
+
15
+ The goal of this gem was to provide a simple interface to Bayeselo in ruby.
16
+ There has currently been no attempt to reproduce all functionality provided by Bayeselo.
17
+
18
+
19
+ == Copyright
20
+
21
+ Copyright (c) 2010 phillc. See LICENSE.txt for
22
+ further details.
23
+
data/Rakefile ADDED
@@ -0,0 +1,54 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'rake'
11
+
12
+ require 'jeweler'
13
+ Jeweler::Tasks.new do |gem|
14
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
15
+ gem.name = "elo-ratings"
16
+ gem.homepage = "http://github.com/phillc/elo-ratings"
17
+ gem.license = "MIT"
18
+ gem.summary = "Ruby wrapper for bayeselo"
19
+ gem.description = "Ruby wrapper for bayeselo"
20
+ gem.email = "spyyderz@gmail.com"
21
+ gem.authors = ["phillc"]
22
+ gem.extensions << "ext/bayeselo/extconf.rb"
23
+ # Include your dependencies below. Runtime dependencies are required when using your gem,
24
+ # and development dependencies are only needed for development (ie running rake tasks, tests, etc)
25
+ # gem.add_runtime_dependency 'jabber4r', '> 0.1'
26
+ # gem.add_development_dependency 'rspec', '> 1.2.3'
27
+ end
28
+ Jeweler::RubygemsDotOrgTasks.new
29
+
30
+ require 'rake/testtask'
31
+ Rake::TestTask.new(:test) do |test|
32
+ test.libs << 'lib' << 'test'
33
+ test.pattern = 'test/**/test_*.rb'
34
+ test.verbose = true
35
+ end
36
+
37
+ require 'rcov/rcovtask'
38
+ Rcov::RcovTask.new do |test|
39
+ test.libs << 'test'
40
+ test.pattern = 'test/**/test_*.rb'
41
+ test.verbose = true
42
+ end
43
+
44
+ task :default => :test
45
+
46
+ require 'rake/rdoctask'
47
+ Rake::RDocTask.new do |rdoc|
48
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
49
+
50
+ rdoc.rdoc_dir = 'rdoc'
51
+ rdoc.title = "elo ratings #{version}"
52
+ rdoc.rdoc_files.include('README*')
53
+ rdoc.rdoc_files.include('lib/**/*.rb')
54
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.0
@@ -0,0 +1,148 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{elo-ratings}
8
+ s.version = "0.0.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["phillc"]
12
+ s.date = %q{2011-01-19}
13
+ s.description = %q{Ruby wrapper for bayeselo}
14
+ s.email = %q{spyyderz@gmail.com}
15
+ s.extensions = ["ext/bayeselo/extconf.rb", "ext/bayeselo/extconf.rb"]
16
+ s.extra_rdoc_files = [
17
+ "LICENSE.txt",
18
+ "README.rdoc"
19
+ ]
20
+ s.files = [
21
+ ".document",
22
+ "Gemfile",
23
+ "Gemfile.lock",
24
+ "LICENSE.txt",
25
+ "README.rdoc",
26
+ "Rakefile",
27
+ "VERSION",
28
+ "elo-ratings.gemspec",
29
+ "ext/bayeselo/.gitignore",
30
+ "ext/bayeselo/CBradleyTerry.cpp",
31
+ "ext/bayeselo/CBradleyTerry.h",
32
+ "ext/bayeselo/CCDistribution.cpp",
33
+ "ext/bayeselo/CCDistribution.h",
34
+ "ext/bayeselo/CCDistributionCUI.cpp",
35
+ "ext/bayeselo/CCDistributionCUI.h",
36
+ "ext/bayeselo/CCondensedResults.cpp",
37
+ "ext/bayeselo/CCondensedResults.h",
38
+ "ext/bayeselo/CDiscretization.h",
39
+ "ext/bayeselo/CDistribution.cpp",
40
+ "ext/bayeselo/CDistribution.h",
41
+ "ext/bayeselo/CDistributionCollection.cpp",
42
+ "ext/bayeselo/CDistributionCollection.h",
43
+ "ext/bayeselo/CEloRatingCUI.cpp",
44
+ "ext/bayeselo/CEloRatingCUI.h",
45
+ "ext/bayeselo/CIndirectCompare.h",
46
+ "ext/bayeselo/CJointBayesian.cpp",
47
+ "ext/bayeselo/CJointBayesian.h",
48
+ "ext/bayeselo/CLUDecomposition.cpp",
49
+ "ext/bayeselo/CLUDecomposition.h",
50
+ "ext/bayeselo/CMatrix.cpp",
51
+ "ext/bayeselo/CMatrix.h",
52
+ "ext/bayeselo/CMatrixIO.cpp",
53
+ "ext/bayeselo/CMatrixIO.h",
54
+ "ext/bayeselo/CPredictionCUI.cpp",
55
+ "ext/bayeselo/CPredictionCUI.h",
56
+ "ext/bayeselo/CResultSet.cpp",
57
+ "ext/bayeselo/CResultSet.h",
58
+ "ext/bayeselo/CResultSetCUI.cpp",
59
+ "ext/bayeselo/CResultSetCUI.h",
60
+ "ext/bayeselo/CTimeIO.cpp",
61
+ "ext/bayeselo/CTimeIO.h",
62
+ "ext/bayeselo/CVector.cpp",
63
+ "ext/bayeselo/CVector.h",
64
+ "ext/bayeselo/EloDataFromFile.cpp",
65
+ "ext/bayeselo/EloDataFromFile.h",
66
+ "ext/bayeselo/README",
67
+ "ext/bayeselo/ReadLineToString.cpp",
68
+ "ext/bayeselo/ReadLineToString.h",
69
+ "ext/bayeselo/chtime.cpp",
70
+ "ext/bayeselo/chtime.h",
71
+ "ext/bayeselo/chtimer.h",
72
+ "ext/bayeselo/clktimer.cpp",
73
+ "ext/bayeselo/clktimer.h",
74
+ "ext/bayeselo/consolui.cpp",
75
+ "ext/bayeselo/consolui.h",
76
+ "ext/bayeselo/const.cpp",
77
+ "ext/bayeselo/const.h",
78
+ "ext/bayeselo/date.cpp",
79
+ "ext/bayeselo/date.h",
80
+ "ext/bayeselo/debug.h",
81
+ "ext/bayeselo/elomain.cpp",
82
+ "ext/bayeselo/extconf.rb",
83
+ "ext/bayeselo/list.h",
84
+ "ext/bayeselo/listi.h",
85
+ "ext/bayeselo/move.cpp",
86
+ "ext/bayeselo/move.h",
87
+ "ext/bayeselo/pgn.h",
88
+ "ext/bayeselo/pgnlex.cpp",
89
+ "ext/bayeselo/pgnlex.h",
90
+ "ext/bayeselo/pgnstr.cpp",
91
+ "ext/bayeselo/piece.h",
92
+ "ext/bayeselo/player.h",
93
+ "ext/bayeselo/position.h",
94
+ "ext/bayeselo/random.cpp",
95
+ "ext/bayeselo/random.h",
96
+ "ext/bayeselo/rb_bayeselo.cpp",
97
+ "ext/bayeselo/readstr.cpp",
98
+ "ext/bayeselo/readstr.h",
99
+ "ext/bayeselo/square.h",
100
+ "ext/bayeselo/str.cpp",
101
+ "ext/bayeselo/str.h",
102
+ "ext/bayeselo/version.cpp",
103
+ "ext/bayeselo/version.h",
104
+ "ext/bayeselo/version_number.h",
105
+ "lib/bayeselo.rb",
106
+ "lib/bayeselo/bayeselo.rb",
107
+ "lib/bayeselo/c_bayeselo.rb",
108
+ "test/bayeselo/test_bayeselo.rb",
109
+ "test/bayeselo/test_c_bayeselo.rb",
110
+ "test/helper.rb"
111
+ ]
112
+ s.homepage = %q{http://github.com/phillc/elo-ratings}
113
+ s.licenses = ["MIT"]
114
+ s.require_paths = ["lib"]
115
+ s.rubygems_version = %q{1.3.7}
116
+ s.summary = %q{Ruby wrapper for bayeselo}
117
+ s.test_files = [
118
+ "test/bayeselo/test_bayeselo.rb",
119
+ "test/bayeselo/test_c_bayeselo.rb",
120
+ "test/helper.rb"
121
+ ]
122
+
123
+ if s.respond_to? :specification_version then
124
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
125
+ s.specification_version = 3
126
+
127
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
128
+ s.add_development_dependency(%q<shoulda>, [">= 0"])
129
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
130
+ s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
131
+ s.add_development_dependency(%q<rcov>, [">= 0"])
132
+ s.add_development_dependency(%q<rice>, [">= 0"])
133
+ else
134
+ s.add_dependency(%q<shoulda>, [">= 0"])
135
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
136
+ s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
137
+ s.add_dependency(%q<rcov>, [">= 0"])
138
+ s.add_dependency(%q<rice>, [">= 0"])
139
+ end
140
+ else
141
+ s.add_dependency(%q<shoulda>, [">= 0"])
142
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
143
+ s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
144
+ s.add_dependency(%q<rcov>, [">= 0"])
145
+ s.add_dependency(%q<rice>, [">= 0"])
146
+ end
147
+ end
148
+
@@ -0,0 +1,4 @@
1
+ *.o
2
+ *.bundle
3
+ Makefile
4
+ *.log
@@ -0,0 +1,670 @@
1
+ /////////////////////////////////////////////////////////////////////////////
2
+ //
3
+ // R�mi Coulom
4
+ //
5
+ // December, 2004
6
+ //
7
+ /////////////////////////////////////////////////////////////////////////////
8
+ #include "CBradleyTerry.h"
9
+ #include "CCondensedResults.h"
10
+ #include "CResultSet.h"
11
+ #include "CCDistribution.h"
12
+ #include "CMatrix.h"
13
+ #include "CLUDecomposition.h"
14
+ #include "random.h"
15
+
16
+ #include <cmath>
17
+
18
+ #include <iostream>
19
+ #include "CMatrixIO.h"
20
+
21
+ /////////////////////////////////////////////////////////////////////////////
22
+ // One iteration of the MM algorithm on gammas
23
+ /////////////////////////////////////////////////////////////////////////////
24
+ void CBradleyTerry::UpdateGammas()
25
+ {
26
+ //
27
+ // Loop over players
28
+ //
29
+ for (int Player = crs.GetPlayers(); --Player >= 0;)
30
+ {
31
+ double A = 0;
32
+ double B = 0;
33
+ for (int j = crs.GetOpponents(Player); --j >= 0;)
34
+ {
35
+ const CCondensedResult &cr = crs.GetCondensedResult(Player, j);
36
+
37
+ double OpponentGamma;
38
+ if (cr.Opponent > Player)
39
+ OpponentGamma = pNextGamma[cr.Opponent];
40
+ else
41
+ OpponentGamma = pGamma[cr.Opponent];
42
+
43
+ A += cr.w_ij + cr.d_ij + cr.l_ji + cr.d_ji;
44
+ B += (cr.d_ij + cr.w_ij) * ThetaW /
45
+ (ThetaW * pGamma[Player] + ThetaD * OpponentGamma) +
46
+ (cr.d_ij + cr.l_ij) * ThetaD * ThetaW /
47
+ (ThetaD * ThetaW * pGamma[Player] + OpponentGamma) +
48
+ (cr.d_ji + cr.w_ji) * ThetaD /
49
+ (ThetaW * OpponentGamma + ThetaD * pGamma[Player]) +
50
+ (cr.d_ji + cr.l_ji) /
51
+ (ThetaD * ThetaW * OpponentGamma + pGamma[Player]);
52
+ }
53
+ pNextGamma[Player] = A / B;
54
+ }
55
+
56
+ //
57
+ // Swap buffers to prepare next iteration
58
+ //
59
+ {
60
+ double *pTemp = pGamma;
61
+ pGamma = pNextGamma;
62
+ pNextGamma = pTemp;
63
+ }
64
+ }
65
+
66
+ /////////////////////////////////////////////////////////////////////////////
67
+ // MM on ThetaW
68
+ /////////////////////////////////////////////////////////////////////////////
69
+ double CBradleyTerry::UpdateThetaW()
70
+ {
71
+ double Numerator = 0;
72
+ double Denominator = 0;
73
+
74
+ for (int Player = crs.GetPlayers(); --Player >= 0;)
75
+ for (int j = crs.GetOpponents(Player); --j >= 0;)
76
+ {
77
+ const CCondensedResult &cr = crs.GetCondensedResult(Player, j);
78
+ double OpponentGamma = pGamma[cr.Opponent];
79
+
80
+ Numerator += cr.w_ij + cr.d_ij;
81
+ Denominator += (cr.d_ij + cr.w_ij) * pGamma[Player] /
82
+ (ThetaW * pGamma[Player] + ThetaD * OpponentGamma) +
83
+ (cr.d_ij + cr.l_ij) * ThetaD * pGamma[Player] /
84
+ (ThetaD * ThetaW * pGamma[Player] + OpponentGamma);
85
+ }
86
+
87
+ return Numerator / Denominator;
88
+ }
89
+
90
+ /////////////////////////////////////////////////////////////////////////////
91
+ // MM on ThetaD
92
+ /////////////////////////////////////////////////////////////////////////////
93
+ double CBradleyTerry::UpdateThetaD()
94
+ {
95
+ double Numerator = 0;
96
+ double Denominator = 0;
97
+
98
+ for (int Player = crs.GetPlayers(); --Player >= 0;)
99
+ for (int j = crs.GetOpponents(Player); --j >= 0;)
100
+ {
101
+ const CCondensedResult &cr = crs.GetCondensedResult(Player, j);
102
+ double OpponentGamma = pGamma[cr.Opponent];
103
+
104
+ Numerator += cr.d_ij;
105
+ Denominator += (cr.d_ij + cr.w_ij) * OpponentGamma /
106
+ (ThetaW * pGamma[Player] + ThetaD * OpponentGamma) +
107
+ (cr.d_ij + cr.l_ij) * ThetaW * pGamma[Player] /
108
+ (ThetaD * ThetaW * pGamma[Player] + OpponentGamma);
109
+ }
110
+
111
+ double C = Numerator / Denominator;
112
+
113
+ return C + std::sqrt(C * C + 1);
114
+ }
115
+
116
+ /////////////////////////////////////////////////////////////////////////////
117
+ // Compute the maximum relative difference between two iterations
118
+ /////////////////////////////////////////////////////////////////////////////
119
+ double CBradleyTerry::GetDifference(int n,
120
+ const double *pd1,
121
+ const double *pd2)
122
+ {
123
+ double Result = 0;
124
+ for (int i = n; --i >= 0;)
125
+ {
126
+ double Diff = std::fabs(pd1[i] - pd2[i]) / (pd1[i] + pd2[i]);
127
+ if (Diff > Result)
128
+ Result = Diff;
129
+ }
130
+ return Result;
131
+ }
132
+
133
+ /////////////////////////////////////////////////////////////////////////////
134
+ // Constructor
135
+ /////////////////////////////////////////////////////////////////////////////
136
+ CBradleyTerry::CBradleyTerry(const CCondensedResults &crsInit):
137
+ crs(crsInit),
138
+ velo(crs.GetPlayers()),
139
+ eloAdvantage(32.8),
140
+ eloDraw(97.3),
141
+ v1(crs.GetPlayers()),
142
+ v2(crs.GetPlayers()),
143
+ pGamma(&v1[0]),
144
+ pNextGamma(&v2[0])
145
+ {
146
+ }
147
+
148
+ /////////////////////////////////////////////////////////////////////////////
149
+ // Convert Elos to gammas and Thetas
150
+ /////////////////////////////////////////////////////////////////////////////
151
+ void CBradleyTerry::ConvertEloToGamma() const
152
+ {
153
+ ThetaW = std::pow(10.0, eloAdvantage/400.0);
154
+ ThetaD = std::pow(10.0, eloDraw/400.0);
155
+ for (int i = crs.GetPlayers(); --i >= 0;)
156
+ pGamma[i] = std::pow(10.0, velo[i]/400.0);
157
+ }
158
+
159
+ /////////////////////////////////////////////////////////////////////////////
160
+ // MM Algorithm
161
+ /////////////////////////////////////////////////////////////////////////////
162
+ void CBradleyTerry::MinorizationMaximization(int fThetaW,
163
+ int fThetaD,
164
+ double Epsilon)
165
+ {
166
+ //
167
+ // Set initial values
168
+ //
169
+ ThetaW = fThetaW ? 1.0 : std::pow(10.0, eloAdvantage/400.0);
170
+ ThetaD = fThetaD ? 1.0 : std::pow(10.0, eloDraw/400.0);
171
+ for (int i = crs.GetPlayers(); --i >= 0;)
172
+ pGamma[i] = 1.0;
173
+
174
+ //
175
+ // Main MM loop
176
+ //
177
+ for (int i = 0; i < 10000; i++)
178
+ {
179
+ UpdateGammas();
180
+ double Diff = GetDifference(crs.GetPlayers(), pGamma, pNextGamma);
181
+
182
+ if (fThetaW)
183
+ {
184
+ double NewThetaW = UpdateThetaW();
185
+ double ThetaW_Diff = std::fabs(ThetaW - NewThetaW);
186
+ if (ThetaW_Diff > Diff)
187
+ Diff = ThetaW_Diff;
188
+ ThetaW = NewThetaW;
189
+ }
190
+
191
+ if (fThetaD)
192
+ {
193
+ double NewThetaD = UpdateThetaD();
194
+ double ThetaD_Diff = std::fabs(ThetaD - NewThetaD);
195
+ if (ThetaD_Diff > Diff)
196
+ Diff = ThetaD_Diff;
197
+ ThetaD = NewThetaD;
198
+ }
199
+
200
+ if (Diff < Epsilon)
201
+ break;
202
+
203
+ //
204
+ // Print iteration information
205
+ //
206
+ #if 1
207
+ if ((i + 1) % 100 == 0)
208
+ {
209
+ std::cout << "Iteration " << i + 1 << ": ";
210
+ std::cout << Diff << ' ';
211
+ std::cout << '\n';
212
+ std::cout.flush();
213
+ }
214
+ #endif
215
+ }
216
+
217
+ //
218
+ // Convert back to Elos
219
+ //
220
+ {
221
+ double Total = 0;
222
+ for (int i = crs.GetPlayers(); --i >= 0;)
223
+ Total += (velo[i] = std::log10(pGamma[i]) * 400);
224
+ double Offset = - Total / crs.GetPlayers();
225
+ for (int i = crs.GetPlayers(); --i >= 0;)
226
+ velo[i] += Offset;
227
+ }
228
+
229
+ if (fThetaW)
230
+ eloAdvantage = std::log10(ThetaW) * 400;
231
+
232
+ if (fThetaD)
233
+ eloDraw = std::log10(ThetaD) * 400;
234
+ }
235
+
236
+ /////////////////////////////////////////////////////////////////////////////
237
+ // Get the likelihood of the results of a player
238
+ /////////////////////////////////////////////////////////////////////////////
239
+ double CBradleyTerry::LogLikelihood(int Player) const
240
+ {
241
+ double Result = 0;
242
+ for (int i = crs.GetOpponents(Player); --i >= 0;)
243
+ {
244
+ const CCondensedResult &cr = crs.GetCondensedResult(Player, i);
245
+ double Delta = velo[Player] - velo[cr.Opponent];
246
+ if (cr.w_ij > 0)
247
+ Result += cr.w_ij * std::log(WinProbability(Delta));
248
+ if (cr.d_ij > 0)
249
+ Result += cr.d_ij * std::log(DrawProbability(Delta));
250
+ if (cr.l_ij > 0)
251
+ Result += cr.l_ij * std::log(LossProbability(Delta));
252
+ if (cr.w_ji > 0)
253
+ Result += cr.w_ji * std::log(WinProbability(-Delta));
254
+ if (cr.d_ji > 0)
255
+ Result += cr.d_ji * std::log(DrawProbability(-Delta));
256
+ if (cr.l_ji > 0)
257
+ Result += cr.l_ji * std::log(LossProbability(-Delta));
258
+ }
259
+ return Result;
260
+ }
261
+
262
+ /////////////////////////////////////////////////////////////////////////////
263
+ // Get the likelihood of a result set
264
+ /////////////////////////////////////////////////////////////////////////////
265
+ double CBradleyTerry::LogLikelihood(const CResultSet &rs) const
266
+ {
267
+ double Result = 0;
268
+
269
+ for (int i = rs.GetGames(); --i >= 0;)
270
+ {
271
+ double Delta = velo[rs.GetWhite(i)] - velo[rs.GetBlack(i)];
272
+ switch(rs.GetResult(i))
273
+ {
274
+ case 0: Result += std::log(LossProbability(Delta)); break;
275
+ case 1: Result += std::log(DrawProbability(Delta)); break;
276
+ case 2: Result += std::log(WinProbability(Delta)); break;
277
+ }
278
+ }
279
+
280
+ return Result;
281
+ }
282
+
283
+ /////////////////////////////////////////////////////////////////////////////
284
+ // Get the likelihood of the full set of results
285
+ /////////////////////////////////////////////////////////////////////////////
286
+ double CBradleyTerry::LogLikelihood() const
287
+ {
288
+ double Result = 0;
289
+ for (int Player = crs.GetPlayers(); --Player >= 0;)
290
+ for (int i = crs.GetOpponents(Player); --i >= 0;)
291
+ {
292
+ const CCondensedResult &cr = crs.GetCondensedResult(Player, i);
293
+ double Delta = velo[Player] - velo[cr.Opponent];
294
+ if (cr.w_ij > 0)
295
+ Result += cr.w_ij * std::log(WinProbability(Delta));
296
+ if (cr.d_ij > 0)
297
+ Result += cr.d_ij * std::log(DrawProbability(Delta));
298
+ if (cr.l_ij > 0)
299
+ Result += cr.l_ij * std::log(LossProbability(Delta));
300
+ }
301
+ return Result;
302
+ }
303
+
304
+ /////////////////////////////////////////////////////////////////////////////
305
+ // Get the likelihood distribution of one player
306
+ // The ratings of opponents are supposed to be exact
307
+ /////////////////////////////////////////////////////////////////////////////
308
+ void CBradleyTerry::GetPlayerDist(int Player, CCDistribution &cdist) const
309
+ {
310
+ std::vector<double> veloBackup(velo);
311
+
312
+ for (int i = cdist.GetSize(); --i >= 0;)
313
+ {
314
+ velo[Player] = cdist.ValueFromIndex(i);
315
+ if (crs.GetPlayers() > 1)
316
+ {
317
+ double Delta = (velo[Player] - veloBackup[Player]) / (crs.GetPlayers() - 1);
318
+ for (int j = crs.GetPlayers(); --j >= 0;)
319
+ if (j != Player)
320
+ velo[j] = veloBackup[j] - Delta;
321
+ }
322
+ cdist.SetProbability(i, LogLikelihood(Player));
323
+ }
324
+ cdist.LogNormalize();
325
+
326
+ velo = veloBackup;
327
+ }
328
+
329
+ /////////////////////////////////////////////////////////////////////////////
330
+ // Likelihood distribution of the eloAdvantage parameter
331
+ /////////////////////////////////////////////////////////////////////////////
332
+ void CBradleyTerry::GetAdvantageDist(CCDistribution &cdist) const
333
+ {
334
+ double eloPrevious = eloAdvantage;
335
+ for (int i = cdist.GetSize(); --i >= 0;)
336
+ {
337
+ eloAdvantage = cdist.ValueFromIndex(i);
338
+ cdist.SetProbability(i, LogLikelihood());
339
+ }
340
+ cdist.LogNormalize();
341
+ eloAdvantage = eloPrevious;
342
+ }
343
+
344
+ /////////////////////////////////////////////////////////////////////////////
345
+ // Likelihood distribution of the eloDraw parameter
346
+ /////////////////////////////////////////////////////////////////////////////
347
+ void CBradleyTerry::GetDrawEloDist(CCDistribution &cdist) const
348
+ {
349
+ double eloPrevious = eloDraw;
350
+ for (int i = cdist.GetSize(); --i >= 0;)
351
+ {
352
+ eloDraw = cdist.ValueFromIndex(i);
353
+ cdist.SetProbability(i, LogLikelihood());
354
+ }
355
+ cdist.LogNormalize();
356
+ eloDraw = eloPrevious;
357
+ }
358
+
359
+ /////////////////////////////////////////////////////////////////////////////
360
+ // Compute variance with the diagonal of the Hessian
361
+ /////////////////////////////////////////////////////////////////////////////
362
+ void CBradleyTerry::GetVariance(double *pdVariance) const
363
+ {
364
+ ConvertEloToGamma();
365
+
366
+ const double x = std::log(10.0) / 400;
367
+ const double xx = x * x;
368
+
369
+ for (int Player = crs.GetPlayers(); --Player >= 0;)
370
+ {
371
+ double Diag = 0;
372
+ double PlayerGamma = pGamma[Player];
373
+
374
+ for (int j = crs.GetOpponents(Player); --j >= 0;)
375
+ {
376
+ const CCondensedResult &cr = crs.GetCondensedResult(Player, j);
377
+ double OpponentGamma = pGamma[cr.Opponent];
378
+
379
+ double h = 0;
380
+
381
+ {
382
+ double d = ThetaW * PlayerGamma + ThetaD * OpponentGamma;
383
+ h += (cr.w_ij + cr.d_ij) / (d * d);
384
+ }
385
+ {
386
+ double d = ThetaD * ThetaW * PlayerGamma + OpponentGamma;
387
+ h += (cr.l_ij + cr.d_ij) / (d * d);
388
+ }
389
+ {
390
+ double d = ThetaW * OpponentGamma + ThetaD * PlayerGamma;
391
+ h += (cr.w_ji + cr.d_ji) / (d * d);
392
+ }
393
+ {
394
+ double d = ThetaD * ThetaW * OpponentGamma + PlayerGamma;
395
+ h += (cr.l_ji + cr.d_ji) / (d * d);
396
+ }
397
+
398
+ h *= PlayerGamma * OpponentGamma * ThetaD * ThetaW;
399
+ Diag -= h;
400
+ }
401
+
402
+ pdVariance[Player] = -1.0 / (Diag * xx);
403
+ }
404
+ }
405
+
406
+ /////////////////////////////////////////////////////////////////////////////
407
+ // Compute the covariance matrix
408
+ // This function assumes that ratings are maximum-likelihood ratings
409
+ /////////////////////////////////////////////////////////////////////////////
410
+ void CBradleyTerry::ComputeCovariance()
411
+ {
412
+ //
413
+ // Compute the truncated opposite of the Hessian
414
+ //
415
+ CMatrix mTruncatedHessian(crs.GetPlayers() - 1, crs.GetPlayers() - 1);
416
+ {
417
+ ConvertEloToGamma();
418
+
419
+ const double x = std::log(10.0) / 400;
420
+ const double xx = -x * x;
421
+
422
+ mTruncatedHessian.Zero();
423
+
424
+ for (int Player = crs.GetPlayers() - 1; --Player >= 0;)
425
+ {
426
+ double Diag = 0;
427
+ double PlayerGamma = pGamma[Player];
428
+
429
+ for (int j = crs.GetOpponents(Player); --j >= 0;)
430
+ {
431
+ const CCondensedResult &cr = crs.GetCondensedResult(Player, j);
432
+ double OpponentGamma = pGamma[cr.Opponent];
433
+
434
+ double h = 0;
435
+
436
+ {
437
+ double d = ThetaW * PlayerGamma + ThetaD * OpponentGamma;
438
+ h += (cr.w_ij + cr.d_ij) / (d * d);
439
+ }
440
+ {
441
+ double d = ThetaD * ThetaW * PlayerGamma + OpponentGamma;
442
+ h += (cr.l_ij + cr.d_ij) / (d * d);
443
+ }
444
+ {
445
+ double d = ThetaW * OpponentGamma + ThetaD * PlayerGamma;
446
+ h += (cr.w_ji + cr.d_ji) / (d * d);
447
+ }
448
+ {
449
+ double d = ThetaD * ThetaW * OpponentGamma + PlayerGamma;
450
+ h += (cr.l_ji + cr.d_ji) / (d * d);
451
+ }
452
+
453
+ h *= PlayerGamma * OpponentGamma * ThetaD * ThetaW;
454
+ Diag -= h;
455
+ if (cr.Opponent != crs.GetPlayers() - 1)
456
+ mTruncatedHessian.SetElement(Player, cr.Opponent, h * xx);
457
+ }
458
+
459
+ mTruncatedHessian.SetElement(Player, Player, Diag * xx);
460
+ }
461
+ }
462
+
463
+ //
464
+ // LU-Decompose it
465
+ //
466
+ CLUDecomposition lud(crs.GetPlayers() - 1);
467
+ std::vector<int> vIndex(crs.GetPlayers() - 1);
468
+ lud.Decompose(mTruncatedHessian, &vIndex[0]);
469
+
470
+ //
471
+ // Fill A
472
+ //
473
+ CMatrix mA(crs.GetPlayers(), crs.GetPlayers() - 1);
474
+ {
475
+ double x = -1.0 / crs.GetPlayers();
476
+ for (int i = crs.GetPlayers() * (crs.GetPlayers() - 1); --i >= 0;)
477
+ mA[i] = x;
478
+ for (int i = crs.GetPlayers() - 1; --i >= 0;)
479
+ mA.SetElement(i, i, 1.0 + x);
480
+ }
481
+
482
+ //
483
+ // Compute AC
484
+ //
485
+ CMatrix mAC(crs.GetPlayers(), crs.GetPlayers() - 1);
486
+ for (int i = crs.GetPlayers(); --i >= 0;)
487
+ {
488
+ int Index = i * (crs.GetPlayers() - 1);
489
+ lud.Solve(mTruncatedHessian, &vIndex[0], mA + Index, mAC + Index);
490
+ }
491
+
492
+ //
493
+ // Compute the covariance
494
+ //
495
+ mCovariance.SetProductByTranspose(mAC, mA);
496
+ }
497
+
498
+ /////////////////////////////////////////////////////////////////////////////
499
+ // Compute the likelihood of superiority
500
+ // This function assumes that the covariance matrix and ratings are computed
501
+ /////////////////////////////////////////////////////////////////////////////
502
+ void CBradleyTerry::ComputeLikelihoodOfSuperiority()
503
+ {
504
+ mLOS.SetSize(crs.GetPlayers(), crs.GetPlayers());
505
+
506
+ for (int i = mLOS.GetRows(); --i > 0;)
507
+ for (int j = i; --j >= 0;)
508
+ {
509
+ double Sigma2 = mCovariance.GetElement(i, i) +
510
+ mCovariance.GetElement(j, j) -
511
+ mCovariance.GetElement(i, j) -
512
+ mCovariance.GetElement(j, i);
513
+ double Mu = velo[j] - velo[i];
514
+ double x = Mu / std::sqrt(2 * Sigma2);
515
+ mLOS.SetElement(i, j, erfc(x) / 2);
516
+ mLOS.SetElement(j, i, erfc(-x) / 2);
517
+ }
518
+
519
+ for (int i = mLOS.GetRows(); --i >= 0;)
520
+ mLOS.SetElement(i, i, 0.0);
521
+ }
522
+
523
+ /////////////////////////////////////////////////////////////////////////////
524
+ // ELOstat bounding of probabilities
525
+ /////////////////////////////////////////////////////////////////////////////
526
+ static double ELOstatBound(double p)
527
+ {
528
+ static const double MaxP = 1 / (1 + std::pow(10.0, -600 / double(400)));
529
+ static const double MinP = 1 / (1 + std::pow(10.0, 600 / double(400)));
530
+
531
+ if (p > MaxP)
532
+ p = MaxP;
533
+ if (p < MinP)
534
+ p = MinP;
535
+
536
+ return p;
537
+ }
538
+
539
+ /////////////////////////////////////////////////////////////////////////////
540
+ // ELOstat algorithm
541
+ /////////////////////////////////////////////////////////////////////////////
542
+ void CBradleyTerry::ELOstat(double Epsilon)
543
+ {
544
+ double *pElo = &v1[0];
545
+ double *pNextElo = &v2[0];
546
+ for (int i = crs.GetPlayers(); --i >= 0;)
547
+ pElo[i] = 0;
548
+
549
+ for (int i = 0; i < 10000; i++)
550
+ {
551
+ //
552
+ // Start by copying ratings
553
+ //
554
+ for (int j = crs.GetPlayers(); --j >= 0;)
555
+ pNextElo[j] = pElo[j];
556
+
557
+ //
558
+ // Loop over players to compute their new ratings
559
+ //
560
+ double TotalElo = 0;
561
+ double GrandTotalGames = 0;
562
+ for (int j = crs.GetPlayers(); --j >= 0;)
563
+ {
564
+ double TotalGames = 0;
565
+ double TotalScore = 0;
566
+ double TotalOpponentElo = 0;
567
+
568
+ //
569
+ // Loop over opponents
570
+ //
571
+ for (int k = crs.GetOpponents(j); --k >= 0;)
572
+ {
573
+ const CCondensedResult &cr = crs.GetCondensedResult(j, k);
574
+ double Games = cr.Games();
575
+ TotalGames += Games;
576
+ TotalScore += cr.Score();
577
+ TotalOpponentElo += Games * pElo[cr.Opponent];
578
+ }
579
+
580
+ double p = ELOstatBound(TotalScore / TotalGames);
581
+ double Delta = 400 * std::log10(p / (1 - p));
582
+ pNextElo[j] = TotalOpponentElo / TotalGames + Delta;
583
+ TotalElo += TotalGames * pNextElo[j];
584
+ GrandTotalGames += TotalGames;
585
+ }
586
+
587
+ //
588
+ // Normalize and swap
589
+ //
590
+ for (int j = crs.GetPlayers(); --j >= 0;)
591
+ pNextElo[j] -= TotalElo / GrandTotalGames;
592
+ {
593
+ double *pd = pElo;
594
+ pElo = pNextElo;
595
+ pNextElo = pd;
596
+ }
597
+
598
+ //
599
+ // Compute difference
600
+ //
601
+ double MaxDiff = 0;
602
+ for (int j = crs.GetPlayers(); --j >= 0;)
603
+ {
604
+ double Diff = std::fabs(pNextElo[j] - pElo[j]);
605
+ if (Diff > MaxDiff)
606
+ MaxDiff = Diff;
607
+ }
608
+ if (MaxDiff < Epsilon)
609
+ {
610
+ std::cout << i + 1 << " iterations\n";
611
+ break;
612
+ }
613
+ }
614
+
615
+ //
616
+ // Store computed Elo ratings into vElo
617
+ //
618
+ for (int i = crs.GetPlayers(); --i >= 0;)
619
+ velo[i] = pElo[i];
620
+ }
621
+
622
+ /////////////////////////////////////////////////////////////////////////////
623
+ // ELOstat way of computing confidence intervals
624
+ /////////////////////////////////////////////////////////////////////////////
625
+ void CBradleyTerry::ELOstatIntervals(double *peloLower,
626
+ double *peloUpper) const
627
+ {
628
+ //
629
+ // Loop over players to compute confidence intervals
630
+ //
631
+ for (int j = crs.GetPlayers(); --j >= 0;)
632
+ {
633
+ //
634
+ // Loop over opponents
635
+ //
636
+ double TotalGames = 0;
637
+ double TotalScore = 0;
638
+ for (int k = crs.GetOpponents(j); --k >= 0;)
639
+ {
640
+ const CCondensedResult &cr = crs.GetCondensedResult(j, k);
641
+ TotalGames += cr.Games();
642
+ TotalScore += cr.Score();
643
+ }
644
+ double MeanScore = TotalScore / TotalGames;
645
+
646
+ //
647
+ // Loop again for variance
648
+ //
649
+ double TotalVariance = 0;
650
+ for (int k = crs.GetOpponents(j); --k >= 0;)
651
+ {
652
+ const CCondensedResult &cr = crs.GetCondensedResult(j, k);
653
+ TotalVariance += (cr.w_ij + cr.l_ji)*(1 - MeanScore)*(1 - MeanScore) +
654
+ (cr.d_ij + cr.d_ji)*(0.5 - MeanScore)*(0.5 - MeanScore) +
655
+ (cr.w_ji + cr.l_ij)*(0 - MeanScore)*(0 - MeanScore);
656
+ }
657
+ if (TotalVariance < 0)
658
+ TotalVariance = 0;
659
+ double Variance = TotalVariance / (TotalGames * TotalGames);
660
+ double StandardDeviation = std::sqrt(Variance);
661
+ double pLower = ELOstatBound(MeanScore - 1.95996 * StandardDeviation);
662
+ double pUpper = ELOstatBound(MeanScore + 1.95996 * StandardDeviation);
663
+ double p = ELOstatBound(MeanScore);
664
+ double DeltaLower = 400 * std::log10(pLower / (1 - pLower));
665
+ double DeltaUpper = 400 * std::log10(pUpper / (1 - pUpper));
666
+ double Delta = 400 * std::log10(p / (1 - p));
667
+ peloLower[j] = Delta - DeltaLower;
668
+ peloUpper[j] = DeltaUpper - Delta;
669
+ }
670
+ }