class Matrix - Documentation for Ruby 2.3.0 (original) (raw)

The Matrix class represents a mathematical matrix. It provides methods for creating matrices, operating on them arithmetically and algebraically, and determining their mathematical properties (trace, rank, inverse, determinant).

Method Catalogue

To create a matrix:

To access Matrix elements/columns/rows/submatrices/properties:

Properties of a matrix:

Matrix arithmetic:

Matrix functions:

Matrix decompositions:

Complex arithmetic:

Conversion to other data types:

String representations:

Constants

SELECTORS

Attributes

column_count[R]

Returns the number of columns.

column_size[R]

Returns the number of columns.

rows[R]

instance creations

Public Class Methods

[](*rows) click to toggle source

Creates a matrix where each argument is a row.

Matrix[ [25, 93], [-1, 66] ] => 25 93 -1 66

def Matrix. rows(rows, false) end

build(row_count, column_count = row_count) { |i, j| ... } click to toggle source

Creates a matrix of size row_count x column_count. It fills the values by calling the given block, passing the current row and column. Returns an enumerator if no block is given.

m = Matrix.build(2, 4) {|row, col| col - row } => Matrix[[0, 1, 2, 3], [-1, 0, 1, 2]] m = Matrix.build(3) { rand } => a 3x3 matrix with random elements

def Matrix.build(row_count, column_count = row_count) row_count = CoercionHelper.coerce_to_int(row_count) column_count = CoercionHelper.coerce_to_int(column_count) raise ArgumentError if row_count < 0 || column_count < 0 return to_enum :build, row_count, column_count unless block_given? rows = Array.new(row_count) do |i| Array.new(column_count) do |j| yield i, j end end new rows, column_count end

column_vector(column) click to toggle source

Creates a single-column matrix where the values of that column are as given in column.

Matrix.column_vector([4,5,6]) => 4 5 6

def Matrix.column_vector(column) column = convert_to_array(column) new [column].transpose, 1 end

columns(columns) click to toggle source

Creates a matrix using columns as an array of column vectors.

Matrix.columns([[25, 93], [-1, 66]]) => 25 -1 93 66

def Matrix.columns(columns) rows(columns, false).transpose end

diagonal(*values) click to toggle source

Creates a matrix where the diagonal elements are composed of values.

Matrix.diagonal(9, 5, -3) => 9 0 0 0 5 0 0 0 -3

def Matrix.diagonal(*values) size = values.size return Matrix.empty if size == 0 rows = Array.new(size) {|j| row = Array.new(size, 0) row[j] = values[j] row } new rows end

empty(row_count = 0, column_count = 0) click to toggle source

Creates a empty matrix of row_count x column_count. At least one of row_count or column_count must be 0.

m = Matrix.empty(2, 0) m == Matrix[ [], [] ] => true n = Matrix.empty(0, 3) n == Matrix.columns([ [], [], [] ]) => true m * n => Matrix[[0, 0, 0], [0, 0, 0]]

def Matrix.empty(row_count = 0, column_count = 0) raise ArgumentError, "One size must be 0" if column_count != 0 && row_count != 0 raise ArgumentError, "Negative size" if column_count < 0 || row_count < 0

new([[]]*row_count, column_count) end

hstack(x, *matrices) click to toggle source

Create a matrix by stacking matrices horizontally

x = Matrix[[1, 2], [3, 4]] y = Matrix[[5, 6], [7, 8]] Matrix.hstack(x, y)

def Matrix.hstack(x, *matrices) raise TypeError, "Expected a Matrix, got a #{x.class}" unless x.is_a?(Matrix) result = x.send(:rows).map(&:dup) total_column_count = x.column_count matrices.each do |m| raise TypeError, "Expected a Matrix, got a #{m.class}" unless m.is_a?(Matrix) if m.row_count != x.row_count raise ErrDimensionMismatch, "The given matrices must have #{x.row_count} rows, but one has #{m.row_count}" end result.each_with_index do |row, i| row.concat m.send(:rows)[i] end total_column_count += m.column_count end new result, total_column_count end

identity(n) click to toggle source

