In Files

  • matrix.rb

Class/Module Index [+]

Quicksearch

Matrix

The Matrix class represents a mathematical matrix, and provides methods for creating special-case matrices (zero, identity, diagonal, singular, vector), operating on them arithmetically and algebraically, and determining their mathematical properties (trace, rank, inverse, determinant).

Note that although matrices should theoretically be rectangular, this is not enforced by the class.

Also note that the determinant of integer matrices may be incorrectly calculated unless you also require 'mathn'. This may be fixed in the future.

Method Catalogue

To create a matrix:

  • Matrix[*rows]

  • Matrix.[](*rows)

  • Matrix.rows(rows, copy = true)

  • Matrix.columns(columns)

  • Matrix.diagonal(*values)

  • Matrix.scalar(n, value)

  • Matrix.scalar(n, value)

  • Matrix.identity(n)

  • Matrix.unit(n)

  • Matrix.I(n)

  • Matrix.zero(n)

  • Matrix.row_vector(row)

  • Matrix.column_vector(column)

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

  • [](i, j)

  • #row_size

  • #column_size

  • #row(i)

  • #column(j)

  • #collect

  • #map

  • #minor(*param)

Properties of a matrix:

  • #regular?

  • #singular?

  • #square?

Matrix arithmetic:

  • *(m)

  • +(m)

  • -(m)

  • #/(m)

  • #inverse

  • #inv

  • **

Matrix functions:

  • #determinant

  • #det

  • #rank

  • #trace

  • #tr

  • #transpose

  • #t

Conversion to other data types:

  • #coerce(other)

  • #row_vectors

  • #column_vectors

  • #to_a

String representations:

  • #to_s

  • #inspect

Public Class Methods

I(n) click to toggle source
Alias for: identity
[](*rows) click to toggle source

Creates a matrix where each argument is a row.

Matrix[ [25, 93], [-1, 66] ]
   =>  25 93
       -1 66
 
               # File matrix.rb, line 120
def Matrix.[](*rows)
  new(:init_rows, rows, false)
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
 
               # File matrix.rb, line 228
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
            
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
 
               # File matrix.rb, line 142
def Matrix.columns(columns)
  rows = (0 .. columns[0].size - 1).collect {|i|
    (0 .. columns.size - 1).collect {|j|
      columns[j][i]
    }
  }
  Matrix.rows(rows, false)
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
 
               # File matrix.rb, line 158
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
  }
  rows(rows, false)
end
            
identity(n) click to toggle source

Creates an n by n identity matrix.

Matrix.identity(2)
  => 1 0
     0 1
 
               # File matrix.rb, line 185
def Matrix.identity(n)
  Matrix.scalar(n, 1)
end
            
Also aliased as: unit, I
new(init_method, *argv) click to toggle source

This method is used by the other methods that create matrices, and is of no use to general users.

 
               # File matrix.rb, line 243
def initialize(init_method, *argv)
  self.send(init_method, *argv)
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
 
               # File matrix.rb, line 209
def Matrix.row_vector(row)
  case row
  when Vector
    Matrix.rows([row.to_a], false)
  when Array
    Matrix.rows([row.dup], false)
  else
    Matrix.rows([[row]], false)
  end
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 to 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
 
               # File matrix.rb, line 131
def Matrix.rows(rows, copy = true)
  new(:init_rows, rows, copy)
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
 
               # File matrix.rb, line 175
def Matrix.scalar(n, value)
  Matrix.diagonal(*Array.new(n).fill(value, 0, n))
end
            
unit(n) click to toggle source
Alias for: identity
zero(n) click to toggle source

Creates an n by n zero matrix.

Matrix.zero(2)
  => 0 0
     0 0
 
               # File matrix.rb, line 199
def Matrix.zero(n)
  Matrix.scalar(n, 0)
end
            

Public Instance Methods

*(m) click to toggle source

Matrix multiplication.

Matrix[[2,4], [6,8]] * Matrix.identity(2)
  => 2 4
     6 8
 
               # File matrix.rb, line 455
def *(m) # m is matrix or vector or number
  case(m)
  when Numeric
    rows = @rows.collect {|row|
      row.collect {|e|
        e * m
      }
    }
    return Matrix.rows(rows, false)
  when Vector
    m = Matrix.column_vector(m)
    r = self * m
    return r.column(0)
  when Matrix
    Matrix.Raise ErrDimensionMismatch if column_size != m.row_size
  
    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
      }
    }
    return Matrix.rows(rows, false)
  else
    x, y = m.coerce(self)
    return x * y
  end
end
            
**(other) click to toggle source

Matrix exponentiation. Defined for integer powers only. Equivalent to multiplying the matrix by itself N times.

