lib/classifier/extensions/vector.rb in classifier-1.3.5 vs lib/classifier/extensions/vector.rb in classifier-1.4.0

- old
+ new

@@ -2,111 +2,105 @@ # Copyright:: Copyright (c) 2005 # These are extensions to the std-lib 'matrix' to allow an all ruby SVD require 'matrix' -require 'mathn' class Array - def sum(identity = 0, &block) - return identity unless size > 0 + def sum_with_identity(identity = 0.0, &block) + return identity unless size.to_i.positive? if block_given? - map(&block).sum + map(&block).sum_with_identity(identity) else - reduce(:+) + compact.reduce(:+).to_f || identity.to_f end end end -class Vector +module VectorExtensions def magnitude - sumsqs = 0.0 - self.size.times do |i| - sumsqs += self[i] ** 2.0 + sum_of_squares = 0.to_r + size.times do |i| + sum_of_squares += self[i]**2.to_r end - Math.sqrt(sumsqs) + Math.sqrt(sum_of_squares.to_f) end - def normalize - nv = [] - mag = self.magnitude - self.size.times do |i| - nv << (self[i] / mag) - + def normalize + normalized_values = [] + magnitude_value = magnitude.to_r + size.times do |i| + normalized_values << (self[i] / magnitude_value) end - Vector[*nv] + Vector[*normalized_values] end end +class Vector + include VectorExtensions +end + class Matrix - def Matrix.diag(s) - Matrix.diagonal(*s) + def self.diag(diagonal_elements) + Matrix.diagonal(*diagonal_elements) end - alias :trans :transpose + alias trans transpose - def SV_decomp(maxSweeps = 20) - if self.row_size >= self.column_size - q = self.trans * self - else - q = self * self.trans - end + def SV_decomp(max_sweeps = 20) + q_matrix = if row_size >= column_size + trans * self + else + self * trans + end - qrot = q.dup - v = Matrix.identity(q.row_size) - azrot = nil - mzrot = nil - cnt = 0 - s_old = nil - mu = nil + q_rotation_matrix = q_matrix.dup + v_matrix = Matrix.identity(q_matrix.row_size) + iteration_count = 0 + previous_s_matrix = nil - while true do - cnt += 1 - for row in (0...qrot.row_size-1) do - for col in (1..qrot.row_size-1) do + loop do + iteration_count += 1 + (0...q_rotation_matrix.row_size - 1).each do |row| + (1..q_rotation_matrix.row_size - 1).each do |col| next if row == col - h = Math.atan((2 * qrot[row,col])/(qrot[row,row]-qrot[col,col]))/2.0 - hcos = Math.cos(h) - hsin = Math.sin(h) - mzrot = Matrix.identity(qrot.row_size) - mzrot[row,row] = hcos - mzrot[row,col] = -hsin - mzrot[col,row] = hsin - mzrot[col,col] = hcos - qrot = mzrot.trans * qrot * mzrot - v = v * mzrot + + angle = Math.atan((2.to_r * q_rotation_matrix[row, + col]) / (q_rotation_matrix[row, + row] - q_rotation_matrix[col, + col])) / 2.0 + cosine = Math.cos(angle) + sine = Math.sin(angle) + rotation_matrix = Matrix.identity(q_rotation_matrix.row_size) + rotation_matrix[row, row] = cosine + rotation_matrix[row, col] = -sine + rotation_matrix[col, row] = sine + rotation_matrix[col, col] = cosine + q_rotation_matrix = rotation_matrix.trans * q_rotation_matrix * rotation_matrix + v_matrix *= rotation_matrix end end - s_old = qrot.dup if cnt == 1 - sum_qrot = 0.0 - if cnt > 1 - qrot.row_size.times do |r| - sum_qrot += (qrot[r,r]-s_old[r,r]).abs if (qrot[r,r]-s_old[r,r]).abs > 0.001 + previous_s_matrix = q_rotation_matrix.dup if iteration_count == 1 + sum_of_differences = 0.to_r + if iteration_count > 1 + q_rotation_matrix.row_size.times do |r| + difference = (q_rotation_matrix[r, r] - previous_s_matrix[r, r]).abs + sum_of_differences += difference.to_r if difference > 0.001 end - s_old = qrot.dup + previous_s_matrix = q_rotation_matrix.dup end - break if (sum_qrot <= 0.001 and cnt > 1) or cnt >= maxSweeps - end # of do while true - s = [] - qrot.row_size.times do |r| - s << Math.sqrt(qrot[r,r]) + break if (sum_of_differences <= 0.001 && iteration_count > 1) || iteration_count >= max_sweeps end - #puts "cnt = #{cnt}" - if self.row_size >= self.column_size - mu = self * v * Matrix.diagonal(*s).inverse - return [mu, v, s] - else - puts v.row_size - puts v.column_size - puts self.row_size - puts self.column_size - puts s.size - mu = (self.trans * v * Matrix.diagonal(*s).inverse) - return [mu, v, s] + singular_values = [] + q_rotation_matrix.row_size.times do |r| + singular_values << Math.sqrt(q_rotation_matrix[r, r].to_f) end + u_matrix = (row_size >= column_size ? self : trans) * v_matrix * Matrix.diagonal(*singular_values).inverse + [u_matrix, v_matrix, singular_values] end - def []=(i,j,val) - @rows[i][j] = val + + def []=(row_index, col_index, value) + @rows[row_index][col_index] = value end end