Creates an n by n identity matrix.

Matrix.identity(2) => 1 0 0 1

def Matrix.identity(n) scalar(n, 1) end

Also aliased as: unit, I

new(rows, column_count = rows[0].size) click to toggle source

Matrix.new is private; use Matrix.rows, columns, [], etc… to create.

def initialize(rows, column_count = rows[0].size)

@rows = rows @column_count = column_count end

row_vector(row) click to toggle source

Creates a single-row matrix where the values of that row are as given in row.

Matrix.row_vector([4,5,6]) => 4 5 6

def Matrix.row_vector(row) row = convert_to_array(row) new [row] end

rows(rows, copy = true) click to toggle source

Creates a matrix where rows is an array of arrays, each of which is a row of the matrix. If the optional argument copy is false, use the given arrays as the internal structure of the matrix without copying.

Matrix.rows([[25, 93], [-1, 66]]) => 25 93 -1 66

def Matrix.rows(rows, copy = true) rows = convert_to_array(rows, copy) rows.map! do |row| convert_to_array(row, copy) end size = (rows[0] || []).size rows.each do |row| raise ErrDimensionMismatch, "row size differs (#{row.size} should be #{size})" unless row.size == size end new rows, size end

scalar(n, value) click to toggle source

Creates an n by n diagonal matrix where each diagonal element is value.

Matrix.scalar(2, 5) => 5 0 0 5

def Matrix.scalar(n, value) diagonal(*Array.new(n, value)) end

vstack(x, *matrices) click to toggle source

Create a matrix by stacking matrices vertically

x = Matrix[[1, 2], [3, 4]] y = Matrix[[5, 6], [7, 8]] Matrix.vstack(x, y)

def Matrix.vstack(x, *matrices) raise TypeError, "Expected a Matrix, got a #{x.class}" unless x.is_a?(Matrix) result = x.send(:rows).map(&:dup) matrices.each do |m| raise TypeError, "Expected a Matrix, got a #{m.class}" unless m.is_a?(Matrix) if m.column_count != x.column_count raise ErrDimensionMismatch, "The given matrices must have #{x.column_count} columns, but one has #{m.column_count}" end result.concat(m.send(:rows)) end new result, x.column_count end

zero(row_count, column_count = row_count) click to toggle source

Creates a zero matrix.

Matrix.zero(2) => 0 0 0 0

def Matrix.zero(row_count, column_count = row_count) rows = Array.new(row_count){Array.new(column_count, 0)} new rows, column_count end

Public Instance Methods

*(m) click to toggle source

Matrix multiplication.

Matrix[[2,4], [6,8]] * Matrix.identity(2) => 2 4 6 8

def *(m) case(m) when Numeric rows = @rows.collect {|row| row.collect {|e| e * m } } return new_matrix rows, column_count when Vector m = self.class.column_vector(m) r = self * m return r.column(0) when Matrix Matrix.Raise ErrDimensionMismatch if column_count != m.row_count

rows = Array.new(row_count) {|i|
  Array.new(m.column_count) {|j|
    (0 ... column_count).inject(0) do |vij, k|
      vij + self[i, k] * m[k, j]
    end
  }
}
return new_matrix rows, m.column_count

else return apply_through_coercion(m, method) end end

**(other) click to toggle source

Matrix exponentiation. Equivalent to multiplying the matrix by itself N times. Non integer exponents will be handled by diagonalizing the matrix.

Matrix[[7,6], [3,9]] ** 2 => 67 96 48 99

def ** (other) case other when Integer x = self if other <= 0 x = self.inverse return self.class.identity(self.column_count) if other == 0 other = -other end z = nil loop do z = z ? z * x : x if other[0] == 1 return z if (other >>= 1).zero? x *= x end when Numeric v, d, v_inv = eigensystem v * self.class.diagonal(*d.each(:diagonal).map{|e| e ** other}) * v_inv else Matrix.Raise ErrOperationNotDefined, "**", self.class, other.class end end

+(m) click to toggle source

Matrix addition.

Matrix.scalar(2,5) + Matrix[[1,0], [-4,7]] => 6 0 -4 12

