1999-10-12 00:53:36 -04:00
|
|
|
#!/usr/local/bin/ruby
|
1998-01-16 07:13:05 -05:00
|
|
|
#
|
|
|
|
# matrix.rb -
|
|
|
|
# $Release Version: 1.0$
|
1999-10-12 00:53:36 -04:00
|
|
|
# $Revision: 1.11 $
|
|
|
|
# $Date: 1999/10/06 11:01:53 $
|
1998-01-16 07:13:05 -05:00
|
|
|
# Original Version from Smalltalk-80 version
|
|
|
|
# on July 23, 1985 at 8:37:17 am
|
|
|
|
# by Keiju ISHITSUKA
|
|
|
|
#
|
|
|
|
# --
|
|
|
|
#
|
|
|
|
# Matrix[[1,2,3],
|
|
|
|
# :
|
|
|
|
# [3,4,5]]
|
|
|
|
# Matrix[row0,
|
|
|
|
# row1,
|
|
|
|
# :
|
|
|
|
# rown]
|
|
|
|
#
|
|
|
|
#
|
1999-01-19 23:59:39 -05:00
|
|
|
# module ExceptionForMatrix::
|
|
|
|
# Exceptions:
|
|
|
|
# ErrDimensionMismatch
|
|
|
|
# number of column/row do not match
|
|
|
|
# ErrNotRegular
|
|
|
|
# not a regular matrix
|
|
|
|
# ErrOperationNotDefined
|
|
|
|
# specified operator is not defined (yet)
|
|
|
|
#
|
|
|
|
# class Matrix
|
|
|
|
# include ExceptionForMatrix
|
|
|
|
#
|
|
|
|
# Methods:
|
|
|
|
# class methods:
|
|
|
|
# Matrix.[](*rows)
|
|
|
|
# creates a matrix where `rows' indicates rows.
|
|
|
|
# `rows' is an array of arrays,
|
|
|
|
# e.g, Matrix[[11, 12], [21, 22]]
|
2000-07-10 00:49:24 -04:00
|
|
|
# Matrix.rows(rows, copy = true)
|
1999-01-19 23:59:39 -05:00
|
|
|
# creates a matrix where `rows' indicates rows.
|
|
|
|
# if optional argument `copy' is false, use the array as
|
|
|
|
# internal structure of the metrix without copying.
|
|
|
|
# Matrix.columns(columns)
|
|
|
|
# creates a new matrix using `columns` as set of colums vectors.
|
|
|
|
# Matrix.diagonal(*values)
|
|
|
|
# creates a matrix where `columns' indicates columns.
|
|
|
|
# Matrix.scalar(n, value)
|
|
|
|
# creates a diagonal matrix such that the diagal compornents is
|
|
|
|
# given by `values'.
|
|
|
|
# Matrix.scalar(n, value)
|
|
|
|
# creates an n-by-n scalar matrix such that the diagal compornent is
|
|
|
|
# given by `value'.
|
|
|
|
# Matrix.identity(n)
|
|
|
|
# Matrix.unit(n)
|
|
|
|
# Matrix.I(n)
|
|
|
|
# creates an n-by-n unit matrix.
|
|
|
|
# Matrix.zero(n)
|
|
|
|
# creates an n-by-n zero matrix.
|
|
|
|
# Matrix.row_vector(row)
|
|
|
|
# creates a 1-by-n matrix such the row vector is `row'.
|
|
|
|
# `row' is specifed as a Vector or an Array.
|
|
|
|
# Matrix.column_vector(column)
|
|
|
|
# creates a 1-by-n matrix such that column vector is `column'.
|
|
|
|
# `column' is specifed as a Vector or an Array.
|
|
|
|
# accessing:
|
|
|
|
# [](i, j)
|
|
|
|
# returns (i,j) compornent
|
|
|
|
# row_size
|
|
|
|
# returns the number of rows
|
|
|
|
# column_size
|
|
|
|
# returns the number of columns
|
|
|
|
# row(i)
|
|
|
|
# returns the i-th row vector.
|
|
|
|
# when the block is supplied for the method, the block is iterated
|
|
|
|
# over all row vectors.
|
|
|
|
# column(j)
|
|
|
|
# returns the jth column vector.
|
|
|
|
# when the block is supplied for the method, the block is iterated
|
|
|
|
# over all column vectors.
|
|
|
|
# collect
|
|
|
|
# map
|
|
|
|
# creates a matrix which is the result of iteration of given
|
|
|
|
# block over all compornents.
|
|
|
|
# minor(*param)
|
|
|
|
# returns sub matrix. parameter is specified as the following:
|
|
|
|
# 1. from_row, row_size, from_col, size_col
|
|
|
|
# 2. from_row..to_row, from_col..to_col
|
|
|
|
# TESTING:
|
|
|
|
# regular?
|
|
|
|
# Is regular?
|
|
|
|
# singular?
|
|
|
|
# Is singular? i.e. Is non-regular?
|
|
|
|
# square?
|
|
|
|
# Is square?
|
|
|
|
# ARITHMETIC:
|
|
|
|
# *(m)
|
|
|
|
# times
|
|
|
|
# +(m)
|
|
|
|
# plus
|
|
|
|
# -(m)
|
|
|
|
# minus
|
|
|
|
# /(m)
|
|
|
|
# self * m.inv
|
|
|
|
# inverse
|
|
|
|
# inv
|
|
|
|
# inverse
|
|
|
|
# **
|
|
|
|
# power
|
|
|
|
# Matrix functions:
|
|
|
|
# determinant
|
|
|
|
# det
|
|
|
|
# returns the determinant
|
|
|
|
# rank
|
|
|
|
# returns the rank
|
|
|
|
# trace
|
|
|
|
# tr
|
|
|
|
# returns the trace
|
|
|
|
# transpose
|
|
|
|
# t
|
|
|
|
# returns the transposed
|
|
|
|
# CONVERTING:
|
|
|
|
# coerce(other)
|
|
|
|
# row_vectors
|
|
|
|
# array of row vectors
|
|
|
|
# column_vectors
|
|
|
|
# array of column vectors
|
|
|
|
# to_a
|
|
|
|
# converts each element to Array
|
|
|
|
# to_f
|
|
|
|
# converts each element to Float
|
|
|
|
# to_i
|
|
|
|
# converts each element to Integer
|
|
|
|
# to_r
|
|
|
|
# converts each element to Rational
|
|
|
|
# PRINTING:
|
|
|
|
# to_s
|
|
|
|
# returns string representation
|
|
|
|
# inspect
|
|
|
|
#
|
|
|
|
# class Vector
|
|
|
|
# include ExceptionForMatrix
|
|
|
|
#
|
|
|
|
# INSTANCE CREATION:
|
|
|
|
# Vector.[](*array)
|
2000-07-10 00:49:24 -04:00
|
|
|
# Vector.elements(array, copy = true)
|
1999-01-19 23:59:39 -05:00
|
|
|
# ACCSESSING:
|
|
|
|
# [](i)
|
|
|
|
# size
|
|
|
|
# ENUMRATIONS:
|
|
|
|
# each2(v)
|
|
|
|
# collect2(v)
|
|
|
|
# ARITHMETIC:
|
|
|
|
# *(x) "is matrix or number"
|
|
|
|
# +(v)
|
|
|
|
# -(v)
|
|
|
|
# VECTOR FUNCTIONS:
|
|
|
|
# inner_product(v)
|
|
|
|
# collect
|
|
|
|
# map
|
|
|
|
# map2(v)
|
|
|
|
# r
|
|
|
|
# CONVERTING:
|
|
|
|
# covector
|
|
|
|
# to_a
|
|
|
|
# to_f
|
|
|
|
# to_i
|
|
|
|
# to_r
|
|
|
|
# coerce(other)
|
|
|
|
# PRINTING:
|
|
|
|
# to_s
|
|
|
|
# inspect
|
1998-01-16 07:13:05 -05:00
|
|
|
|
|
|
|
require "e2mmap.rb"
|
|
|
|
|
|
|
|
module ExceptionForMatrix
|
1999-08-13 01:45:20 -04:00
|
|
|
extend Exception2MessageMapper
|
1998-01-16 07:13:05 -05:00
|
|
|
def_e2message(TypeError, "wrong argument type %s (expected %s)")
|
|
|
|
def_e2message(ArgumentError, "Wrong # of arguments(%d for %d)")
|
|
|
|
|
1999-08-13 01:45:20 -04:00
|
|
|
def_exception("ErrDimensionMismatch", "\#{self.name} dimension mismatch")
|
1998-01-16 07:13:05 -05:00
|
|
|
def_exception("ErrNotRegular", "Not Regular Matrix")
|
|
|
|
def_exception("ErrOperationNotDefined", "This operation(%s) can\\'t defined")
|
|
|
|
end
|
|
|
|
|
|
|
|
class Matrix
|
1999-10-12 00:53:36 -04:00
|
|
|
@RCS_ID='-$Id: matrix.rb,v 1.11 1999/10/06 11:01:53 keiju Exp keiju $-'
|
1999-08-13 01:45:20 -04:00
|
|
|
|
|
|
|
# extend Exception2MessageMapper
|
1998-01-16 07:13:05 -05:00
|
|
|
include ExceptionForMatrix
|
|
|
|
|
|
|
|
# instance creations
|
|
|
|
private_class_method :new
|
|
|
|
|
|
|
|
def Matrix.[](*rows)
|
2000-07-10 00:49:24 -04:00
|
|
|
new(:init_rows, rows, false)
|
1998-01-16 07:13:05 -05:00
|
|
|
end
|
|
|
|
|
2000-07-10 00:49:24 -04:00
|
|
|
def Matrix.rows(rows, copy = true)
|
1998-01-16 07:13:05 -05:00
|
|
|
new(:init_rows, rows, copy)
|
|
|
|
end
|
|
|
|
|
|
|
|
def Matrix.columns(columns)
|
|
|
|
rows = (0 .. columns[0].size - 1).collect {
|
|
|
|
|i|
|
|
|
|
(0 .. columns.size - 1).collect {
|
|
|
|
|j|
|
|
|
|
columns[j][i]
|
|
|
|
}
|
|
|
|
}
|
2000-07-10 00:49:24 -04:00
|
|
|
Matrix.rows(rows, false)
|
1998-01-16 07:13:05 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def Matrix.diagonal(*values)
|
|
|
|
size = values.size
|
|
|
|
rows = (0 .. size - 1).collect {
|
|
|
|
|j|
|
|
|
|
row = Array.new(size).fill(0, 0, size)
|
|
|
|
row[j] = values[j]
|
|
|
|
row
|
|
|
|
}
|
1999-10-12 00:53:36 -04:00
|
|
|
self
|
2000-07-10 00:49:24 -04:00
|
|
|
rows(rows, false)
|
1998-01-16 07:13:05 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def Matrix.scalar(n, value)
|
|
|
|
Matrix.diagonal(*Array.new(n).fill(value, 0, n))
|
|
|
|
end
|
|
|
|
|
|
|
|
def Matrix.identity(n)
|
|
|
|
Matrix.scalar(n, 1)
|
|
|
|
end
|
|
|
|
class << Matrix
|
|
|
|
alias unit identity
|
|
|
|
alias I identity
|
|
|
|
end
|
|
|
|
|
|
|
|
def Matrix.zero(n)
|
|
|
|
Matrix.scalar(n, 0)
|
|
|
|
end
|
|
|
|
|
|
|
|
def Matrix.row_vector(row)
|
|
|
|
case row
|
|
|
|
when Vector
|
2000-07-10 00:49:24 -04:00
|
|
|
Matrix.rows([row.to_a], false)
|
1998-01-16 07:13:05 -05:00
|
|
|
when Array
|
2000-07-10 00:49:24 -04:00
|
|
|
Matrix.rows([row.dup], false)
|
1998-01-16 07:13:05 -05:00
|
|
|
else
|
2000-07-10 00:49:24 -04:00
|
|
|
Matrix.row([[row]], false)
|
1998-01-16 07:13:05 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def Matrix.column_vector(column)
|
|
|
|
case column
|
|
|
|
when Vector
|
|
|
|
Matrix.columns([column.to_a])
|
|
|
|
when Array
|
|
|
|
Matrix.columns([column])
|
|
|
|
else
|
|
|
|
Matrix.columns([[column]])
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# initializing
|
|
|
|
def initialize(init_method, *argv)
|
|
|
|
self.send(init_method, *argv)
|
|
|
|
end
|
|
|
|
|
|
|
|
def init_rows(rows, copy)
|
|
|
|
if copy
|
|
|
|
@rows = rows.collect{|row| row.dup}
|
|
|
|
else
|
|
|
|
@rows = rows
|
|
|
|
end
|
|
|
|
self
|
|
|
|
end
|
|
|
|
private :init_rows
|
|
|
|
|
|
|
|
#accessing
|
|
|
|
def [](i, j)
|
|
|
|
@rows[i][j]
|
|
|
|
end
|
|
|
|
|
|
|
|
def row_size
|
|
|
|
@rows.size
|
|
|
|
end
|
|
|
|
|
|
|
|
def column_size
|
|
|
|
@rows[0].size
|
|
|
|
end
|
|
|
|
|
|
|
|
def row(i)
|
2000-06-28 04:31:35 -04:00
|
|
|
if block_given?
|
1998-01-16 07:13:05 -05:00
|
|
|
for e in @rows[i]
|
|
|
|
yield e
|
1999-01-19 23:59:39 -05:00
|
|
|
|
1998-01-16 07:13:05 -05:00
|
|
|
end
|
|
|
|
else
|
|
|
|
Vector.elements(@rows[i])
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def column(j)
|
2000-06-28 04:31:35 -04:00
|
|
|
if block_given?
|
1998-01-16 07:13:05 -05:00
|
|
|
0.upto(row_size - 1) do
|
|
|
|
|i|
|
|
|
|
yield @rows[i][j]
|
|
|
|
end
|
|
|
|
else
|
|
|
|
col = (0 .. row_size - 1).collect {
|
|
|
|
|i|
|
|
|
|
@rows[i][j]
|
|
|
|
}
|
2000-07-10 00:49:24 -04:00
|
|
|
Vector.elements(col, false)
|
1998-01-16 07:13:05 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def collect
|
|
|
|
rows = @rows.collect{|row| row.collect{|e| yield e}}
|
2000-07-10 00:49:24 -04:00
|
|
|
Matrix.rows(rows, false)
|
1998-01-16 07:13:05 -05:00
|
|
|
end
|
|
|
|
alias map collect
|
|
|
|
|
|
|
|
#
|
|
|
|
# param: (from_row, row_size, from_col, size_col)
|
|
|
|
# (from_row..to_row, from_col..to_col)
|
|
|
|
#
|
|
|
|
def minor(*param)
|
|
|
|
case param.size
|
|
|
|
when 2
|
|
|
|
from_row = param[0].first
|
|
|
|
size_row = param[0].size
|
|
|
|
from_col = param[1].first
|
|
|
|
size_col = param[1].size
|
|
|
|
when 4
|
|
|
|
from_row = param[0]
|
|
|
|
size_row = param[1]
|
|
|
|
from_col = param[2]
|
|
|
|
size_col = param[3]
|
|
|
|
else
|
1999-08-13 01:45:20 -04:00
|
|
|
Matrix.Raise ArgumentError, param.inspect
|
1998-01-16 07:13:05 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
rows = @rows[from_row, size_row].collect{
|
|
|
|
|row|
|
|
|
|
row[from_col, size_col]
|
|
|
|
}
|
2000-07-10 00:49:24 -04:00
|
|
|
Matrix.rows(rows, false)
|
1998-01-16 07:13:05 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
# TESTING
|
|
|
|
def regular?
|
|
|
|
square? and rank == column_size
|
|
|
|
end
|
|
|
|
|
|
|
|
def singular?
|
|
|
|
not regular?
|
|
|
|
end
|
|
|
|
|
|
|
|
def square?
|
|
|
|
column_size == row_size
|
|
|
|
end
|
|
|
|
|
1999-01-19 23:59:39 -05:00
|
|
|
# COMPARING
|
|
|
|
def ==(other)
|
2000-07-10 00:49:24 -04:00
|
|
|
return false unless Matrix === other
|
1999-01-19 23:59:39 -05:00
|
|
|
|
|
|
|
other.compare_by_row_vectors(@rows)
|
|
|
|
end
|
|
|
|
alias eql? ==
|
|
|
|
|
|
|
|
def compare_by_row_vectors(rows)
|
2000-07-10 00:49:24 -04:00
|
|
|
return false unless @rows.size == rows.size
|
1999-01-19 23:59:39 -05:00
|
|
|
|
|
|
|
0.upto(@rows.size - 1) do
|
|
|
|
|i|
|
2000-07-10 00:49:24 -04:00
|
|
|
return false unless @rows[i] == rows[i]
|
1999-01-19 23:59:39 -05:00
|
|
|
end
|
2000-07-10 00:49:24 -04:00
|
|
|
true
|
1999-01-19 23:59:39 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def clone
|
|
|
|
Matrix.rows(@rows)
|
|
|
|
end
|
|
|
|
|
|
|
|
def hash
|
|
|
|
value = 0
|
|
|
|
for row in @rows
|
|
|
|
for e in row
|
|
|
|
value ^= e.hash
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return value
|
|
|
|
end
|
|
|
|
|
1998-01-16 07:13:05 -05:00
|
|
|
# ARITHMETIC
|
|
|
|
|
|
|
|
def *(m) #is matrix or vector or number"
|
|
|
|
case(m)
|
|
|
|
when Numeric
|
|
|
|
rows = @rows.collect {
|
|
|
|
|row|
|
|
|
|
row.collect {
|
|
|
|
|e|
|
|
|
|
e * m
|
|
|
|
}
|
|
|
|
}
|
2000-07-10 00:49:24 -04:00
|
|
|
return Matrix.rows(rows, false)
|
1998-01-16 07:13:05 -05:00
|
|
|
when Vector
|
|
|
|
m = Matrix.column_vector(m)
|
|
|
|
r = self * m
|
|
|
|
return r.column(0)
|
|
|
|
when Matrix
|
1999-08-13 01:45:20 -04:00
|
|
|
Matrix.Raise ErrDimensionMismatch if column_size != m.row_size
|
1998-01-16 07:13:05 -05:00
|
|
|
|
|
|
|
rows = (0 .. row_size - 1).collect {
|
|
|
|
|i|
|
|
|
|
(0 .. m.column_size - 1).collect {
|
|
|
|
|j|
|
|
|
|
vij = 0
|
|
|
|
0.upto(column_size - 1) do
|
|
|
|
|k|
|
|
|
|
vij += self[i, k] * m[k, j]
|
|
|
|
end
|
|
|
|
vij
|
|
|
|
}
|
|
|
|
}
|
2000-07-10 00:49:24 -04:00
|
|
|
return Matrix.rows(rows, false)
|
1998-01-16 07:13:05 -05:00
|
|
|
else
|
|
|
|
x, y = m.coerce(self)
|
|
|
|
return x * y
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def +(m)
|
|
|
|
case m
|
|
|
|
when Numeric
|
1999-08-13 01:45:20 -04:00
|
|
|
Matrix.Raise ErrOperationNotDefined, "+"
|
1998-01-16 07:13:05 -05:00
|
|
|
when Vector
|
|
|
|
m = Matrix.column_vector(m)
|
|
|
|
when Matrix
|
|
|
|
else
|
|
|
|
x, y = m.coerce(self)
|
|
|
|
return x + y
|
|
|
|
end
|
|
|
|
|
1999-08-13 01:45:20 -04:00
|
|
|
Matrix.Raise ErrDimensionMismatch unless row_size == m.row_size and column_size == m.column_size
|
1998-01-16 07:13:05 -05:00
|
|
|
|
|
|
|
rows = (0 .. row_size - 1).collect {
|
|
|
|
|i|
|
|
|
|
(0 .. column_size - 1).collect {
|
|
|
|
|j|
|
|
|
|
self[i, j] + m[i, j]
|
|
|
|
}
|
|
|
|
}
|
2000-07-10 00:49:24 -04:00
|
|
|
Matrix.rows(rows, false)
|
1998-01-16 07:13:05 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def -(m)
|
|
|
|
case m
|
|
|
|
when Numeric
|
1999-08-13 01:45:20 -04:00
|
|
|
Matrix.Raise ErrOperationNotDefined, "-"
|
1998-01-16 07:13:05 -05:00
|
|
|
when Vector
|
|
|
|
m = Matrix.column_vector(m)
|
|
|
|
when Matrix
|
|
|
|
else
|
|
|
|
x, y = m.coerce(self)
|
|
|
|
return x - y
|
|
|
|
end
|
|
|
|
|
1999-08-13 01:45:20 -04:00
|
|
|
Matrix.Raise ErrDimensionMismatch unless row_size == m.row_size and column_size == m.column_size
|
1998-01-16 07:13:05 -05:00
|
|
|
|
|
|
|
rows = (0 .. row_size - 1).collect {
|
|
|
|
|i|
|
|
|
|
(0 .. column_size - 1).collect {
|
|
|
|
|j|
|
|
|
|
self[i, j] - m[i, j]
|
|
|
|
}
|
|
|
|
}
|
2000-07-10 00:49:24 -04:00
|
|
|
Matrix.rows(rows, false)
|
1998-01-16 07:13:05 -05:00
|
|
|
end
|
1999-01-19 23:59:39 -05:00
|
|
|
|
|
|
|
def /(other)
|
|
|
|
case other
|
|
|
|
when Numeric
|
|
|
|
rows = @rows.collect {
|
|
|
|
|row|
|
|
|
|
row.collect {
|
|
|
|
|e|
|
|
|
|
e / other
|
|
|
|
}
|
|
|
|
}
|
2000-07-10 00:49:24 -04:00
|
|
|
return Matrix.rows(rows, false)
|
1999-01-19 23:59:39 -05:00
|
|
|
when Matrix
|
|
|
|
return self * other.inverse
|
|
|
|
else
|
|
|
|
x, y = other.coerce(self)
|
|
|
|
rerurn x / y
|
|
|
|
end
|
|
|
|
end
|
1998-01-16 07:13:05 -05:00
|
|
|
|
|
|
|
def inverse
|
1999-08-13 01:45:20 -04:00
|
|
|
Matrix.Raise ErrDimensionMismatch unless square?
|
1998-01-16 07:13:05 -05:00
|
|
|
Matrix.I(row_size).inverse_from(self)
|
|
|
|
end
|
|
|
|
alias inv inverse
|
|
|
|
|
|
|
|
def inverse_from(src)
|
|
|
|
size = row_size - 1
|
|
|
|
a = src.to_a
|
|
|
|
|
|
|
|
for k in 0..size
|
|
|
|
if (akk = a[k][k]) == 0
|
|
|
|
i = k
|
|
|
|
begin
|
1999-08-13 01:45:20 -04:00
|
|
|
Matrix.Raise ErrNotRegular if (i += 1) > size
|
1998-01-16 07:13:05 -05:00
|
|
|
end while a[i][k] == 0
|
|
|
|
a[i], a[k] = a[k], a[i]
|
|
|
|
@rows[i], @rows[k] = @rows[k], @rows[i]
|
|
|
|
akk = a[k][k]
|
|
|
|
end
|
|
|
|
|
|
|
|
for i in 0 .. size
|
|
|
|
next if i == k
|
|
|
|
q = a[i][k] / akk
|
|
|
|
a[i][k] = 0
|
|
|
|
|
|
|
|
(k + 1).upto(size) do
|
|
|
|
|j|
|
|
|
|
a[i][j] -= a[k][j] * q
|
|
|
|
end
|
|
|
|
0.upto(size) do
|
|
|
|
|j|
|
|
|
|
@rows[i][j] -= @rows[k][j] * q
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
(k + 1).upto(size) do
|
|
|
|
|j|
|
|
|
|
a[k][j] /= akk
|
|
|
|
end
|
|
|
|
0.upto(size) do
|
|
|
|
|j|
|
|
|
|
@rows[k][j] /= akk
|
|
|
|
end
|
|
|
|
end
|
|
|
|
self
|
|
|
|
end
|
|
|
|
#alias reciprocal inverse
|
|
|
|
|
|
|
|
def ** (other)
|
|
|
|
if other.kind_of?(Integer)
|
|
|
|
x = self
|
|
|
|
if other <= 0
|
|
|
|
x = self.inverse
|
|
|
|
return Matrix.identity(self.column_size) if other == 0
|
|
|
|
other = -other
|
|
|
|
end
|
|
|
|
z = x
|
|
|
|
n = other - 1
|
|
|
|
while n != 0
|
|
|
|
while (div, mod = n.divmod(2)
|
|
|
|
mod == 0)
|
|
|
|
x = x * x
|
|
|
|
n = div
|
|
|
|
end
|
|
|
|
z *= x
|
|
|
|
n -= 1
|
|
|
|
end
|
|
|
|
z
|
|
|
|
elsif other.kind_of?(Float) || defined?(Rational) && other.kind_of?(Rational)
|
1999-08-13 01:45:20 -04:00
|
|
|
Matrix.Raise ErrOperationNotDefined, "**"
|
1998-01-16 07:13:05 -05:00
|
|
|
else
|
1999-08-13 01:45:20 -04:00
|
|
|
Matrix.Raise ErrOperationNotDefined, "**"
|
1998-01-16 07:13:05 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# Matrix functions
|
|
|
|
|
|
|
|
def determinant
|
|
|
|
return 0 unless square?
|
|
|
|
|
|
|
|
size = row_size - 1
|
|
|
|
a = to_a
|
|
|
|
|
|
|
|
det = 1
|
|
|
|
k = 0
|
|
|
|
begin
|
|
|
|
if (akk = a[k][k]) == 0
|
|
|
|
i = k
|
|
|
|
begin
|
|
|
|
return 0 if (i += 1) > size
|
|
|
|
end while a[i][k] == 0
|
|
|
|
a[i], a[k] = a[k], a[i]
|
|
|
|
akk = a[k][k]
|
1999-09-01 05:48:03 -04:00
|
|
|
det *= -1
|
1998-01-16 07:13:05 -05:00
|
|
|
end
|
|
|
|
(k + 1).upto(size) do
|
|
|
|
|i|
|
|
|
|
q = a[i][k] / akk
|
|
|
|
(k + 1).upto(size) do
|
|
|
|
|j|
|
|
|
|
a[i][j] -= a[k][j] * q
|
|
|
|
end
|
|
|
|
end
|
|
|
|
det *= akk
|
|
|
|
end while (k += 1) <= size
|
|
|
|
det
|
|
|
|
end
|
|
|
|
alias det determinant
|
|
|
|
|
|
|
|
def rank
|
|
|
|
if column_size > row_size
|
|
|
|
a = transpose.to_a
|
2000-08-31 23:31:05 -04:00
|
|
|
a_column_size = row_size
|
|
|
|
a_row_size = column_size
|
1998-01-16 07:13:05 -05:00
|
|
|
else
|
|
|
|
a = to_a
|
2000-08-31 23:31:05 -04:00
|
|
|
a_column_size = column_size
|
|
|
|
a_row_size = row_size
|
1998-01-16 07:13:05 -05:00
|
|
|
end
|
|
|
|
rank = 0
|
|
|
|
k = 0
|
|
|
|
begin
|
|
|
|
if (akk = a[k][k]) == 0
|
1999-10-12 00:53:36 -04:00
|
|
|
i = k
|
|
|
|
exists = true
|
1998-01-16 07:13:05 -05:00
|
|
|
begin
|
2000-08-31 23:31:05 -04:00
|
|
|
if (i += 1) > a_column_size - 1
|
1999-10-12 00:53:36 -04:00
|
|
|
exists = false
|
1998-01-16 07:13:05 -05:00
|
|
|
break
|
|
|
|
end
|
|
|
|
end while a[i][k] == 0
|
1999-10-12 00:53:36 -04:00
|
|
|
if exists
|
|
|
|
a[i], a[k] = a[k], a[i]
|
|
|
|
akk = a[k][k]
|
|
|
|
else
|
|
|
|
i = k
|
|
|
|
exists = true
|
|
|
|
begin
|
2000-08-31 23:31:05 -04:00
|
|
|
if (i += 1) > a_row_size - 1
|
1999-10-12 00:53:36 -04:00
|
|
|
exists = false
|
|
|
|
break
|
|
|
|
end
|
|
|
|
end while a[k][i] == 0
|
|
|
|
if exists
|
2000-08-31 23:31:05 -04:00
|
|
|
k.upto(a_column_size - 1) do
|
1999-10-12 00:53:36 -04:00
|
|
|
|j|
|
|
|
|
a[j][k], a[j][i] = a[j][i], a[j][k]
|
|
|
|
end
|
|
|
|
akk = a[k][k]
|
|
|
|
else
|
|
|
|
next
|
|
|
|
end
|
|
|
|
end
|
1998-01-16 07:13:05 -05:00
|
|
|
end
|
2000-08-31 23:31:05 -04:00
|
|
|
(k + 1).upto(a_row_size - 1) do
|
1998-01-16 07:13:05 -05:00
|
|
|
|i|
|
|
|
|
q = a[i][k] / akk
|
2000-08-31 23:31:05 -04:00
|
|
|
(k + 1).upto(a_column_size - 1) do
|
1998-01-16 07:13:05 -05:00
|
|
|
|j|
|
|
|
|
a[i][j] -= a[k][j] * q
|
|
|
|
end
|
|
|
|
end
|
|
|
|
rank += 1
|
2000-08-31 23:31:05 -04:00
|
|
|
end while (k += 1) <= a_column_size - 1
|
1998-01-16 07:13:05 -05:00
|
|
|
return rank
|
|
|
|
end
|
|
|
|
|
|
|
|
def trace
|
|
|
|
tr = 0
|
|
|
|
0.upto(column_size - 1) do
|
|
|
|
|i|
|
|
|
|
tr += @rows[i][i]
|
|
|
|
end
|
|
|
|
tr
|
|
|
|
end
|
|
|
|
alias tr trace
|
|
|
|
|
|
|
|
def transpose
|
|
|
|
Matrix.columns(@rows)
|
|
|
|
end
|
|
|
|
alias t transpose
|
|
|
|
|
|
|
|
# CONVERTING
|
|
|
|
|
|
|
|
def coerce(other)
|
|
|
|
case other
|
|
|
|
when Numeric
|
|
|
|
return Scalar.new(other), self
|
1999-08-13 01:45:20 -04:00
|
|
|
else
|
|
|
|
raise TypeError, "#{type} can't be coerced into #{other.type}"
|
1998-01-16 07:13:05 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def row_vectors
|
|
|
|
rows = (0 .. column_size - 1).collect {
|
|
|
|
|i|
|
|
|
|
row(i)
|
|
|
|
}
|
|
|
|
rows
|
|
|
|
end
|
|
|
|
|
|
|
|
def column_vectors
|
|
|
|
columns = (0 .. row_size - 1).collect {
|
|
|
|
|i|
|
|
|
|
column(i)
|
|
|
|
}
|
|
|
|
columns
|
|
|
|
end
|
|
|
|
|
|
|
|
def to_a
|
|
|
|
@rows.collect{|row| row.collect{|e| e}}
|
|
|
|
end
|
|
|
|
|
|
|
|
def to_f
|
|
|
|
collect{|e| e.to_f}
|
|
|
|
end
|
|
|
|
|
|
|
|
def to_i
|
|
|
|
collect{|e| e.to_i}
|
|
|
|
end
|
|
|
|
|
|
|
|
def to_r
|
|
|
|
collect{|e| e.to_r}
|
|
|
|
end
|
|
|
|
|
|
|
|
# PRINTING
|
|
|
|
def to_s
|
|
|
|
"Matrix[" + @rows.collect{
|
|
|
|
|row|
|
|
|
|
"[" + row.collect{|e| e.to_s}.join(", ") + "]"
|
|
|
|
}.join(", ")+"]"
|
|
|
|
end
|
|
|
|
|
|
|
|
def inspect
|
|
|
|
"Matrix"+@rows.inspect
|
|
|
|
end
|
|
|
|
|
|
|
|
# Private CLASS
|
|
|
|
|
|
|
|
class Scalar < Numeric
|
|
|
|
include ExceptionForMatrix
|
|
|
|
|
|
|
|
def initialize(value)
|
|
|
|
@value = value
|
|
|
|
end
|
|
|
|
|
|
|
|
# ARITHMETIC
|
|
|
|
def +(other)
|
|
|
|
case other
|
|
|
|
when Numeric
|
|
|
|
Scalar.new(@value + other)
|
|
|
|
when Vector, Matrix
|
1999-08-13 01:45:20 -04:00
|
|
|
Scalar.Raise WrongArgType, other.type, "Numeric or Scalar"
|
1998-01-16 07:13:05 -05:00
|
|
|
when Scalar
|
|
|
|
Scalar.new(@value + other.value)
|
|
|
|
else
|
|
|
|
x, y = other.coerce(self)
|
|
|
|
x + y
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def -(other)
|
|
|
|
case other
|
|
|
|
when Numeric
|
|
|
|
Scalar.new(@value - other)
|
|
|
|
when Vector, Matrix
|
1999-08-13 01:45:20 -04:00
|
|
|
Scalar.Raise WrongArgType, other.type, "Numeric or Scalar"
|
1998-01-16 07:13:05 -05:00
|
|
|
when Scalar
|
|
|
|
Scalar.new(@value - other.value)
|
|
|
|
else
|
|
|
|
x, y = other.coerce(self)
|
|
|
|
x - y
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def *(other)
|
|
|
|
case other
|
|
|
|
when Numeric
|
|
|
|
Scalar.new(@value * other)
|
|
|
|
when Vector, Matrix
|
|
|
|
other.collect{|e| @value * e}
|
|
|
|
else
|
|
|
|
x, y = other.coerce(self)
|
|
|
|
x * y
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def / (other)
|
|
|
|
case other
|
|
|
|
when Numeric
|
|
|
|
Scalar.new(@value / other)
|
|
|
|
when Vector
|
1999-08-13 01:45:20 -04:00
|
|
|
Scalar.Raise WrongArgType, other.type, "Numeric or Scalar or Matrix"
|
1998-01-16 07:13:05 -05:00
|
|
|
when Matrix
|
|
|
|
self * _M.inverse
|
|
|
|
else
|
|
|
|
x, y = other.coerce(self)
|
|
|
|
x / y
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def ** (other)
|
|
|
|
case other
|
|
|
|
when Numeric
|
|
|
|
Scalar.new(@value ** other)
|
|
|
|
when Vector
|
1999-08-13 01:45:20 -04:00
|
|
|
Scalar.Raise WrongArgType, other.type, "Numeric or Scalar or Matrix"
|
1998-01-16 07:13:05 -05:00
|
|
|
when Matrix
|
|
|
|
other.powered_by(self)
|
|
|
|
else
|
|
|
|
x, y = other.coerce(self)
|
|
|
|
x ** y
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
#
|
|
|
|
# -
|
|
|
|
#
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
class Vector
|
|
|
|
include ExceptionForMatrix
|
|
|
|
|
|
|
|
#INSTANCE CREATION
|
|
|
|
|
|
|
|
private_class_method :new
|
|
|
|
def Vector.[](*array)
|
2000-07-10 00:49:24 -04:00
|
|
|
new(:init_elements, array, copy = false)
|
1998-01-16 07:13:05 -05:00
|
|
|
end
|
|
|
|
|
2000-07-10 00:49:24 -04:00
|
|
|
def Vector.elements(array, copy = true)
|
1998-01-16 07:13:05 -05:00
|
|
|
new(:init_elements, array, copy)
|
|
|
|
end
|
|
|
|
|
|
|
|
def initialize(method, array, copy)
|
|
|
|
self.send(method, array, copy)
|
|
|
|
end
|
|
|
|
|
|
|
|
def init_elements(array, copy)
|
|
|
|
if copy
|
|
|
|
@elements = array.dup
|
|
|
|
else
|
|
|
|
@elements = array
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# ACCSESSING
|
|
|
|
|
|
|
|
def [](i)
|
|
|
|
@elements[i]
|
|
|
|
end
|
|
|
|
|
|
|
|
def size
|
|
|
|
@elements.size
|
|
|
|
end
|
|
|
|
|
|
|
|
# ENUMRATIONS
|
|
|
|
def each2(v)
|
1999-08-13 01:45:20 -04:00
|
|
|
Vector.Raise ErrDimensionMismatch if size != v.size
|
1998-01-16 07:13:05 -05:00
|
|
|
0.upto(size - 1) do
|
|
|
|
|i|
|
|
|
|
yield @elements[i], v[i]
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def collect2(v)
|
1999-08-13 01:45:20 -04:00
|
|
|
Vector.Raise ErrDimensionMismatch if size != v.size
|
1998-01-16 07:13:05 -05:00
|
|
|
(0 .. size - 1).collect do
|
|
|
|
|i|
|
|
|
|
yield @elements[i], v[i]
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
1999-01-19 23:59:39 -05:00
|
|
|
# COMPARING
|
|
|
|
def ==(other)
|
2000-07-10 00:49:24 -04:00
|
|
|
return false unless Vector === other
|
1999-01-19 23:59:39 -05:00
|
|
|
|
|
|
|
other.compare_by(@elements)
|
|
|
|
end
|
|
|
|
alias eqn? ==
|
|
|
|
|
|
|
|
def compare_by(elements)
|
|
|
|
@elements == elements
|
|
|
|
end
|
|
|
|
|
|
|
|
def clone
|
|
|
|
Vector.elements(@elements)
|
|
|
|
end
|
|
|
|
|
|
|
|
def hash
|
|
|
|
@elements.hash
|
|
|
|
end
|
|
|
|
|
1998-01-16 07:13:05 -05:00
|
|
|
# ARITHMETIC
|
|
|
|
|
1999-10-12 00:53:36 -04:00
|
|
|
def *(x) "is matrix or number"
|
1998-01-16 07:13:05 -05:00
|
|
|
case x
|
|
|
|
when Numeric
|
|
|
|
els = @elements.collect{|e| e * x}
|
2000-07-10 00:49:24 -04:00
|
|
|
Vector.elements(els, false)
|
1998-01-16 07:13:05 -05:00
|
|
|
when Matrix
|
|
|
|
self.covector * x
|
|
|
|
else
|
1999-08-13 01:45:20 -04:00
|
|
|
s, x = X.coerce(self)
|
1998-01-16 07:13:05 -05:00
|
|
|
s * x
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def +(v)
|
|
|
|
case v
|
|
|
|
when Vector
|
1999-08-13 01:45:20 -04:00
|
|
|
Vector.Raise ErrDimensionMismatch if size != v.size
|
1998-01-16 07:13:05 -05:00
|
|
|
els = collect2(v) {
|
|
|
|
|v1, v2|
|
|
|
|
v1 + v2
|
|
|
|
}
|
2000-07-10 00:49:24 -04:00
|
|
|
Vector.elements(els, false)
|
1998-01-16 07:13:05 -05:00
|
|
|
when Matrix
|
|
|
|
Matrix.column_vector(self) + v
|
|
|
|
else
|
1999-08-13 01:45:20 -04:00
|
|
|
s, x = v.coerce(self)
|
1998-01-16 07:13:05 -05:00
|
|
|
s + x
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def -(v)
|
|
|
|
case v
|
|
|
|
when Vector
|
1999-08-13 01:45:20 -04:00
|
|
|
Vector.Raise ErrDimensionMismatch if size != v.size
|
1998-01-16 07:13:05 -05:00
|
|
|
els = collect2(v) {
|
|
|
|
|v1, v2|
|
|
|
|
v1 - v2
|
|
|
|
}
|
2000-07-10 00:49:24 -04:00
|
|
|
Vector.elements(els, false)
|
1998-01-16 07:13:05 -05:00
|
|
|
when Matrix
|
|
|
|
Matrix.column_vector(self) - v
|
|
|
|
else
|
1999-08-13 01:45:20 -04:00
|
|
|
s, x = v.coerce(self)
|
1998-01-16 07:13:05 -05:00
|
|
|
s - x
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# VECTOR FUNCTIONS
|
|
|
|
|
|
|
|
def inner_product(v)
|
1999-08-13 01:45:20 -04:00
|
|
|
Vector.Raise ErrDimensionMismatch if size != v.size
|
1998-01-16 07:13:05 -05:00
|
|
|
|
|
|
|
p = 0
|
|
|
|
each2(v) {
|
|
|
|
|v1, v2|
|
|
|
|
p += v1 * v2
|
|
|
|
}
|
|
|
|
p
|
|
|
|
end
|
|
|
|
|
|
|
|
def collect
|
|
|
|
els = @elements.collect {
|
|
|
|
|v|
|
|
|
|
yield v
|
|
|
|
}
|
2000-07-10 00:49:24 -04:00
|
|
|
Vector.elements(els, false)
|
1998-01-16 07:13:05 -05:00
|
|
|
end
|
|
|
|
alias map collect
|
|
|
|
|
|
|
|
def map2(v)
|
|
|
|
els = collect2(v) {
|
|
|
|
|v1, v2|
|
|
|
|
yield v1, v2
|
|
|
|
}
|
2000-07-10 00:49:24 -04:00
|
|
|
Vector.elements(els, false)
|
1998-01-16 07:13:05 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def r
|
|
|
|
v = 0
|
|
|
|
for e in @elements
|
|
|
|
v += e*e
|
|
|
|
end
|
1999-01-19 23:59:39 -05:00
|
|
|
return Math.sqrt(v)
|
1998-01-16 07:13:05 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
# CONVERTING
|
|
|
|
def covector
|
|
|
|
Matrix.row_vector(self)
|
|
|
|
end
|
|
|
|
|
|
|
|
def to_a
|
|
|
|
@elements.dup
|
|
|
|
end
|
|
|
|
|
|
|
|
def to_f
|
|
|
|
collect{|e| e.to_f}
|
|
|
|
end
|
|
|
|
|
|
|
|
def to_i
|
|
|
|
collect{|e| e.to_i}
|
|
|
|
end
|
|
|
|
|
|
|
|
def to_r
|
|
|
|
collect{|e| e.to_r}
|
|
|
|
end
|
|
|
|
|
|
|
|
def coerce(other)
|
|
|
|
case other
|
|
|
|
when Numeric
|
|
|
|
return Scalar.new(other), self
|
1999-08-13 01:45:20 -04:00
|
|
|
else
|
|
|
|
raise TypeError, "#{type} can't be coerced into #{other.type}"
|
1998-01-16 07:13:05 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# PRINTING
|
|
|
|
|
|
|
|
def to_s
|
|
|
|
"Vector[" + @elements.join(", ") + "]"
|
|
|
|
end
|
|
|
|
|
|
|
|
def inspect
|
|
|
|
str = "Vector"+@elements.inspect
|
|
|
|
end
|
|
|
|
end
|