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.
- data/.document +5 -0
- data/Gemfile +14 -0
- data/Gemfile.lock +22 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +23 -0
- data/Rakefile +54 -0
- data/VERSION +1 -0
- data/elo-ratings.gemspec +148 -0
- data/ext/bayeselo/.gitignore +4 -0
- data/ext/bayeselo/CBradleyTerry.cpp +670 -0
- data/ext/bayeselo/CBradleyTerry.h +137 -0
- data/ext/bayeselo/CCDistribution.cpp +135 -0
- data/ext/bayeselo/CCDistribution.h +44 -0
- data/ext/bayeselo/CCDistributionCUI.cpp +104 -0
- data/ext/bayeselo/CCDistributionCUI.h +38 -0
- data/ext/bayeselo/CCondensedResults.cpp +253 -0
- data/ext/bayeselo/CCondensedResults.h +64 -0
- data/ext/bayeselo/CDiscretization.h +40 -0
- data/ext/bayeselo/CDistribution.cpp +123 -0
- data/ext/bayeselo/CDistribution.h +58 -0
- data/ext/bayeselo/CDistributionCollection.cpp +34 -0
- data/ext/bayeselo/CDistributionCollection.h +29 -0
- data/ext/bayeselo/CEloRatingCUI.cpp +1046 -0
- data/ext/bayeselo/CEloRatingCUI.h +67 -0
- data/ext/bayeselo/CIndirectCompare.h +28 -0
- data/ext/bayeselo/CJointBayesian.cpp +111 -0
- data/ext/bayeselo/CJointBayesian.h +38 -0
- data/ext/bayeselo/CLUDecomposition.cpp +154 -0
- data/ext/bayeselo/CLUDecomposition.h +36 -0
- data/ext/bayeselo/CMatrix.cpp +48 -0
- data/ext/bayeselo/CMatrix.h +35 -0
- data/ext/bayeselo/CMatrixIO.cpp +27 -0
- data/ext/bayeselo/CMatrixIO.h +17 -0
- data/ext/bayeselo/CPredictionCUI.cpp +393 -0
- data/ext/bayeselo/CPredictionCUI.h +50 -0
- data/ext/bayeselo/CResultSet.cpp +245 -0
- data/ext/bayeselo/CResultSet.h +50 -0
- data/ext/bayeselo/CResultSetCUI.cpp +355 -0
- data/ext/bayeselo/CResultSetCUI.h +44 -0
- data/ext/bayeselo/CTimeIO.cpp +58 -0
- data/ext/bayeselo/CTimeIO.h +19 -0
- data/ext/bayeselo/CVector.cpp +110 -0
- data/ext/bayeselo/CVector.h +42 -0
- data/ext/bayeselo/EloDataFromFile.cpp +120 -0
- data/ext/bayeselo/EloDataFromFile.h +21 -0
- data/ext/bayeselo/README +8 -0
- data/ext/bayeselo/ReadLineToString.cpp +32 -0
- data/ext/bayeselo/ReadLineToString.h +18 -0
- data/ext/bayeselo/chtime.cpp +56 -0
- data/ext/bayeselo/chtime.h +61 -0
- data/ext/bayeselo/chtimer.h +27 -0
- data/ext/bayeselo/clktimer.cpp +178 -0
- data/ext/bayeselo/clktimer.h +29 -0
- data/ext/bayeselo/consolui.cpp +538 -0
- data/ext/bayeselo/consolui.h +154 -0
- data/ext/bayeselo/const.cpp +79 -0
- data/ext/bayeselo/const.h +42 -0
- data/ext/bayeselo/date.cpp +96 -0
- data/ext/bayeselo/date.h +44 -0
- data/ext/bayeselo/debug.h +58 -0
- data/ext/bayeselo/elomain.cpp +26 -0
- data/ext/bayeselo/extconf.rb +6 -0
- data/ext/bayeselo/list.h +367 -0
- data/ext/bayeselo/listi.h +125 -0
- data/ext/bayeselo/move.cpp +249 -0
- data/ext/bayeselo/move.h +139 -0
- data/ext/bayeselo/pgn.h +62 -0
- data/ext/bayeselo/pgnlex.cpp +432 -0
- data/ext/bayeselo/pgnlex.h +105 -0
- data/ext/bayeselo/pgnstr.cpp +126 -0
- data/ext/bayeselo/piece.h +44 -0
- data/ext/bayeselo/player.h +31 -0
- data/ext/bayeselo/position.h +89 -0
- data/ext/bayeselo/random.cpp +114 -0
- data/ext/bayeselo/random.h +63 -0
- data/ext/bayeselo/rb_bayeselo.cpp +76 -0
- data/ext/bayeselo/readstr.cpp +51 -0
- data/ext/bayeselo/readstr.h +19 -0
- data/ext/bayeselo/square.h +61 -0
- data/ext/bayeselo/str.cpp +81 -0
- data/ext/bayeselo/str.h +71 -0
- data/ext/bayeselo/version.cpp +30 -0
- data/ext/bayeselo/version.h +28 -0
- data/ext/bayeselo/version_number.h +1 -0
- data/lib/bayeselo.rb +6 -0
- data/lib/bayeselo/bayeselo.rb +11 -0
- data/lib/bayeselo/c_bayeselo.rb +7 -0
- data/test/bayeselo/test_bayeselo.rb +33 -0
- data/test/bayeselo/test_c_bayeselo.rb +84 -0
- data/test/helper.rb +18 -0
- metadata +226 -0
data/.document
ADDED
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
|
data/elo-ratings.gemspec
ADDED
|
@@ -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,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
|
+
}
|