def +(m) case m when Numeric Matrix.Raise ErrOperationNotDefined, "+", self.class, m.class when Vector m = self.class.column_vector(m) when Matrix else return apply_through_coercion(m, method) end

Matrix.Raise ErrDimensionMismatch unless row_count == m.row_count && column_count == m.column_count

rows = Array.new(row_count) {|i| Array.new(column_count) {|j| self[i, j] + m[i, j] } } new_matrix rows, column_count end

+@() click to toggle source

-(m) click to toggle source

Matrix subtraction.

Matrix[[1,5], [4,2]] - Matrix[[9,3], [-4,1]] => -8 2 8 1

def -(m) case m when Numeric Matrix.Raise ErrOperationNotDefined, "-", self.class, m.class when Vector m = self.class.column_vector(m) when Matrix else return apply_through_coercion(m, method) end

Matrix.Raise ErrDimensionMismatch unless row_count == m.row_count && column_count == m.column_count

rows = Array.new(row_count) {|i| Array.new(column_count) {|j| self[i, j] - m[i, j] } } new_matrix rows, column_count end

-@() click to toggle source

def -@ collect {|e| -e } end

/(other) click to toggle source

Matrix division (multiplication by the inverse).

Matrix[[7,6], [3,9]] / Matrix[[2,9], [3,1]] => -7 1 -3 -6

def /(other) case other when Numeric rows = @rows.collect {|row| row.collect {|e| e / other } } return new_matrix rows, column_count when Matrix return self * other.inverse else return apply_through_coercion(other, method) end end

==(other) click to toggle source

Returns true if and only if the two matrices contain equal elements.

def ==(other) return false unless Matrix === other && column_count == other.column_count rows == other.rows end

[](i, j) click to toggle source

Returns element (i,j) of the matrix. That is: row i, column j.

def [](i, j) @rows.fetch(i){return nil}[j] end

adjugate() click to toggle source

Returns the adjugate of the matrix.

Matrix[ [7,6],[3,9] ].adjugate => 9 -6 -3 7

def adjugate Matrix.Raise ErrDimensionMismatch unless square? Matrix.build(row_count, column_count) do |row, column| cofactor(column, row) end end

clone() click to toggle source

Returns a clone of the matrix, so that the contents of each do not reference identical objects. There should be no good reason to do this since Matrices are immutable.

def clone new_matrix @rows.map(&:dup), column_count end

coerce(other) click to toggle source

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) case other when Numeric return Scalar.new(other), self else raise TypeError, "#{self.class} can't be coerced into #{other.class}" end end

cofactor(row, column) click to toggle source

Returns the (row, column) cofactor which is obtained by multiplying the first minor by (-1)**(row + column).

Matrix.diagonal(9, 5, -3, 4).cofactor(1, 1) => -108

def cofactor(row, column) raise RuntimeError, "cofactor of empty matrix is not defined" if empty? Matrix.Raise ErrDimensionMismatch unless square?

det_of_minor = first_minor(row, column).determinant det_of_minor * (-1) ** (row + column) end

cofactor_expansion(row: nil, column: nil)

collect() { |e| ... } click to toggle source

Returns a matrix that is the result of iteration of the given block over all elements of the matrix.

Matrix[ [1,2], [3,4] ].collect { |e| e**2 } => 1 4 9 16

def collect(&block) return to_enum(:collect) unless block_given? rows = @rows.collect{|row| row.collect(&block)} new_matrix rows, column_count end

Also aliased as: map

column(j) { |e| ... } click to toggle source

Returns column vector number j of the matrix as a Vector (starting at 0 like an array). When a block is given, the elements of that vector are iterated.

def column(j) if block_given? return self if j >= column_count || j < -column_count row_count.times do |i| yield @rows[i][j] end self else return nil if j >= column_count || j < -column_count col = Array.new(row_count) {|i| @rows[i][j] } Vector.elements(col, false) end end

column_vectors() click to toggle source

Returns an array of the column vectors of the matrix. See Vector.

def column_vectors Array.new(column_count) {|i| column(i) } end

component(i, j)

Alias for: []

conjugate() click to toggle source

Returns the conjugate of the matrix.

Matrix[[Complex(1,2), Complex(0,1), 0], [1, 2, 3]] => 1+2i i 0 1 2 3 Matrix[[Complex(1,2), Complex(0,1), 0], [1, 2, 3]].conjugate => 1-2i -i 0 1 2 3

def conjugate collect(&:conjugate) end

Also aliased as: conj

determinant() click to toggle source

Returns the determinant of the matrix.

Beware that using Float values can yield erroneous results because of their lack of precision. Consider using exact types like Rational or BigDecimal instead.

Matrix[[7,6], [3,9]].determinant => 45

def determinant Matrix.Raise ErrDimensionMismatch unless square? m = @rows case row_count

when 0 +1 when 1 + m[0][0] when 2 + m[0][0] * m[1][1] - m[0][1] * m[1][0] when 3 m0, m1, m2 = m + m0[0] * m1[1] * m2[2] - m0[0] * m1[2] * m2[1]
- m0[1] * m1[0] * m2[2] + m0[1] * m1[2] * m2[0]
+ m0[2] * m1[0] * m2[1] - m0[2] * m1[1] * m2[0] when 4 m0, m1, m2, m3 = m + m0[0] * m1[1] * m2[2] * m3[3] - m0[0] * m1[1] * m2[3] * m3[2]
- m0[0] * m1[2] * m2[1] * m3[3] + m0[0] * m1[2] * m2[3] * m3[1]
+ m0[0] * m1[3] * m2[1] * m3[2] - m0[0] * m1[3] * m2[2] * m3[1]
- m0[1] * m1[0] * m2[2] * m3[3] + m0[1] * m1[0] * m2[3] * m3[2]
+ m0[1] * m1[2] * m2[0] * m3[3] - m0[1] * m1[2] * m2[3] * m3[0]
- m0[1] * m1[3] * m2[0] * m3[2] + m0[1] * m1[3] * m2[2] * m3[0]
+ m0[2] * m1[0] * m2[1] * m3[3] - m0[2] * m1[0] * m2[3] * m3[1]
- m0[2] * m1[1] * m2[0] * m3[3] + m0[2] * m1[1] * m2[3] * m3[0]
+ m0[2] * m1[3] * m2[0] * m3[1] - m0[2] * m1[3] * m2[1] * m3[0]
- m0[3] * m1[0] * m2[1] * m3[2] + m0[3] * m1[0] * m2[2] * m3[1]
+ m0[3] * m1[1] * m2[0] * m3[2] - m0[3] * m1[1] * m2[2] * m3[0]
- m0[3] * m1[2] * m2[0] * m3[1] + m0[3] * m1[2] * m2[1] * m3[0] else

determinant_bareiss

end end

Also aliased as: det

determinant_e() click to toggle source

deprecated; use Matrix#determinant

def determinant_e warn "#{caller(1)[0]}: warning: Matrix#determinant_e is deprecated; use #determinant" determinant end

Also aliased as: det_e

diagonal?() click to toggle source

Returns true if this is a diagonal matrix. Raises an error if matrix is not square.

def diagonal? Matrix.Raise ErrDimensionMismatch unless square? each(:off_diagonal).all?(&:zero?) end

each(which = :all) { |e| ... } click to toggle source

Yields all elements of the matrix, starting with those of the first row, or returns an Enumerator if no block given. Elements can be restricted by passing an argument:

def each(which = :all) return to_enum :each, which unless block_given? last = column_count - 1 case which when :all block = Proc.new @rows.each do |row| row.each(&block) end when :diagonal @rows.each_with_index do |row, row_index| yield row.fetch(row_index){return self} end when :off_diagonal @rows.each_with_index do |row, row_index| column_count.times do |col_index| yield row[col_index] unless row_index == col_index end end when :lower @rows.each_with_index do |row, row_index| 0.upto([row_index, last].min) do |col_index| yield row[col_index] end end when :strict_lower @rows.each_with_index do |row, row_index| [row_index, column_count].min.times do |col_index| yield row[col_index] end end when :strict_upper @rows.each_with_index do |row, row_index| (row_index+1).upto(last) do |col_index| yield row[col_index] end end when :upper @rows.each_with_index do |row, row_index| row_index.upto(last) do |col_index| yield row[col_index] end end else raise ArgumentError, "expected #{which.inspect} to be one of :all, :diagonal, :off_diagonal, :lower, :strict_lower, :strict_upper or :upper" end self end

