Permalink
Browse files

fixed calculation of inverse error function for correct draw margin c…

…alculation
  • Loading branch information...
1 parent e90afe2 commit 2e97dc2b376f6affe7f2fed5b5ef120edcf1c788 @larskuhnt larskuhnt committed Jan 17, 2011
@@ -74,10 +74,29 @@ def probability_density_function(x)
# The inverse of the cummulative Gaussian distribution function
def quantile_function(x)
- -@@sqrt2 * Math.erfc(2.0 * x)
+ -@@sqrt2 * inv_erf(2.0 * x)
end
alias_method :inv_cdf, :quantile_function
+ def inv_erf(p)
+ return -100 if p >= 2.0
+ return 100 if p <= 0.0
+
+ pp = p < 1.0 ? p : 2 - p
+ t = Math.sqrt(-2*Math.log(pp/2.0)) # Initial guess
+ x = -0.70711*((2.30753 + t*0.27061)/(1.0 + t*(0.99229 + t*0.04481)) - t)
+
+ [0,1].each do |j|
+ err = erf(x) - pp
+ x += err/(1.12837916709551257*Math.exp(-(x*x)) - x*err) # Halley
+ end
+ p < 1.0 ? x : -x
+ end
+
+ def erf(x)
+ Math.erfc(x)
+ end
+
end
def value_at(x)
@@ -46,7 +46,7 @@ class FactorGraph
# include Saulabs::TrueSkill
#
# # team 1 has just one player with a mean skill of 27.1, a skill-deviation of 2.13
- # # and an play activity of 100 %
+ # # and a play activity of 100 %
# team1 = [Rating.new(27.1, 2.13, 1.0)]
#
# # team 2 has two players
@@ -130,7 +130,7 @@
describe 'value = 0.27' do
- it "#cumulative_distribution_function should return 0.6064198" do
+ it "#cumulative_distribution_function should return 0.6064198 for 0.27" do
Gauss::Distribution.cumulative_distribution_function(0.27).should be_close(0.6064198, 0.00001)
Gauss::Distribution.cdf(2.0).should be_close(0.9772498, 0.00001)
end
@@ -139,8 +139,36 @@
Gauss::Distribution.probability_density_function(0.27).should be_close(0.384662, 0.0001)
end
- it "#quantile_function should return -0.62941" do
- Gauss::Distribution.quantile_function(0.27).should be_close(-0.62941, 0.00001)
+ it "#quantile_function should return ~ -0.6128123 at 0.27" do
+ Gauss::Distribution.quantile_function(0.27).should be_close(-0.6128123, 0.00001)
+ end
+
+ it "#quantile_function should return ~ 1.281551 at 0.9" do
+ Gauss::Distribution.quantile_function(0.9).should be_close(1.281551, 0.00001)
+ end
+
+ it "#erf_inv should return 0.0888559 at 0.9" do
+ Gauss::Distribution.inv_erf(0.9).should be_close(0.0888559, 0.00001)
+ end
+
+ it "#erf_inv should return 0.779983 at 0.27" do
+ Gauss::Distribution.inv_erf(0.27).should be_close(0.779983, 0.00001)
+ end
+
+ it "#erf_inv should return 100 at -0.5" do
+ Gauss::Distribution.inv_erf(-0.5).should be_close(100, 0.00001)
+ end
+
+ it "#erf should return 0.203091 at 0.9" do
+ Gauss::Distribution.erf(0.9).should be_close(0.203091, 0.00001)
+ end
+
+ it "#erf should return 0.702581 at 0.27" do
+ Gauss::Distribution.erf(0.27).should be_close(0.702581, 0.00001)
+ end
+
+ it "#erf should return 1.520499 at -0.5" do
+ Gauss::Distribution.erf(-0.5).should be_close(1.520499, 0.00001)
end
end
@@ -24,6 +24,7 @@
describe "#w_exceeds_margin" do
it "should return 0.657847 for (0.2, 0.3)" do
+ Gauss::TruncatedCorrection.w_exceeds_margin(0.0, 0.740466).should be_close(0.76774506, tolerance)
Gauss::TruncatedCorrection.w_exceeds_margin(0.2, 0.3).should be_close(0.657847, tolerance)
Gauss::TruncatedCorrection.w_exceeds_margin(0.1, 0.03).should be_close(0.621078, tolerance)
end
@@ -33,6 +34,7 @@
describe "#v_exceeds_margin" do
it "should return 0.8626174 for (0.2, 0.3)" do
+ Gauss::TruncatedCorrection.v_exceeds_margin(0.0, 0.740466).should be_close(1.32145197, tolerance)
Gauss::TruncatedCorrection.v_exceeds_margin(0.2, 0.3).should be_close(0.8626174, tolerance)
Gauss::TruncatedCorrection.v_exceeds_margin(0.1, 0.03).should be_close(0.753861, tolerance)
end
@@ -11,48 +11,100 @@
describe "#update_skills" do
- it "should update the mean of the first player in team1 to 29.61452" do
+ it "should update the mean of the first player in team1 to 30.38345" do
@graph.update_skills
- @skill.mean.should be_close(29.61452, tolerance)
+ @skill.mean.should be_close(30.38345, tolerance)
end
- it "should update the deviation of the first player in team1 to 3.5036" do
+ it "should update the deviation of the first player in team1 to 3.46421" do
@graph.update_skills
- @skill.deviation.should be_close(3.5036, tolerance)
+ @skill.deviation.should be_close(3.46421, tolerance)
end
end
describe "#draw_margin" do
- it "should be -0.998291" do
- @graph.draw_margin.should be_close(-0.998291, tolerance)
+ it "should be -0.998291 for diff 0.740466" do
+ @graph.draw_margin.should be_close(0.740466, tolerance)
end
end
end
-describe Saulabs::TrueSkill::FactorGraph do
+describe Saulabs::TrueSkill::FactorGraph, "two players" do
+
+ before :each do
+ @teams = [
+ [TrueSkill::Rating.new(25.0, 25.0/3.0, 1.0, 25.0/300.0)],
+ [TrueSkill::Rating.new(25.0, 25.0/3.0, 1.0, 25.0/300.0)]
+ ]
+ end
+
+ describe 'win with standard rating' do
+
+ before :each do
+ TrueSkill::FactorGraph.new(@teams, [1,2]).update_skills
+ end
+
+ it "should change first players rating to [29.395832, 7.1714755]" do
+ @teams[0][0].should eql_rating(29.395832, 7.1714755)
+ end
+
+ it "should change second players rating to [20.6041679, 7.1714755]" do
+ @teams[1][0].should eql_rating(20.6041679, 7.1714755)
+ end
+
+ end
+
+ describe 'draw with standard rating' do
+
+ before :each do
+ TrueSkill::FactorGraph.new(@teams, [1,1]).update_skills
+ end
+
+ it "should change first players rating to [25.0, 6.4575196]" do
+ @teams[0][0].should eql_rating(25.0, 6.4575196)
+ end
+
+ it "should change second players rating to [25.0, 6.4575196]" do
+ @teams[1][0].should eql_rating(25.0, 6.4575196)
+ end
+
+ end
+
+ describe 'draw with different ratings' do
+
+ before :each do
+ @teams[1][0] = TrueSkill::Rating.new(50.0, 12.5, 1.0, 25.0/300.0)
+ TrueSkill::FactorGraph.new(@teams, [1,1]).update_skills
+ end
+
+ it "should change first players rating to [31.6623, 7.1374]" do
+ @teams[0][0].should eql_rating(31.662301, 7.1374459)
+ end
+
+ it "should change second players mean to [35.0107, 7.9101]" do
+ @teams[1][0].should eql_rating(35.010653, 7.910077)
+ end
+
+ end
+
+end
+
+describe Saulabs::TrueSkill::FactorGraph, "two on two" do
before :each do
@teams = [
- [
- TrueSkill::Rating.new(8.3, 3.1, 0.8, 0.02),
- TrueSkill::Rating.new(3.0, 3.1, 0.8, 0.02),
- TrueSkill::Rating.new(2.2, 3.2, 0.8, 0.02)
- ],
- [
- TrueSkill::Rating.new(25.0, 8.3333, 0.8, 0.02)
- ]
+ [TrueSkill::Rating.new(25.0, 25.0/3.0, 1.0, 25.0/300.0),TrueSkill::Rating.new(25.0, 25.0/3.0, 1.0, 25.0/300.0)],
+ [TrueSkill::Rating.new(25.0, 25.0/3.0, 1.0, 25.0/300.0),TrueSkill::Rating.new(25.0, 25.0/3.0, 1.0, 25.0/300.0)]
]
- @skill = @teams.last.first
- @graph = TrueSkill::FactorGraph.new(@teams, [1,2], :beta => 25.0, :draw_probability => 0.0)
end
- it "should do something" do
- 40.times { TrueSkill::FactorGraph.new(@teams, [1,2], :beta => 15.0, :draw_probability => 0.0).update_skills }
- puts "#{@skill.to_s}"
+ describe 'win with standard rating' do
+
+
end
end
View
@@ -2,12 +2,13 @@
require 'rubygems'
require 'spec'
require 'spec/autorun'
-require "#{File.dirname(__FILE__)}/../lib/saulabs/trueskill.rb"
+require File.expand_path(File.join(File.dirname(__FILE__), "..", "lib", "saulabs", "trueskill.rb"))
+require File.expand_path(File.join(File.dirname(__FILE__), "true_skill_matchers.rb"))
include Saulabs
Spec::Runner.configure do |config|
-
+ config.include(TrueSkillMatchers)
end
def tolerance
@@ -0,0 +1,38 @@
+module TrueSkillMatchers
+
+ class EqualRating
+
+ def initialize(mean, deviation, precision)
+ @expected = TrueSkill::Rating.new(mean, deviation)
+ @precision = 10**precision
+ end
+
+ def matches?(target)
+ @target = target
+ @mean_diff = @expected.mean - @target.mean
+ @deviation_diff = @expected.deviation - @target.deviation
+ (@mean_diff*@precision).to_i + (@deviation_diff*@precision).to_i == 0
+ end
+
+ def failure_message
+ "expected rating #{@target.to_s} to be equal to #{@expected.to_s} #{failure_info}"
+ end
+
+ def negative_failure_message
+ "expected rating #{@target.to_s} not to be equal to #{@expected.to_s} #{failure_info}"
+ end
+
+ def failure_info
+ msg = []
+ msg << "mean differs by #{@mean_diff}" if @mean_diff != 0.0
+ msg << "deviation differs by #{@deviation_diff}" if @deviation_diff != 0.0
+ " (#{msg.join(', ')})"
+ end
+
+ end
+
+ def eql_rating(target_mean, target_deviation, precision = 5)
+ EqualRating.new(target_mean, target_deviation, precision)
+ end
+
+end

0 comments on commit 2e97dc2

Please sign in to comment.