Class: Symian::TransitionMatrix
- Inherits:
-
Object
- Object
- Symian::TransitionMatrix
- Defined in:
- lib/symian/transition_matrix.rb
Instance Attribute Summary collapse
-
#transition_probabilities ⇒ Object
readonly
this is mostly for testing purposes.
Instance Method Summary collapse
- #escalation(from) ⇒ Object
-
#initialize(input) ⇒ TransitionMatrix
constructor
A new instance of TransitionMatrix.
- #merge(sg1_name, sg2_name, new_name = nil) ⇒ Object
- #to_s ⇒ Object
Constructor Details
#initialize(input) ⇒ TransitionMatrix
Returns a new instance of TransitionMatrix.
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
# 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 |
Instance Attribute Details
#transition_probabilities ⇒ Object (readonly)
this is mostly for testing purposes
11 12 13 |
# File 'lib/symian/transition_matrix.rb', line 11 def transition_probabilities @transition_probabilities end |
Instance Method Details
#escalation(from) ⇒ Object
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
# 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) ⇒ Object
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
# 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 ⇒ Object
140 141 142 143 144 145 146 147 148 149 |
# 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 |