each_with_index(which = :all) { |e, row, column| ... } click to toggle source

Same as each, but the row index and column index in addition to the element

Matrix[ [1,2], [3,4] ].each_with_index do |e, row, col| puts "#{e} at #{row}, #{col}" end

def each_with_index(which = :all) return to_enum :each_with_index, which unless block_given? last = column_count - 1 case which when :all @rows.each_with_index do |row, row_index| row.each_with_index do |e, col_index| yield e, row_index, col_index end end when :diagonal @rows.each_with_index do |row, row_index| yield row.fetch(row_index){return self}, row_index, row_index end when :off_diagonal @rows.each_with_index do |row, row_index| column_count.times do |col_index| yield row[col_index], row_index, col_index unless row_index == col_index end end when :lower @rows.each_with_index do |row, row_index| 0.upto([row_index, last].min) do |col_index| yield row[col_index], row_index, col_index end end when :strict_lower @rows.each_with_index do |row, row_index| [row_index, column_count].min.times do |col_index| yield row[col_index], row_index, col_index end end when :strict_upper @rows.each_with_index do |row, row_index| (row_index+1).upto(last) do |col_index| yield row[col_index], row_index, col_index end end when :upper @rows.each_with_index do |row, row_index| row_index.upto(last) do |col_index| yield row[col_index], row_index, col_index end end else raise ArgumentError, "expected #{which.inspect} to be one of :all, :diagonal, :off_diagonal, :lower, :strict_lower, :strict_upper or :upper" end self end

eigensystem() click to toggle source

Returns the Eigensystem of the matrix; see EigenvalueDecomposition.

m = Matrix[[1, 2], [3, 4]] v, d, v_inv = m.eigensystem d.diagonal? v.inv == v_inv (v * d * v_inv).round(5) == m

def eigensystem EigenvalueDecomposition.new(self) end

Also aliased as: eigen

element(i, j)

Alias for: []

elements_to_f() click to toggle source

def elements_to_f warn "#{caller(1)[0]}: warning: Matrix#elements_to_f is deprecated, use map(&:to_f)" map(&:to_f) end

elements_to_i() click to toggle source

def elements_to_i warn "#{caller(1)[0]}: warning: Matrix#elements_to_i is deprecated, use map(&:to_i)" map(&:to_i) end

elements_to_r() click to toggle source

def elements_to_r warn "#{caller(1)[0]}: warning: Matrix#elements_to_r is deprecated, use map(&:to_r)" map(&:to_r) end

empty?() click to toggle source

Returns true if this is an empty matrix, i.e. if the number of rows or the number of columns is 0.

def empty? column_count == 0 || row_count == 0 end

eql?(other) click to toggle source

def eql?(other) return false unless Matrix === other && column_count == other.column_count rows.eql? other.rows end

first_minor(row, column) click to toggle source

Returns the submatrix obtained by deleting the specified row and column.

Matrix.diagonal(9, 5, -3, 4).first_minor(1, 2) => 9 0 0 0 0 0 0 0 4

def first_minor(row, column) raise RuntimeError, "first_minor of empty matrix is not defined" if empty?

unless 0 <= row && row < row_count raise ArgumentError, "invalid row (#{row.inspect} for 0..#{row_count - 1})" end

unless 0 <= column && column < column_count raise ArgumentError, "invalid column (#{column.inspect} for 0..#{column_count - 1})" end

arrays = to_a arrays.delete_at(row) arrays.each do |array| array.delete_at(column) end

new_matrix arrays, column_count - 1 end

hash() click to toggle source

Returns a hash-code for the matrix.

hermitian?() click to toggle source

Returns true if this is an hermitian matrix. Raises an error if matrix is not square.

