class Bayesnet::Factor

Factor if a function of sevaral variables (A, B, …) each defined on values from finite set

Public Class Methods

build(&block) click to toggle source
# File lib/bayesnet/factor.rb, line 6
def self.build(&block)
  factor = new
  factor.instance_eval(&block)
  factor
end
from_distribution(var_distribution) click to toggle source
# File lib/bayesnet/factor.rb, line 36
def self.from_distribution(var_distribution)
  self.class.new(var_distribution.keys, var_distribution.values.map(&:to_a))
end
new(scope = {}, vals = {}) click to toggle source
# File lib/bayesnet/factor.rb, line 89
def initialize(scope = {}, vals = {})
  @scope = scope
  @vals = vals
end

Public Instance Methods

[](*context) click to toggle source
# File lib/bayesnet/factor.rb, line 27
def [](*context)
  key = if context.size == 1 && context[0].is_a?(Hash)
          context[0].slice(*var_names).values
        else
          context
        end
  @vals[key]
end
contextes(*var_names) click to toggle source
# File lib/bayesnet/factor.rb, line 40
def contextes(*var_names)
  return [] if var_names.empty?

  @scope[var_names[0]].product(*var_names[1..].map { |var_name| @scope[var_name] })
end
delete_by_indices(array, indices) click to toggle source
# File lib/bayesnet/factor.rb, line 70
def delete_by_indices(array, indices)
  result = array.dup
  indices.map { |i| result[i] = nil }
  result.compact
end
marginalize(var_names) click to toggle source

groups by ‘var_names` having same context and sum out values.

# File lib/bayesnet/factor.rb, line 77
def marginalize(var_names)
  scope = @scope.slice(*var_names)

  indices = scope.keys.map { |k| index_by_var_name[k] }
  vals = @vals.group_by { |context, _val| indices.map { |i| context[i] } }
  vals.transform_values! { |v| v.map(&:last).sum }

  self.class.new(scope, vals)
end
normalize() click to toggle source
# File lib/bayesnet/factor.rb, line 50
def normalize
  vals = @vals.clone
  norm_factor = vals.map(&:last).sum * 1.0
  vals.each { |k, _v| vals[k] /= norm_factor }
  self.class.new(@scope.clone, vals)
end
reduce_to(context) click to toggle source
# File lib/bayesnet/factor.rb, line 57
def reduce_to(context)
  # TODO: use Hash#except when Ruby 2.6 support no longer needed
  context_keys_set = context.keys.to_set
  scope = @scope.reject { |k, _| context_keys_set.include?(k) }

  context_vals = context.values
  indices = context.keys.map { |k| index_by_var_name[k] }
  vals = @vals.select { |k, _v| indices.map { |i| k[i] } == context_vals }
  vals.transform_keys! { |k| delete_by_indices(k, indices) }

  self.class.new(scope, vals)
end
scope(var_name_to_values) click to toggle source

Specifies variable name together with its values

# File lib/bayesnet/factor.rb, line 13
def scope(var_name_to_values)
  @scope.merge!(var_name_to_values)
end
val(*context_and_val) click to toggle source

Specifies value for a scope context. Value is the last element in ‘context_and_val`

# File lib/bayesnet/factor.rb, line 18
def val(*context_and_val)
  context_and_val = context_and_val[0] if context_and_val.size == 1 && context_and_val[0].is_a?(Array)
  @vals[context_and_val[0..-2]] = context_and_val[-1]
end
values() click to toggle source
# File lib/bayesnet/factor.rb, line 46
def values
  @vals.values
end
var_names() click to toggle source
# File lib/bayesnet/factor.rb, line 23
def var_names
  @scope.keys
end

Private Instance Methods

index_by_var_name() click to toggle source
# File lib/bayesnet/factor.rb, line 94
def index_by_var_name
  return @index_by_var_name if @index_by_var_name

  @index_by_var_name = {}
  @scope.each_with_index { |(k, _v), i| @index_by_var_name[k] = i }
  @index_by_var_name
end