Matrix[[7,6], [3,9]] ** 2
  => 67 96
     48 99
 
               # File matrix.rb, line 633
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)
    Matrix.Raise ErrOperationNotDefined, "**"
  else
    Matrix.Raise ErrOperationNotDefined, "**"
  end
end
            
+(m) click to toggle source

Matrix addition.

Matrix.scalar(2,5) + Matrix[[1,0], [-4,7]]
  =>  6  0
     -4 12
 
               # File matrix.rb, line 493
def +(m)
  case m
  when Numeric
    Matrix.Raise ErrOperationNotDefined, "+"
  when Vector
    m = Matrix.column_vector(m)
  when Matrix
  else
    x, y = m.coerce(self)
    return x + y
  end
  
  Matrix.Raise ErrDimensionMismatch unless row_size == m.row_size and column_size == m.column_size
  
  rows = (0 .. row_size - 1).collect {|i|
    (0 .. column_size - 1).collect {|j|
      self[i, j] + m[i, j]
    }
  }
  Matrix.rows(rows, false)
end
            
-(m) click to toggle source

Matrix subtraction.

Matrix[[1,5], [4,2]] - Matrix[[9,3], [-4,1]]
  => -8  2
      8  1
 
               # File matrix.rb, line 521
def -(m)
  case m
  when Numeric
    Matrix.Raise ErrOperationNotDefined, "-"
  when Vector
    m = Matrix.column_vector(m)
  when Matrix
  else
    x, y = m.coerce(self)
    return x - y
  end
  
  Matrix.Raise ErrDimensionMismatch unless row_size == m.row_size and column_size == m.column_size
  
  rows = (0 .. row_size - 1).collect {|i|
    (0 .. column_size - 1).collect {|j|
      self[i, j] - m[i, j]
    }
  }
  Matrix.rows(rows, false)
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
 
               # File matrix.rb, line 549
def /(other)
  case other
  when Numeric
    rows = @rows.collect {|row|
      row.collect {|e|
        e / other
      }
    }
    return Matrix.rows(rows, false)
  when Matrix
    return self * other.inverse
  else
    x, y = other.coerce(self)
    return x / y
  end
end
            
==(other) click to toggle source

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

 
               # File matrix.rb, line 401
def ==(other)
  return false unless Matrix === other
  
  other.compare_by_row_vectors(@rows)
end
            
[](i, j) click to toggle source

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

 
               # File matrix.rb, line 260
def [](i, j)
  @rows[i][j]
end
            
Also aliased as: element, component
clone() click to toggle source

Returns a clone of the matrix, so that the contents of each do not reference identical objects.

 
               # File matrix.rb, line 428
def clone
  Matrix.rows(@rows)
end
            
coerce(other) click to toggle source

FIXME: describe coerce.

 
               # File matrix.rb, line 891
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
            
collect() 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
 
               # File matrix.rb, line 329
def collect # :yield: e
  rows = @rows.collect{|row| row.collect{|e| yield e}}
  Matrix.rows(rows, false)
end
            
Also aliased as: map
column(j) 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.

 
               # File matrix.rb, line 309
def column(j) # :yield: e
  if block_given?
    0.upto(row_size - 1) do |i|
      yield @rows[i][j]
    end
  else
    col = (0 .. row_size - 1).collect {|i|
      @rows[i][j]
    }
    Vector.elements(col, false)
  end
end
            
column_size() click to toggle source

Returns the number of columns. Note that it is possible to construct a matrix with uneven columns (e.g. Matrix[ [1,2,3], [4,5] ]), but this is mathematically unsound. This method uses the first row to determine the result.

 
               # File matrix.rb, line 286
def column_size
  @rows[0].size
end
            
column_vectors() click to toggle source

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

 
               # File matrix.rb, line 913
def column_vectors
  columns = (0 .. column_size - 1).collect {|i|
    column(i)
  }
  columns
end
            
compare_by_row_vectors(rows, comparison = :==) click to toggle source

Not really intended for general consumption.

 
               # File matrix.rb, line 415
def compare_by_row_vectors(rows, comparison = :==)
  return false unless @rows.size == rows.size
  
  0.upto(@rows.size - 1) do |i|
    return false unless @rows[i].send(comparison, rows[i])
  end
  true
end
            
component(i, j) click to toggle source
Alias for: []
det() click to toggle source
Alias for: determinant
det_e() click to toggle source
Alias for: determinant_e
determinant() click to toggle source

Returns the determinant of the matrix. If the matrix is not square, the result is 0. This method's algorism is Gaussian elimination method and using Numeric#quo(). Beware that using Float values, with their usual lack of precision, can affect the value returned by this method. Use Rational values or #det_e instead if this is important to you.

Matrix[[7,6], [3,9]].determinant
  => 63.0
 
               # File matrix.rb, line 674
