mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
* NEWS: List all changes to lib/matrix
* lib/matrix.rb: Improve doc and style. Trivial optimizations. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@27585 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
c8e50abfe8
commit
ec8b5d0625
2 changed files with 114 additions and 87 deletions
24
NEWS
24
NEWS
|
@ -224,6 +224,30 @@ with all sufficient information, see the ChangeLog file.
|
||||||
* do not raise ShiftingError if an aged file already exists.
|
* do not raise ShiftingError if an aged file already exists.
|
||||||
(no ShiftingError will be raised from 1.2.7, just warn() instead)
|
(no ShiftingError will be raised from 1.2.7, just warn() instead)
|
||||||
|
|
||||||
|
* matrix
|
||||||
|
* API change to adhere strictly to mathematical definitions:
|
||||||
|
* Matrices must now be rectangular.
|
||||||
|
* trace, regular?, singular? are defined only for square matrices
|
||||||
|
* support for empty matrices
|
||||||
|
* all integer matrices now have the right determinant (also an integer)
|
||||||
|
|
||||||
|
* Matrix and Vector include Enumerable.
|
||||||
|
|
||||||
|
* new methods:
|
||||||
|
* Matrix.build
|
||||||
|
* Matrix.empty
|
||||||
|
* Matrix#conj
|
||||||
|
* Matrix#conjugate
|
||||||
|
* Matrix#each
|
||||||
|
* Matrix#each_with_index
|
||||||
|
* Matrix#empty?
|
||||||
|
* Matrix#imag
|
||||||
|
* Matrix#imaginary
|
||||||
|
* Matrix#real
|
||||||
|
* Matrix#real?
|
||||||
|
* Matrix#rect
|
||||||
|
* Matrix#rectangular
|
||||||
|
|
||||||
* net/http
|
* net/http
|
||||||
* merged net/https.
|
* merged net/https.
|
||||||
|
|
||||||
|
|
145
lib/matrix.rb
145
lib/matrix.rb
|
@ -1,21 +1,15 @@
|
||||||
#--
|
# encoding: utf-8
|
||||||
# matrix.rb -
|
|
||||||
# $Release Version: 1.0$
|
|
||||||
# $Revision: 1.13 $
|
|
||||||
# Original Version from Smalltalk-80 version
|
|
||||||
# on July 23, 1985 at 8:37:17 am
|
|
||||||
# by Keiju ISHITSUKA
|
|
||||||
#++
|
|
||||||
#
|
#
|
||||||
# = matrix.rb
|
# = matrix.rb
|
||||||
#
|
#
|
||||||
# An implementation of Matrix and Vector classes.
|
# An implementation of Matrix and Vector classes.
|
||||||
#
|
#
|
||||||
# Author:: Keiju ISHITSUKA
|
|
||||||
# Documentation:: Gavin Sinclair (sourced from <i>Ruby in a Nutshell</i> (Matsumoto, O'Reilly))
|
|
||||||
#
|
|
||||||
# See classes Matrix and Vector for documentation.
|
# See classes Matrix and Vector for documentation.
|
||||||
#
|
#
|
||||||
|
# Current Maintainer:: Marc-André Lafortune
|
||||||
|
# Original Author:: Keiju ISHITSUKA
|
||||||
|
# Original Documentation:: Gavin Sinclair (sourced from <i>Ruby in a Nutshell</i> (Matsumoto, O'Reilly))
|
||||||
|
##
|
||||||
|
|
||||||
require "e2mmap.rb"
|
require "e2mmap.rb"
|
||||||
|
|
||||||
|
@ -31,15 +25,9 @@ module ExceptionForMatrix # :nodoc:
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
# The +Matrix+ class represents a mathematical matrix, and provides methods for creating
|
# The +Matrix+ class represents a mathematical matrix. It provides methods for creating
|
||||||
# special-case matrices (zero, identity, diagonal, singular, vector), operating on them
|
# matrices, operating on them arithmetically and algebraically,
|
||||||
# arithmetically and algebraically, and determining their mathematical properties (trace, rank,
|
# and determining their mathematical properties (trace, rank, inverse, determinant).
|
||||||
# inverse, determinant).
|
|
||||||
#
|
|
||||||
# Note that matrices must be rectangular, otherwise an ErrDimensionMismatch is raised.
|
|
||||||
#
|
|
||||||
# Also note that the determinant of integer matrices may be approximated unless you
|
|
||||||
# also <tt>require 'mathn'</tt>. This may be fixed in the future.
|
|
||||||
#
|
#
|
||||||
# == Method Catalogue
|
# == Method Catalogue
|
||||||
#
|
#
|
||||||
|
@ -115,9 +103,6 @@ end
|
||||||
# * <tt> #inspect </tt>
|
# * <tt> #inspect </tt>
|
||||||
#
|
#
|
||||||
class Matrix
|
class Matrix
|
||||||
@RCS_ID='-$Id: matrix.rb,v 1.13 2001/12/09 14:22:23 keiju Exp keiju $-'
|
|
||||||
|
|
||||||
# extend Exception2MessageMapper
|
|
||||||
include Enumerable
|
include Enumerable
|
||||||
include ExceptionForMatrix
|
include ExceptionForMatrix
|
||||||
|
|
||||||
|
@ -145,13 +130,13 @@ class Matrix
|
||||||
# -1 66
|
# -1 66
|
||||||
#
|
#
|
||||||
def Matrix.rows(rows, copy = true)
|
def Matrix.rows(rows, copy = true)
|
||||||
rows = Matrix.convert_to_array(rows)
|
rows = convert_to_array(rows)
|
||||||
rows.map! do |row|
|
rows.map! do |row|
|
||||||
Matrix.convert_to_array(row, copy)
|
convert_to_array(row, copy)
|
||||||
end
|
end
|
||||||
size = (rows[0] || []).size
|
size = (rows[0] || []).size
|
||||||
rows.each do |row|
|
rows.each do |row|
|
||||||
Matrix.Raise ErrDimensionMismatch, "element size differs (#{row.size} should be #{size})" unless row.size == size
|
Matrix.Raise ErrDimensionMismatch, "row size differs (#{row.size} should be #{size})" unless row.size == size
|
||||||
end
|
end
|
||||||
new rows, size
|
new rows, size
|
||||||
end
|
end
|
||||||
|
@ -200,7 +185,7 @@ class Matrix
|
||||||
def Matrix.diagonal(*values)
|
def Matrix.diagonal(*values)
|
||||||
size = values.size
|
size = values.size
|
||||||
rows = (0 ... size).collect {|j|
|
rows = (0 ... size).collect {|j|
|
||||||
row = Array.new(size).fill(0, 0, size)
|
row = Array.new(size, 0)
|
||||||
row[j] = values[j]
|
row[j] = values[j]
|
||||||
row
|
row
|
||||||
}
|
}
|
||||||
|
@ -215,7 +200,7 @@ class Matrix
|
||||||
# 0 5
|
# 0 5
|
||||||
#
|
#
|
||||||
def Matrix.scalar(n, value)
|
def Matrix.scalar(n, value)
|
||||||
Matrix.diagonal(*Array.new(n).fill(value, 0, n))
|
Matrix.diagonal(*Array.new(n, value))
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -249,7 +234,7 @@ class Matrix
|
||||||
# => 4 5 6
|
# => 4 5 6
|
||||||
#
|
#
|
||||||
def Matrix.row_vector(row)
|
def Matrix.row_vector(row)
|
||||||
row = Matrix.convert_to_array(row)
|
row = convert_to_array(row)
|
||||||
new [row]
|
new [row]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -262,13 +247,13 @@ class Matrix
|
||||||
# 6
|
# 6
|
||||||
#
|
#
|
||||||
def Matrix.column_vector(column)
|
def Matrix.column_vector(column)
|
||||||
column = Matrix.convert_to_array(column)
|
column = convert_to_array(column)
|
||||||
new [column].transpose, 1
|
new [column].transpose, 1
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
# Creates a empty matrix of +row_size+ x +column_size+.
|
# Creates a empty matrix of +row_size+ x +column_size+.
|
||||||
# +row_size+ or +column_size+ must be 0.
|
# At least one of +row_size+ or +column_size+ must be 0.
|
||||||
#
|
#
|
||||||
# m = Matrix.empty(2, 0)
|
# m = Matrix.empty(2, 0)
|
||||||
# m == Matrix[ [], [] ]
|
# m == Matrix[ [], [] ]
|
||||||
|
@ -417,7 +402,7 @@ class Matrix
|
||||||
#
|
#
|
||||||
# Returns a section of the matrix. The parameters are either:
|
# Returns a section of the matrix. The parameters are either:
|
||||||
# * start_row, nrows, start_col, ncols; OR
|
# * start_row, nrows, start_col, ncols; OR
|
||||||
# * col_range, row_range
|
# * row_range, col_range
|
||||||
#
|
#
|
||||||
# Matrix.diagonal(9, 5, -3).minor(0..1, 0..2)
|
# Matrix.diagonal(9, 5, -3).minor(0..1, 0..2)
|
||||||
# => 9 0 0
|
# => 9 0 0
|
||||||
|
@ -430,17 +415,19 @@ class Matrix
|
||||||
def minor(*param)
|
def minor(*param)
|
||||||
case param.size
|
case param.size
|
||||||
when 2
|
when 2
|
||||||
from_row = param[0].first
|
row_range, col_range = param
|
||||||
|
from_row = row_range.first
|
||||||
from_row += row_size if from_row < 0
|
from_row += row_size if from_row < 0
|
||||||
to_row = param[0].end
|
to_row = row_range.end
|
||||||
to_row += row_size if to_row < 0
|
to_row += row_size if to_row < 0
|
||||||
to_row += 1 unless param[0].exclude_end?
|
to_row += 1 unless row_range.exclude_end?
|
||||||
size_row = to_row - from_row
|
size_row = to_row - from_row
|
||||||
from_col = param[1].first
|
|
||||||
|
from_col = col_range.first
|
||||||
from_col += column_size if from_col < 0
|
from_col += column_size if from_col < 0
|
||||||
to_col = param[1].end
|
to_col = col_range.end
|
||||||
to_col += column_size if to_col < 0
|
to_col += column_size if to_col < 0
|
||||||
to_col += 1 unless param[1].exclude_end?
|
to_col += 1 unless col_range.exclude_end?
|
||||||
size_col = to_col - from_col
|
size_col = to_col - from_col
|
||||||
when 4
|
when 4
|
||||||
from_row, size_row, from_col, size_col = param
|
from_row, size_row, from_col, size_col = param
|
||||||
|
@ -521,7 +508,7 @@ class Matrix
|
||||||
# There should be no good reason to do this since Matrices are immutable.
|
# There should be no good reason to do this since Matrices are immutable.
|
||||||
#
|
#
|
||||||
def clone
|
def clone
|
||||||
new_matrix @rows.map{|row| row.dup}, column_size
|
new_matrix @rows.map(&:dup), column_size
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -654,21 +641,18 @@ class Matrix
|
||||||
#
|
#
|
||||||
def inverse
|
def inverse
|
||||||
Matrix.Raise ErrDimensionMismatch unless square?
|
Matrix.Raise ErrDimensionMismatch unless square?
|
||||||
Matrix.I(row_size).inverse_from(self)
|
Matrix.I(row_size).send(:inverse_from, self)
|
||||||
end
|
end
|
||||||
alias inv inverse
|
alias inv inverse
|
||||||
|
|
||||||
#
|
def inverse_from(src) # :nodoc:
|
||||||
# Not for public consumption?
|
last = row_size - 1
|
||||||
#
|
|
||||||
def inverse_from(src)
|
|
||||||
size = row_size
|
|
||||||
a = src.to_a
|
a = src.to_a
|
||||||
|
|
||||||
size.times do |k|
|
0.upto(last) do |k|
|
||||||
i = k
|
i = k
|
||||||
akk = a[k][k].abs
|
akk = a[k][k].abs
|
||||||
(k+1 ... size).each do |j|
|
(k+1).upto(last) do |j|
|
||||||
v = a[j][k].abs
|
v = a[j][k].abs
|
||||||
if v > akk
|
if v > akk
|
||||||
i = j
|
i = j
|
||||||
|
@ -682,39 +666,40 @@ class Matrix
|
||||||
end
|
end
|
||||||
akk = a[k][k]
|
akk = a[k][k]
|
||||||
|
|
||||||
size.times do |ii|
|
0.upto(last) do |ii|
|
||||||
next if ii == k
|
next if ii == k
|
||||||
q = a[ii][k].quo(akk)
|
q = a[ii][k].quo(akk)
|
||||||
a[ii][k] = 0
|
a[ii][k] = 0
|
||||||
|
|
||||||
(k + 1 ... size).each do |j|
|
(k + 1).upto(last) do |j|
|
||||||
a[ii][j] -= a[k][j] * q
|
a[ii][j] -= a[k][j] * q
|
||||||
end
|
end
|
||||||
size.times do |j|
|
0.upto(last) do |j|
|
||||||
@rows[ii][j] -= @rows[k][j] * q
|
@rows[ii][j] -= @rows[k][j] * q
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
(k + 1 ... size).each do |j|
|
(k+1).upto(last) do |j|
|
||||||
a[k][j] = a[k][j].quo(akk)
|
a[k][j] = a[k][j].quo(akk)
|
||||||
end
|
end
|
||||||
size.times do |j|
|
0.upto(last) do |j|
|
||||||
@rows[k][j] = @rows[k][j].quo(akk)
|
@rows[k][j] = @rows[k][j].quo(akk)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
self
|
self
|
||||||
end
|
end
|
||||||
#alias reciprocal inverse
|
private :inverse_from
|
||||||
|
|
||||||
#
|
#
|
||||||
# Matrix exponentiation. Defined for integer powers only. Equivalent to
|
# Matrix exponentiation. Currently implemented for integer powers only.
|
||||||
# multiplying the matrix by itself N times.
|
# Equivalent to multiplying the matrix by itself N times.
|
||||||
# Matrix[[7,6], [3,9]] ** 2
|
# Matrix[[7,6], [3,9]] ** 2
|
||||||
# => 67 96
|
# => 67 96
|
||||||
# 48 99
|
# 48 99
|
||||||
#
|
#
|
||||||
def ** (other)
|
def ** (other)
|
||||||
if other.kind_of?(Integer)
|
case other
|
||||||
|
when Integer
|
||||||
x = self
|
x = self
|
||||||
if other <= 0
|
if other <= 0
|
||||||
x = self.inverse
|
x = self.inverse
|
||||||
|
@ -727,7 +712,7 @@ class Matrix
|
||||||
return z if (other >>= 1).zero?
|
return z if (other >>= 1).zero?
|
||||||
x *= x
|
x *= x
|
||||||
end
|
end
|
||||||
elsif other.kind_of?(Float) || defined?(Rational) && other.kind_of?(Rational)
|
when Float, Rational
|
||||||
Matrix.Raise ErrOperationNotImplemented, "**", self.class, other.class
|
Matrix.Raise ErrOperationNotImplemented, "**", self.class, other.class
|
||||||
else
|
else
|
||||||
Matrix.Raise ErrOperationNotDefined, "**", self.class, other.class
|
Matrix.Raise ErrOperationNotDefined, "**", self.class, other.class
|
||||||
|
@ -796,8 +781,8 @@ class Matrix
|
||||||
# It has the same computational cost order O(n^3) as standard Gaussian elimination.
|
# It has the same computational cost order O(n^3) as standard Gaussian elimination.
|
||||||
# Intermediate results are fraction free and of lower complexity.
|
# Intermediate results are fraction free and of lower complexity.
|
||||||
# A matrix of Integers will have thus intermediate results that are also Integers,
|
# A matrix of Integers will have thus intermediate results that are also Integers,
|
||||||
# with smaller bignums (if any), while a matrix of Float will usually have more
|
# with smaller bignums (if any), while a matrix of Float will usually have
|
||||||
# precise intermediate results.
|
# intermediate results with better precision.
|
||||||
#
|
#
|
||||||
def determinant_bareiss
|
def determinant_bareiss
|
||||||
size = row_size
|
size = row_size
|
||||||
|
@ -973,7 +958,11 @@ class Matrix
|
||||||
#++
|
#++
|
||||||
|
|
||||||
#
|
#
|
||||||
# FIXME: describe #coerce.
|
# The coerce method provides support for Ruby type coercion.
|
||||||
|
# This coercion mechanism is used by Ruby to handle mixed-type
|
||||||
|
# numeric operations: it is intended to find a compatible common
|
||||||
|
# type between the two operands of the operator.
|
||||||
|
# See also Numeric#coerce.
|
||||||
#
|
#
|
||||||
def coerce(other)
|
def coerce(other)
|
||||||
case other
|
case other
|
||||||
|
@ -1010,17 +999,17 @@ class Matrix
|
||||||
end
|
end
|
||||||
|
|
||||||
def elements_to_f
|
def elements_to_f
|
||||||
warn "#{caller(1)[0]}: warning: Matrix#elements_to_f is deprecated"
|
warn "#{caller(1)[0]}: warning: Matrix#elements_to_f is deprecated, use map(&:to_f)"
|
||||||
map(&:to_f)
|
map(&:to_f)
|
||||||
end
|
end
|
||||||
|
|
||||||
def elements_to_i
|
def elements_to_i
|
||||||
warn "#{caller(1)[0]}: warning: Matrix#elements_to_i is deprecated"
|
warn "#{caller(1)[0]}: warning: Matrix#elements_to_i is deprecated, use map(&:to_i)"
|
||||||
map(&:to_i)
|
map(&:to_i)
|
||||||
end
|
end
|
||||||
|
|
||||||
def elements_to_r
|
def elements_to_r
|
||||||
warn "#{caller(1)[0]}: warning: Matrix#elements_to_r is deprecated"
|
warn "#{caller(1)[0]}: warning: Matrix#elements_to_r is deprecated, use map(&:to_r)"
|
||||||
map(&:to_r)
|
map(&:to_r)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1052,11 +1041,14 @@ class Matrix
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Private helper modules
|
||||||
|
|
||||||
|
module ConversionHelper # :nodoc:
|
||||||
#
|
#
|
||||||
# Converts the obj to an Array. If copy is set to true
|
# Converts the obj to an Array. If copy is set to true
|
||||||
# a copy of obj will be made if necessary.
|
# a copy of obj will be made if necessary.
|
||||||
#
|
#
|
||||||
def Matrix.convert_to_array(obj, copy = false)
|
def convert_to_array(obj, copy = false) # :nodoc:
|
||||||
case obj
|
case obj
|
||||||
when Array
|
when Array
|
||||||
copy ? obj.dup : obj
|
copy ? obj.dup : obj
|
||||||
|
@ -1072,10 +1064,16 @@ class Matrix
|
||||||
converted
|
converted
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
private :convert_to_array
|
||||||
|
end
|
||||||
|
|
||||||
# Private helper module
|
extend ConversionHelper
|
||||||
|
|
||||||
module CoercionHelper # :nodoc:
|
module CoercionHelper # :nodoc:
|
||||||
|
#
|
||||||
|
# Applies the operator +oper+ with argument +obj+
|
||||||
|
# through coercion of +obj+
|
||||||
|
#
|
||||||
def apply_through_coercion(obj, oper)
|
def apply_through_coercion(obj, oper)
|
||||||
coercion = obj.coerce(self)
|
coercion = obj.coerce(self)
|
||||||
raise TypeError unless coercion.is_a?(Array) && coercion.length == 2
|
raise TypeError unless coercion.is_a?(Array) && coercion.length == 2
|
||||||
|
@ -1085,10 +1083,12 @@ class Matrix
|
||||||
end
|
end
|
||||||
private :apply_through_coercion
|
private :apply_through_coercion
|
||||||
|
|
||||||
|
#
|
||||||
# Helper method to coerce a value into a specific class.
|
# Helper method to coerce a value into a specific class.
|
||||||
# Raises a TypeError if the coercion fails or the returned value
|
# Raises a TypeError if the coercion fails or the returned value
|
||||||
# is not of the right class.
|
# is not of the right class.
|
||||||
# (from Rubinius)
|
# (from Rubinius)
|
||||||
|
#
|
||||||
def self.coerce_to(obj, cls, meth) # :nodoc:
|
def self.coerce_to(obj, cls, meth) # :nodoc:
|
||||||
return obj if obj.kind_of?(cls)
|
return obj if obj.kind_of?(cls)
|
||||||
|
|
||||||
|
@ -1227,17 +1227,19 @@ class Vector
|
||||||
include ExceptionForMatrix
|
include ExceptionForMatrix
|
||||||
include Enumerable
|
include Enumerable
|
||||||
include Matrix::CoercionHelper
|
include Matrix::CoercionHelper
|
||||||
|
extend Matrix::ConversionHelper
|
||||||
#INSTANCE CREATION
|
#INSTANCE CREATION
|
||||||
|
|
||||||
private_class_method :new
|
private_class_method :new
|
||||||
attr_reader :elements
|
attr_reader :elements
|
||||||
protected :elements
|
protected :elements
|
||||||
|
|
||||||
#
|
#
|
||||||
# Creates a Vector from a list of elements.
|
# Creates a Vector from a list of elements.
|
||||||
# Vector[7, 4, ...]
|
# Vector[7, 4, ...]
|
||||||
#
|
#
|
||||||
def Vector.[](*array)
|
def Vector.[](*array)
|
||||||
new Matrix.convert_to_array(array, copy = false)
|
new convert_to_array(array, copy = false)
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -1245,7 +1247,7 @@ class Vector
|
||||||
# whether the array itself or a copy is used internally.
|
# whether the array itself or a copy is used internally.
|
||||||
#
|
#
|
||||||
def Vector.elements(array, copy = true)
|
def Vector.elements(array, copy = true)
|
||||||
new Matrix.convert_to_array(array, copy)
|
new convert_to_array(array, copy)
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -1502,7 +1504,11 @@ class Vector
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
# FIXME: describe Vector#coerce.
|
# The coerce method provides support for Ruby type coercion.
|
||||||
|
# This coercion mechanism is used by Ruby to handle mixed-type
|
||||||
|
# numeric operations: it is intended to find a compatible common
|
||||||
|
# type between the two operands of the operator.
|
||||||
|
# See also Numeric#coerce.
|
||||||
#
|
#
|
||||||
def coerce(other)
|
def coerce(other)
|
||||||
case other
|
case other
|
||||||
|
@ -1531,6 +1537,3 @@ class Vector
|
||||||
str = "Vector"+@elements.inspect
|
str = "Vector"+@elements.inspect
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Documentation comments:
|
|
||||||
# - Matrix#coerce and Vector#coerce need to be documented
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue