Permalink
Browse files

added first working version of the factor graph, but some layers are …

…missing
  • Loading branch information...
1 parent 6000b67 commit 5d689c52464e62e772ed6935e27fcd9e56a0e54b @larskuhnt larskuhnt committed Apr 14, 2010
@@ -24,6 +24,10 @@ def with_deviation(mean, deviation)
dist.precision_mean = dist.precision * mean
return dist
end
+
+ def with_variance(mean, variance)
+ Distribution.with_deviation(mean, Math.sqrt(variance))
+ end
def with_precision(mean, precision)
Distribution.with_deviation(mean / precision, Math.sqrt(1 / precision))
@@ -49,13 +53,22 @@ def log_ratio_normalisation(x, y)
end
end
+
+ # copy values from other distribution
+ def absorb!(other)
+ @precision = other.precision
+ @precision_mean = other.precision_mean
+ @mean = other.mean
+ @deviation = other.deviation
+ @variance = other.variance
+ end
def *(other)
- Rating.with_precision(self.precision_mean + other.precision_mean, self.precision + other.precision)
+ Distribution.with_precision(self.precision_mean + other.precision_mean, self.precision + other.precision)
end
def /(other)
- Rating.with_precision(self.precision_mean - other.precision_mean, self.precision - other.precision)
+ Distribution.with_precision(self.precision_mean - other.precision_mean, self.precision - other.precision)
end
# absolute difference
@@ -1,21 +1,11 @@
module Saulabs
module TrueSkill
- class Calculation
-
- @@tau = 1
- @@beta = 1
- @@draw_probability = 0.1
-
- @@tau_squared = @@tau**2
- @@beta_squared = @@beta**2
-
- def self.update_skills(game)
- graph = Saulabs::Gauss::FactorGraphs::FactorGraph.new
- end
-
- def self.match_quality(game)
+ class Calculator
+ def self.update_skills(teams)
+ graph = FactorGraph.new(teams, :tau => 0.1, :beta => 20, :draw_probability => 0.0)
+ return graph.evaluate
end
end
@@ -0,0 +1,71 @@
+module Saulabs
+ module TrueSkill
+
+ class FactorGraph
+
+ attr_reader :beta, :beta_squared, :tau, :tau_squared, :draw_probability, :epsilon
+
+ def initialize(teams, options = {})
+ @tau = options[:tau] || 0.1
+ @beta = options[:beta] || 20
+ @draw_probability = options[:draw_probability] || 0.1
+ @tau_squared = tau**2
+ @beta_squared = beta**2
+ @epsilon = -Math.sqrt(2.0 * @beta_squared) * Gauss::Functions.inv_cdf((1.0 - @draw_probability) / 2.0)
+
+ @prior_layer = Layers::PriorToSkills.new(self, teams)
+ @layers = [
+ @prior_layer
+ ]
+ end
+
+ def evaluate
+ build_layers
+ run_schedule
+ [ranking_probability, updated_skills]
+ end
+
+ private
+
+ def ranking_probability
+ factor_list = []
+ sum_log_z = 0.0
+ sum_log_s = 0.0
+ @layers.each do |layer|
+ layer.factors.each do |factor|
+ factor.reset_marginals
+ factor.messages.each_index { |i| sum_log_z += factor.send_message_at(i) }
+ sum_log_s += factor.log_normalisation
+ end
+ end
+ Math.exp(sum_log_z + sum_log_s)
+ end
+
+ def updated_skills
+ @prior_layer.output
+ end
+
+ def build_layers
+ output = nil
+ @layers.each do |layer|
+ layer.input = output if output
+ layer.build
+ output = layer.output
+ end
+ end
+
+ def run_schedule
+ schedules = []
+ @layers.each do |layer|
+ schedules << layer.create_prior_schedule
+ end
+ @layers.reverse.each do |layer|
+ schedules << layer.create_posterior_schedule
+ end
+ Schedules::Sequence.new(schedules.compact).visit
+ end
+
+ end
+
+ end
+end
@@ -1,37 +0,0 @@
-module Saulabs
- module TrueSkill
- module FactorGraphs
-
- class FactorGraph
-
- # layers:
- def initialize(teams)
- @prior_layer = Saulabs::TrueSkill::Layers::PriorToSkills.new(teams)
- @layers = [
- @prior_layer,
- Saulabs::TrueSkill::Layers::SkillsToPerformances.new(self),
- Saulabs::TrueSkill::Layers::TeamDifferences.new(self)
- ]
- build
- end
-
- def run_schedule
-
- end
-
- private
-
- def build
- output = nil
- @layers.each do |layer|
- layer.input = output if output
- layer.build
- output = layer.output
- end
- end
-
- end
-
- end
- end
-end
@@ -1,18 +0,0 @@
-module Saulabs
- module TrueSkill
- module FactorGraphs
-
- class Variable
-
- attr_accessor :value, :parent_index
-
- def initialize(value, parent_index)
- @value = value
- @parent_index = parent_index
- end
-
- end
-
- end
- end
-end
@@ -4,42 +4,49 @@ module Factors
class Base
- attr_accessor :messages, :binding, :variables
+ attr_accessor :messages, :bindings
def initialize
@messages = []
- @binding = {}
- @variables = []
+ @bindings = {}
end
def update_message_at(idx)
- update_message(@messages[idx], @binding[@messages[idx]])
+ raise "illegal message index: #{idx}" if idx < 0 || idx > message_count-1
+ update_message(@messages[idx], @bindings[@messages[idx]])
end
def update_message(message, variable)
- raise "Abstract method Gauss::Factors::Base#update_message(message, variable) called"
+ raise "Abstract method Factors::Base#update_message(message, variable) called"
+ end
+
+ def message_count
+ 0
+ end
+
+ def log_normalisation
+ 0.0
end
def reset_marginals
- @binding.values.each { |var| var.reset_to_prior }
+ @bindings.values.each { |var| var.absorb!(Gauss::Distribution.with_deviation(0.0, 0.0)) }
end
def send_message_at(idx)
- self.send_message(@messages[idx], @binding[@messages[idx]])
+ self.send_message(@messages[idx], @bindings[@messages[idx]])
end
# message: normal distribution
def send_message(message, variable)
- log_z = Saulabs::Gauss::Distribution.log_product_normalisation(message, variable.value)
- variable.value = message.value * variable.value
+ log_z = Saulabs::Gauss::Distribution.log_product_normalisation(message, variable)
+ variable.absorb!(message * variable)
return log_z
end
def bind(variable)
message = Saulabs::Gauss::Distribution.new
@messages << message
- @binding[message] = variable
- @variables << variable
+ @bindings[message] = variable
return message
end
@@ -2,32 +2,28 @@ module Saulabs
module TrueSkill
module Factors
- class Prior < Saulabs::TrueSkill::Factors::Base
-
- attr_accessor :message
+ class Prior < Base
+ #
def initialize(mean, variance, variable)
- super
- @message = Saulabs::Gauss::Distribution.with_deviation(mean, Math.sqrt(variance))
+ super()
+ @message = Gauss::Distribution.with_variance(mean, variance)
bind(variable)
end
def update_message(message, variable)
- old_marginal = variable.value.clone
- old_message = message.clone
- new_marginal = generate_new_marginal(old_marginal, old_message)
- variable.value = new_marginal
- message = @message
- return old_marginal - new_marginal
+ new_marginal = Gauss::Distribution.with_precision(
+ variable.precision_mean + @message.precision_mean - message.precision_mean,
+ variable.precision + @message.precision - message.precision
+ )
+ diff = variable - message
+ variable.absorb!(new_marginal)
+ message.absorb!(@message)
+ return diff
end
- private
-
- def generate_new_marginal(old_marginal, old_message)
- Saulabs::Gauss::Distribution.with_precision(
- old_marginal.precision_mean + @message.precision_mean - old_message.precision_mean,
- old_marginal.precision + @message.precision - old_message.precision
- )
+ def message_count
+ 1
end
end
@@ -4,13 +4,26 @@ module Layers
class Base
- attr_accessor :factors, :output, :input
+ attr_accessor :graph, :factors, :output, :input
- def initialize
+ def initialize(graph)
+ @graph = graph
@factors = []
@output = []
@input = []
end
+
+ def build
+ raise "Abstract method Layers::Base#build called"
+ end
+
+ def create_prior_schedule
+ nil
+ end
+
+ def create_posterior_schedule
+ nil
+ end
end
@@ -2,21 +2,28 @@ module Saulabs
module TrueSkill
module Layers
- class PriorToSkills
+ class PriorToSkills < Base
- def initialize(teams)
- super
- teams.each do |team|
+ def initialize(graph, teams)
+ super(graph)
+ @teams = teams
+ end
+
+ def build
+ @teams.each do |team|
team_skills = []
team.each do |skill|
- variable = Saulabs::TrueSkill::FactorGraphs::Variable.new()
- @layers << Saulabs::TrueSkill::Factors::Prior.new(skill.mean, skill.deviation**2 + Saulabs::TrueSkill.tau, variable)
+ @factors << Factors::Prior.new(skill.mean, skill.variance + @graph.tau_squared, Gauss::Distribution.new)
team_skills << skill
end
@output << team_skills
end
end
+ def create_prior_schedule
+ Schedules::Sequence.new(@factors.map { |f| Schedules::Step.new(f, 0) })
+ end
+
end
end
@@ -0,0 +1,15 @@
+module Saulabs
+ module TrueSkill
+ module Schedules
+
+ class Base
+
+ def visit(depth = -1, max_depth = 0)
+ raise "Abstract method Schedules::Base#visit(depth, max_depth) called"
+ end
+
+ end
+
+ end
+ end
+end
Oops, something went wrong.

0 comments on commit 5d689c5

Please sign in to comment.