def determinant
  return 0 unless square?
  
  size = row_size - 1
  a = to_a
  
  det = 1
  k = 0
  loop do
    if (akk = a[k][k]) == 0
      i = k
      loop do
        return 0 if (i += 1) > size
        break unless a[i][k] == 0
      end
      a[i], a[k] = a[k], a[i]
      akk = a[k][k]
      det *= -1
    end

    for i in k + 1 .. size
      q = a[i][k].quo(akk)
      (k + 1).upto(size) do |j|
        a[i][j] -= a[k][j] * q
      end
    end
    det *= akk
    break unless (k += 1) <= size
  end
  det
end
            
Also aliased as: det
determinant_e() click to toggle source

Returns the determinant of the matrix. If the matrix is not square, the result is 0. This method's algorism is Gaussian elimination method. This method uses Euclidean algorism. If all elements are integer, really exact value. But, if an element is a float, can't return exact value.

Matrix[[7,6], [3,9]].determinant
  => 63
 
               # File matrix.rb, line 717
def determinant_e
  return 0 unless square?
  
  size = row_size - 1
  a = to_a
  
  det = 1
  k = 0
  loop do
    if a[k][k].zero?
      i = k
      loop do
        return 0 if (i += 1) > size
        break unless a[i][k].zero?
      end
      a[i], a[k] = a[k], a[i]
      det *= -1
    end

    for i in (k + 1)..size
      q = a[i][k].quo(a[k][k])
      k.upto(size) do |j|
        a[i][j] -= a[k][j] * q
      end
      unless a[i][k].zero?
        a[i], a[k] = a[k], a[i]
        det *= -1
        redo
      end
    end
    det *= a[k][k]
    break unless (k += 1) <= size
  end
  det
end
            
Also aliased as: det_e
element(i, j) click to toggle source
Alias for: []
elements_to_f() click to toggle source
 
               # File matrix.rb, line 927
def elements_to_f
  collect{|e| e.to_f}
end
            
elements_to_i() click to toggle source
 
               # File matrix.rb, line 931
def elements_to_i
  collect{|e| e.to_i}
end
            
elements_to_r() click to toggle source
 
               # File matrix.rb, line 935
def elements_to_r
  collect{|e| e.to_r}
end
            
eql?(other) click to toggle source
 
               # File matrix.rb, line 406
def eql?(other)
  return false unless Matrix === other
  
  other.compare_by_row_vectors(@rows, :eql?)
end
            
hash() click to toggle source

Returns a hash-code for the matrix.

 
               # File matrix.rb, line 435
def hash
  value = 0
  for row in @rows
    for e in row
      value ^= e.hash
    end
  end
  return value
end
            
inspect() click to toggle source

Overrides Object#inspect

 
               # File matrix.rb, line 955
def inspect
  "Matrix"+@rows.inspect
end
            
inv() click to toggle source
Alias for: inverse
inverse() click to toggle source

Returns the inverse of the matrix.

Matrix[[1, 2], [2, 1]].inverse
  => -1  1
      0 -1
 
               # File matrix.rb, line 572
def inverse
  Matrix.Raise ErrDimensionMismatch unless square?
  Matrix.I(row_size).inverse_from(self)
end
            
Also aliased as: inv
inverse_from(src) click to toggle source

Not for public consumption?

 
               # File matrix.rb, line 581
def inverse_from(src)
  size = row_size - 1
  a = src.to_a
  
  for k in 0..size
    i = k
    akk = a[k][k].abs
    ((k+1)..size).each do |j|
      v = a[j][k].abs
      if v > akk
        i = j
        akk = v
      end
    end
    Matrix.Raise ErrNotRegular if akk == 0
    if i != k
      a[i], a[k] = a[k], a[i]
      @rows[i], @rows[k] = @rows[k], @rows[i]
    end
    akk = a[k][k]
    
    for i in 0 .. size
      next if i == k
      q = a[i][k].quo(akk)
      a[i][k] = 0
      
      for j in (k + 1).. size
        a[i][j] -= a[k][j] * q
      end
      for j in 0..size
        @rows[i][j] -= @rows[k][j] * q
      end
    end
    
    for j in (k + 1).. size
      a[k][j] = a[k][j].quo(akk)
    end
    for j in 0..size
      @rows[k][j] = @rows[k][j].quo(akk)
    end
  end
  self
end
            
map() click to toggle source
Alias for: collect
minor(*param) click to toggle source

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

  • start_row, nrows, start_col, ncols; OR

  • col_range, row_range

Matrix.diagonal(9, 5, -3).minor(0..1, 0..2)
  => 9 0 0
     0 5 0
 
               # File matrix.rb, line 344