def hermitian? Matrix.Raise ErrDimensionMismatch unless square? each_with_index(:upper).all? do |e, row, col| e == rows[col][row].conj end end

hstack(*matrices) click to toggle source

Returns a new matrix resulting by stacking horizontally the receiver with the given matrices

x = Matrix[[1, 2], [3, 4]] y = Matrix[[5, 6], [7, 8]] x.hstack(y)

def hstack(*matrices) self.class.hstack(self, *matrices) end

imaginary() click to toggle source

Returns the imaginary part of the matrix.

Matrix[[Complex(1,2), Complex(0,1), 0], [1, 2, 3]] => 1+2i i 0 1 2 3 Matrix[[Complex(1,2), Complex(0,1), 0], [1, 2, 3]].imaginary => 2i i 0 0 0 0

def imaginary collect(&:imaginary) end

Also aliased as: imag

index(value, selector = :all) → [row, column] click to toggle source

index(selector = :all){ block } → [row, column]

index(selector = :all) → an_enumerator

The index method is specialized to return the index as [row, column] It also accepts an optional selector argument, see each for details.

Matrix[ [1,2], [3,4] ].index(&:even?) Matrix[ [1,1], [1,1] ].index(1, :strict_lower)

def index(*args) raise ArgumentError, "wrong number of arguments(#{args.size} for 0-2)" if args.size > 2 which = (args.size == 2 || SELECTORS.include?(args.last)) ? args.pop : :all return to_enum :find_index, which, *args unless block_given? || args.size == 1 if args.size == 1 value = args.first each_with_index(which) do |e, row_index, col_index| return row_index, col_index if e == value end else each_with_index(which) do |e, row_index, col_index| return row_index, col_index if yield e end end nil end

inspect() click to toggle source

Overrides Object#inspect

def inspect if empty? "#{self.class}.empty(#{row_count}, #{column_count})" else "#{self.class}#{@rows.inspect}" end end

inverse() click to toggle source

Returns the inverse of the matrix.

Matrix[[-1, -1], [0, -1]].inverse => -1 1 0 -1

def inverse Matrix.Raise ErrDimensionMismatch unless square? self.class.I(row_count).send(:inverse_from, self) end

Also aliased as: inv

laplace_expansion(row: nil, column: nil) click to toggle source

Returns the Laplace expansion along given row or column.

Matrix[[7,6], [3,9]].laplace_expansion(column: 1) => 45

Matrix[[Vector[1, 0], Vector[0, 1]], [2, 3]].laplace_expansion(row: 0) => Vector[3, -2]

def laplace_expansion(row: nil, column: nil) num = row || column

if !num || (row && column) raise ArgumentError, "exactly one the row or column arguments must be specified" end

Matrix.Raise ErrDimensionMismatch unless square? raise RuntimeError, "laplace_expansion of empty matrix is not defined" if empty?

unless 0 <= num && num < row_count raise ArgumentError, "invalid num (#{num.inspect} for 0..#{row_count - 1})" end

send(row ? :row : :column, num).map.with_index { |e, k| e * cofactor(*(row ? [num, k] : [k,num])) }.inject(:+) end

lower_triangular?() click to toggle source

Returns true if this is a lower triangular matrix.

def lower_triangular? each(:strict_upper).all?(&:zero?) end

lup() click to toggle source

Returns the LUP decomposition of the matrix; see LUPDecomposition.

a = Matrix[[1, 2], [3, 4]] l, u, p = a.lup l.lower_triangular? u.upper_triangular? p.permutation?
l * u == p * a
a.lup.solve([2, 5])

def lup LUPDecomposition.new(self) end

lup_decomposition()

Alias for: lup

minor(*param) click to toggle source

Returns a section of the matrix. The parameters are either:

Matrix.diagonal(9, 5, -3).minor(0..1, 0..2) => 9 0 0 0 5 0

Like Array#[], negative indices count backward from the end of the row or column (-1 is the last element). Returns nil if the starting row or column is greater than row_count or column_count respectively.

