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