def minor(*param)
  case param.size
  when 2
    from_row = param[0].first
    size_row = param[0].end - from_row
    size_row += 1 unless param[0].exclude_end?
    from_col = param[1].first
    size_col = param[1].end - from_col
    size_col += 1 unless param[1].exclude_end?
  when 4
    from_row = param[0]
    size_row = param[1]
    from_col = param[2]
    size_col = param[3]
  else
    Matrix.Raise ArgumentError, param.inspect
  end
  
  rows = @rows[from_row, size_row].collect{|row|
    row[from_col, size_col]
  }
  Matrix.rows(rows, false)
end
            
rank() click to toggle source

Returns the rank of the matrix. Beware that using Float values, probably return faild value. Use Rational values or #rank_e for getting exact result.

Matrix[[7,6], [3,9]].rank
  => 2
 
               # File matrix.rb, line 762
def rank
  if column_size > row_size
    a = transpose.to_a
    a_column_size = row_size
    a_row_size = column_size
  else
    a = to_a
    a_column_size = column_size
    a_row_size = row_size
  end
  rank = 0
  k = 0
  begin
    if (akk = a[k][k]) == 0
      i = k
      exists = true
      loop do
        if (i += 1) > a_row_size - 1
          exists = false
          break
        end
        break unless a[i][k] == 0
      end
      if exists
        a[i], a[k] = a[k], a[i]
        akk = a[k][k]
      else
        i = k
        exists = true
        loop do
          if (i += 1) > a_column_size - 1
            exists = false
            break
          end
          break unless a[k][i] == 0
        end
        if exists
          k.upto(a_row_size - 1) do |j|
            a[j][k], a[j][i] = a[j][i], a[j][k]
          end
          akk = a[k][k]
        else
          next
        end
      end
    end

    for i in (k + 1)..(a_row_size - 1)
      q = a[i][k].quo(akk)
      for j in (k + 1)..(a_column_size - 1)
        a[i][j] -= a[k][j] * q
      end
    end
    rank += 1
  end while (k += 1) <= a_column_size - 1
  return rank
end
            
rank_e() click to toggle source

Returns the rank of the matrix. This method uses Euclidean algorism. If all elements are integer, really exact value. But, if an element is a float, can't return exact value.

Matrix[[7,6], [3,9]].rank
  => 2
 
               # File matrix.rb, line 828
def rank_e
  a = to_a
  a_column_size = column_size
  a_row_size = row_size
  pi = 0
  (0 ... a_column_size).each do |j|
    if i = (pi ... a_row_size).find{|i0| !a[i0][j].zero?}
      if i != pi
        a[pi], a[i] = a[i], a[pi]
      end
      (pi + 1 ... a_row_size).each do |k|
        q = a[k][j].quo(a[pi][j])
        (pi ... a_column_size).each do |j0|
          a[k][j0] -= q * a[pi][j0]
        end
        if k > pi && !a[k][j].zero?
          a[k], a[pi] = a[pi], a[k]
          redo
        end
      end
      pi += 1
    end
  end
  pi
end
            
regular?() click to toggle source

Returns true if this is a regular matrix.

 
               # File matrix.rb, line 375
def regular?
  square? and rank == column_size
end
            
row(i) 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.

 
               # File matrix.rb, line 294
def row(i) # :yield: e
  if block_given?
    for e in @rows[i]
      yield e
    end
  else
    Vector.elements(@rows[i])
  end
end
            
row_size() click to toggle source

Returns the number of rows.

 
               # File matrix.rb, line 276
def row_size
  @rows.size
end
            
row_vectors() click to toggle source

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

 
               # File matrix.rb, line 903
def row_vectors
  rows = (0 .. row_size - 1).collect {|i|
    row(i)
  }
  rows
end
            
singular?() click to toggle source

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

 
               # File matrix.rb, line 382
def singular?
  not regular?
end
            
square?() click to toggle source

Returns true is this is a square matrix. See note in #column_size about this being unreliable, though.

 
               # File matrix.rb, line 390
def square?
  column_size == row_size
end
            
t() click to toggle source
Alias for: transpose
to_a() click to toggle source

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

 
               # File matrix.rb, line 923
def to_a
  @rows.collect{|row| row.collect{|e| e}}
end
            
to_s() click to toggle source

Overrides Object#to_s

 
               # File matrix.rb, line 946
def to_s
  "Matrix[" + @rows.collect{|row|
    "[" + row.collect{|e| e.to_s}.join(", ") + "]"
  }.join(", ")+"]"
end
            
tr() click to toggle source
Alias for: trace
trace() click to toggle source

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

Matrix[[7,6], [3,9]].trace
  => 16
 
               # File matrix.rb, line 860
def trace
  tr = 0
  0.upto(column_size - 1) do |i|
    tr += @rows[i][i]
  end
  tr
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
 
               # File matrix.rb, line 879
def transpose
  Matrix.columns(@rows)
end
            
Also aliased as: t