def minor(*param) case param.size when 2 row_range, col_range = param from_row = row_range.first from_row += row_count if from_row < 0 to_row = row_range.end to_row += row_count if to_row < 0 to_row += 1 unless row_range.exclude_end? size_row = to_row - from_row

from_col = col_range.first
from_col += column_count if from_col < 0
to_col = col_range.end
to_col += column_count if to_col < 0
to_col += 1 unless col_range.exclude_end?
size_col = to_col - from_col

when 4 from_row, size_row, from_col, size_col = param return nil if size_row < 0 || size_col < 0 from_row += row_count if from_row < 0 from_col += column_count if from_col < 0 else raise ArgumentError, param.inspect end

return nil if from_row > row_count || from_col > column_count || from_row < 0 || from_col < 0 rows = @rows[from_row, size_row].collect{|row| row[from_col, size_col] } new_matrix rows, [column_count - from_col, size_col].min end

normal?() click to toggle source

Returns true if this is a normal matrix. Raises an error if matrix is not square.

def normal? Matrix.Raise ErrDimensionMismatch unless square? rows.each_with_index do |row_i, i| rows.each_with_index do |row_j, j| s = 0 rows.each_with_index do |row_k, k| s += row_i[k] * row_j[k].conj - row_k[i].conj * row_k[j] end return false unless s == 0 end end true end

orthogonal?() click to toggle source

Returns true if this is an orthogonal matrix Raises an error if matrix is not square.

def orthogonal? Matrix.Raise ErrDimensionMismatch unless square? rows.each_with_index do |row, i| column_count.times do |j| s = 0 row_count.times do |k| s += row[k] * rows[k][j] end return false unless s == (i == j ? 1 : 0) end end true end

permutation?() click to toggle source

Returns true if this is a permutation matrix Raises an error if matrix is not square.

def permutation? Matrix.Raise ErrDimensionMismatch unless square? cols = Array.new(column_count) rows.each_with_index do |row, i| found = false row.each_with_index do |e, j| if e == 1 return false if found || cols[j] found = cols[j] = true elsif e != 0 return false end end return false unless found end true end

rank() click to toggle source

Returns the rank of the matrix. Beware that using Float values can yield erroneous results because of their lack of precision. Consider using exact types like Rational or BigDecimal instead.

Matrix[[7,6], [3,9]].rank => 2

def rank

a = to_a last_column = column_count - 1 last_row = row_count - 1 pivot_row = 0 previous_pivot = 1 0.upto(last_column) do |k| switch_row = (pivot_row .. last_row).find {|row| a[row][k] != 0 } if switch_row a[switch_row], a[pivot_row] = a[pivot_row], a[switch_row] unless pivot_row == switch_row pivot = a[pivot_row][k] (pivot_row+1).upto(last_row) do |i| ai = a[i] (k+1).upto(last_column) do |j| ai[j] = (pivot * ai[j] - ai[k] * a[pivot_row][j]) / previous_pivot end end pivot_row += 1 previous_pivot = pivot end end pivot_row end

rank_e() click to toggle source

deprecated; use Matrix#rank

def rank_e warn "#{caller(1)[0]}: warning: Matrix#rank_e is deprecated; use #rank" rank end

real() click to toggle source

Returns the real part of the matrix.

Matrix[[Complex(1,2), Complex(0,1), 0], [1, 2, 3]] => 1+2i i 0 1 2 3 Matrix[[Complex(1,2), Complex(0,1), 0], [1, 2, 3]].real => 1 0 0 1 2 3

def real collect(&:real) end

real?() click to toggle source

Returns true if all entries of the matrix are real.

def real? all?(&:real?) end

rect() click to toggle source

Returns an array containing matrices corresponding to the real and imaginary parts of the matrix

m.rect == [m.real, m.imag] # ==> true for all matrices m

def rect [real, imag] end

regular?() click to toggle source

Returns true if this is a regular (i.e. non-singular) matrix.

def regular? not singular? end

round(ndigits=0) click to toggle source

