class Symian::TransitionMatrix
Attributes
transition_probabilities[R]
this is mostly for testing purposes
Public Class Methods
new(input)
click to toggle source
# File lib/symian/transition_matrix.rb, line 13 def initialize(input) # allow filename, string, and IO objects as input if input.kind_of?(String) if File.exists?(input) input = File.new(input, 'r') else input = StringIO.new(input.strip.split("\n").collect{|l| l.strip }.join("\n")) end else raise RuntimeError unless input.respond_to?(:read) end @transition_probabilities = {} # process escalation matrix headers = nil CSV.parse(input.read, :headers => :first_row) do |row| headers ||= row.headers @sg_names ||= headers[1..-2] # make sure that support groups do not include the "In" virtual support group raise RuntimeError if @sg_names.include?("In") # make sure that last support group is the "Out" virtual support group raise RuntimeError unless headers[-1] == "Out" sg_name = row[0] # the first row element is the support group name # make sure support group name is valid raise RuntimeError unless sg_name == "In" or @sg_names.include?(sg_name) # make sure we are not overwriting existing data raise RuntimeError if @transition_probabilities[sg_name] @transition_probabilities[sg_name] = [] # prepare corresponding row in transition matrix 2.upto(row.length) do |i| escalations = Integer(row[i-1]) # raises ArgumentError in case of errors if escalations > 0 @transition_probabilities[sg_name] << { :sg_name => headers[i-1], :escalations => escalations } end end # calculate normalized probabilities normalize_probabilities(@transition_probabilities[sg_name]) end # check that we have transition probabilities for each support group [ "In", *@sg_names].each do |name| raise RuntimeError unless @transition_probabilities.has_key?(name) end # TODO: make seeding of this thing configurable... @rng = ERV::RandomVariable.new(:distribution => :uniform, :min_value => 0.0, :max_value => 1.0) end
Public Instance Methods
escalation(from)
click to toggle source
# File lib/symian/transition_matrix.rb, line 71 def escalation(from) # raise error if source support group does not exist raise ArgumentError unless tps = @transition_probabilities[from] # get random value x = @rng.next # return name of first support group whose (cumulative) # transition probability is larger than x tps.each do |el| return el[:sg_name] if el[:probability] > x end # the destination support group was not found raise RuntimeError end
merge(sg1_name, sg2_name, new_name=nil)
click to toggle source
# File lib/symian/transition_matrix.rb, line 89 def merge(sg1_name, sg2_name, new_name=nil) # raise error if support groups do not exist raise RuntimeError unless sg1_probs = @transition_probabilities.delete(sg1_name) and sg2_probs = @transition_probabilities.delete(sg2_name) new_sg_name = new_name || "Merge_of_%s_and_%s" % [ sg1_name, sg2_name ] # recalculate escalations to new sg @transition_probabilities.each do |k,v| # add escalation information for new group escalations = 0 v.each do |el| if el[:sg_name] == sg1_name or el[:sg_name] == sg2_name escalations += el[:escalations] end end v << { :sg_name => new_sg_name, :escalations => escalations } # remove old escalation information v.delete_if {|el| el[:sg_name] == sg1_name or el[:sg_name] == sg2_name } # recalculate normalized probabilities normalize_probabilities(v) end # update @sg_names @sg_names[@sg_names.index(sg1_name)] = new_name @sg_names.delete(sg2_name) # recalculate escalations from new sg total_escalation_info = sg1_probs + sg2_probs @transition_probabilities[new_sg_name] = [] [ @sg_names, "Out" ].flatten!.each do |name| escalations = total_escalation_info.inject(0) do |sum,el| sum + (el[:sg_name] == name ? el[:escalations] : 0) end if escalations > 0 @transition_probabilities[new_sg_name] << { :sg_name => name, :escalations => escalations } end end # recalculate normalized probabilities normalize_probabilities(@transition_probabilities[new_sg_name]) end
to_s()
click to toggle source
# File lib/symian/transition_matrix.rb, line 140 def to_s lines = [ "From/To,#{@sg_names.join(',')},Out" ] [ "In", *@sg_names ].each do |input_sg| escalations = [ @sg_names, "Out" ].flatten!.map do |output_sg| @transition_probabilities[input_sg].map{|x| x[:sg_name] == output_sg ? x[:escalations] : nil }.compact.first || 0 end lines << "#{input_sg},#{escalations.join(',')}" end lines.join("\n") end
Private Instance Methods
normalize_probabilities(probability_vector)
click to toggle source
# File lib/symian/transition_matrix.rb, line 153 def normalize_probabilities(probability_vector) # calculate total escalations total_escalations = probability_vector.inject(0) { |sum,el| sum += el[:escalations] } # probability values are cumulative cumulative_escalations = 0 probability_vector.each do |el| cumulative_escalations += el[:escalations] el[:probability] = cumulative_escalations.to_f / total_escalations.to_f end # just in case... probability_vector[-1][:probability] = 1.0 end