Req: a function that indexes all Markov matrices

Hi @amiika, good idea to code the transitions a->b into the number as ab and increase probabilities for a transition through repetition.

But your initialization and normalization does not allow for matrices with 0’s in it. According to my experience, those 0’s are important to exclude transitions. Therefore, I would suggest to initialize the matrix with 0’s:

mm = Array.new(length) { Array.new(length, 0.0) } if mm.is_a?(Integer)

and a modified normalization function. Another aspect is how to deal with matrix dimensions > 9. I think this would require a string as code instead of just a large integer, for example

"1-2-3-10-11-1-2-1"

(or any other character marking the transition).
Here is an example on how it could work (without music, just the helper functions)

# matrix creation and progression

define :to_mm do |idx, mm=8|
  # Length of the markov matrix
  length = mm.is_a?(Integer) ? mm : mm.length
  # Init with random matrix if mm=integer
  mm = Array.new(length) { Array.new(length, 0.0) } if mm.is_a?(Integer)
  # Treat integer as a markov chain: 121 = 1->2, 2->1
  degrees = idx.split("-")
  degrees.push(degrees[0]) if degrees.length==1
  degrees.each_with_index do |d,i|
    if degrees[i+1]
      # Overflow depending on mm length: 8->0, 9->1, 0->2
      row = (d.to_i==0 ? length : d.to_i-1)%length
      column = (degrees[(i+1)].to_i-1)%length
      mm[row][column] += 1.0
    end
  end
  # Normalize the resulted matrix
  normalize mm
end

define :print_matrix do |mm|
  mm.each do |row|
    r = ""
    row.each do |c|
      r += "#{c.round(1)} "
    end
    puts r
  end
end


define :normalize do |mm|
  mm.length.times do |row|
    pp = 0.0
    mm[row].each do |p|
      pp += p
    end
    mm[row].length.times do |i|
      if pp == 0.0 then
        puts "warning: no transition defined for row #{row+1}!" if i == 0
        mm[row][i] = 1.0/mm[row].length
      else
        mm[row][i] /= pp
      end
    end
  end
  mm
end

# function to get next index according to markov matrix mm
define :next_idx do |current_idx, mm|
  n = 0
  r = rand
  pp = 0
  mm[current_idx].each do |p|
    pp += p
    break if pp > r
    n += 1
  end
  return n
end

mm1 = to_mm "1-2-10-2-3-2", 10
print_matrix mm1

EDIT: Just used this new code here: Markov chains for beginners - Part 3