Returns a matrix with entries rounded to the given precision (see Float#round)

def round(ndigits=0) map{|e| e.round(ndigits)} end

row(i) { |e| ... } click to toggle source

Returns row vector number i of the matrix as a Vector (starting at 0 like an array). When a block is given, the elements of that vector are iterated.

def row(i, &block) if block_given? @rows.fetch(i){return self}.each(&block) self else Vector.elements(@rows.fetch(i){return nil}) end end

row_count() click to toggle source

Returns the number of rows.

def row_count @rows.size end

row_vectors() click to toggle source

Returns an array of the row vectors of the matrix. See Vector.

def row_vectors Array.new(row_count) {|i| row(i) } end

singular?() click to toggle source

Returns true if this is a singular matrix.

def singular? determinant == 0 end

square?() click to toggle source

Returns true if this is a square matrix.

def square? column_count == row_count end

symmetric?() click to toggle source

Returns true if this is a symmetric matrix. Raises an error if matrix is not square.

def symmetric? Matrix.Raise ErrDimensionMismatch unless square? each_with_index(:strict_upper) do |e, row, col| return false if e != rows[col][row] end true end

to_a() click to toggle source

Returns an array of arrays that describe the rows of the matrix.

def to_a @rows.collect(&:dup) end

to_s() click to toggle source

Overrides Object#to_s

def to_s if empty? "#{self.class}.empty(#{row_count}, #{column_count})" else "#{self.class}[" + @rows.collect{|row| "[" + row.collect{|e| e.to_s}.join(", ") + "]" }.join(", ")+"]" end end

trace() click to toggle source

Returns the trace (sum of diagonal elements) of the matrix.

Matrix[[7,6], [3,9]].trace => 16

def trace Matrix.Raise ErrDimensionMismatch unless square? (0...column_count).inject(0) do |tr, i| tr + @rows[i][i] end end

Also aliased as: tr

transpose() click to toggle source

Returns the transpose of the matrix.

Matrix[[1,2], [3,4], [5,6]] => 1 2 3 4 5 6 Matrix[[1,2], [3,4], [5,6]].transpose => 1 3 5 2 4 6

def transpose return self.class.empty(column_count, 0) if row_count.zero? new_matrix @rows.transpose, row_count end

Also aliased as: t

unitary?() click to toggle source

Returns true if this is a unitary matrix Raises an error if matrix is not square.

def unitary? Matrix.Raise ErrDimensionMismatch unless square? rows.each_with_index do |row, i| column_count.times do |j| s = 0 row_count.times do |k| s += row[k].conj * rows[k][j] end return false unless s == (i == j ? 1 : 0) end end true end

upper_triangular?() click to toggle source

Returns true if this is an upper triangular matrix.

def upper_triangular? each(:strict_lower).all?(&:zero?) end

vstack(*matrices) click to toggle source

Returns a new matrix resulting by stacking vertically the receiver with the given matrices

x = Matrix[[1, 2], [3, 4]] y = Matrix[[5, 6], [7, 8]] x.vstack(y)

def vstack(*matrices) self.class.vstack(self, *matrices) end

zero?() click to toggle source

Returns true if this is a matrix with only zero elements

def zero? all?(&:zero?) end

Private Instance Methods

[]=(i, j, v) click to toggle source

def []=(i, j, v) @rows[i][j] = v end

determinant_bareiss() click to toggle source

Private. Use Matrix#determinant

Returns the determinant of the matrix, using Bareiss' multistep integer-preserving 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. 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 intermediate results with better precision.

def determinant_bareiss size = row_count last = size - 1 a = to_a no_pivot = Proc.new{ return 0 } sign = +1 pivot = 1 size.times do |k| previous_pivot = pivot if (pivot = a[k][k]) == 0 switch = (k+1 ... size).find(no_pivot) {|row| a[row][k] != 0 } a[switch], a[k] = a[k], a[switch] pivot = a[k][k] sign = -sign end (k+1).upto(last) do |i| ai = a[i] (k+1).upto(last) do |j| ai[j] = (pivot * ai[j] - ai[k] * a[k][j]) / previous_pivot end end end sign * pivot end

set_component(i, j, v)

Alias for: []=

set_element(i, j, v)

Alias for: []=