all-up-in-your-elo 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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 +42 -0
  6. data/Rakefile +54 -0
  7. data/VERSION +1 -0
  8. data/all-up-in-your-elo.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/eloratings.cpp +76 -0
  63. data/ext/bayeselo/extconf.rb +6 -0
  64. data/ext/bayeselo/list.h +367 -0
  65. data/ext/bayeselo/listi.h +125 -0
  66. data/ext/bayeselo/move.cpp +249 -0
  67. data/ext/bayeselo/move.h +139 -0
  68. data/ext/bayeselo/pgn.h +62 -0
  69. data/ext/bayeselo/pgnlex.cpp +432 -0
  70. data/ext/bayeselo/pgnlex.h +105 -0
  71. data/ext/bayeselo/pgnstr.cpp +126 -0
  72. data/ext/bayeselo/piece.h +44 -0
  73. data/ext/bayeselo/player.h +31 -0
  74. data/ext/bayeselo/position.h +89 -0
  75. data/ext/bayeselo/random.cpp +114 -0
  76. data/ext/bayeselo/random.h +63 -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/elo_ratings.rb +9 -0
  86. data/lib/elo_ratings/c_bayeselo.rb +5 -0
  87. data/lib/elo_ratings/results.rb +41 -0
  88. data/test/elo_ratings/test_c_bayeselo.rb +84 -0
  89. data/test/elo_ratings/test_results.rb +41 -0
  90. data/test/helper.rb +19 -0
  91. metadata +233 -0
@@ -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
@@ -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
@@ -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.
@@ -0,0 +1,42 @@
1
+ = All Up in Your Elo
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
+ = All Up in Your Elo
14
+
15
+ The goal of this gem was to provide a simple interface to Bayeselo in ruby.
16
+ Advantage is set to zero (no advan advantage modifier to player 1)
17
+ There has currently been no attempt to reproduce all functionality provided by Bayeselo.
18
+
19
+ Only tested in ruby 1.9.1
20
+ Ruby 1.9.2 throws some form of bus error at end of execution.
21
+
22
+ = Usage
23
+
24
+ require 'elo_ratings'
25
+ ratings = EloRatings::Results.new
26
+ ratings.add_game "player 1", "player 2", EloRatings::Player1Win
27
+ ratings.add_game "player 1", "player 2", EloRatings::Player2Win
28
+ ratings.add_game "player 3", "player 2", EloRatings::Player1Win
29
+
30
+ ratings.count_games_for "player 2"
31
+ => 3
32
+ ratings.elo_for "player 2"
33
+ => -37
34
+ ratings.elo_for "player 3"
35
+ => 86
36
+
37
+
38
+ == Copyright
39
+
40
+ Copyright (c) 2010 phillc. See LICENSE.txt for
41
+ further details.
42
+
@@ -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 = "all-up-in-your-elo"
16
+ gem.homepage = "http://github.com/phillc/https://github.com/phillc/all-up-in-your-elo"
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 = "all up in your elo #{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.2
@@ -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{all-up-in-your-elo}
8
+ s.version = "0.0.2"
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-04-13}
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
+ "all-up-in-your-elo.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/eloratings.cpp",
83
+ "ext/bayeselo/extconf.rb",
84
+ "ext/bayeselo/list.h",
85
+ "ext/bayeselo/listi.h",
86
+ "ext/bayeselo/move.cpp",
87
+ "ext/bayeselo/move.h",
88
+ "ext/bayeselo/pgn.h",
89
+ "ext/bayeselo/pgnlex.cpp",
90
+ "ext/bayeselo/pgnlex.h",
91
+ "ext/bayeselo/pgnstr.cpp",
92
+ "ext/bayeselo/piece.h",
93
+ "ext/bayeselo/player.h",
94
+ "ext/bayeselo/position.h",
95
+ "ext/bayeselo/random.cpp",
96
+ "ext/bayeselo/random.h",
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/elo_ratings.rb",
106
+ "lib/elo_ratings/c_bayeselo.rb",
107
+ "lib/elo_ratings/results.rb",
108
+ "test/elo_ratings/test_c_bayeselo.rb",
109
+ "test/elo_ratings/test_results.rb",
110
+ "test/helper.rb"
111
+ ]
112
+ s.homepage = %q{http://github.com/phillc/https://github.com/phillc/all-up-in-your-elo}
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/elo_ratings/test_c_bayeselo.rb",
119
+ "test/elo_ratings/test_results.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
+ }