# Matrix (Class)

In: | matrix.rb
Parent: | Object

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).

The capabilities of the class indicated in the above paragraph are probably not exhaustive. Browse the methods and their documentation for more information.

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

## Methods

* ** + - / == I [] [] clone coerce collect column column_size column_vector column_vectors columns compare_by_row_vectors det determinant diagonal eql? hash identity inspect inv inverse inverse_from map minor new rank regular? row row_size row_vector row_vectors rows scalar singular? square? t to_a to_s tr trace transpose unit zero

## Included Modules

ExceptionForMatrix

## Public Class methods

[](*rows)

Creates a matrix where each argument is a row.

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

[Source]

    # File matrix.rb, line 205
      def Matrix.[](*rows)
        new(:init_rows, rows, false)
      end

rows(rows, copy = true)

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

[Source]

    # File matrix.rb, line 216
      def Matrix.rows(rows, copy = true)
        new(:init_rows, rows, copy)
      end

columns(columns)

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

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

[Source]

    # File matrix.rb, line 227
      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)

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

[Source]

    # File matrix.rb, line 245
      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

scalar(n, value)

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

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

[Source]

    # File matrix.rb, line 263
      def Matrix.scalar(n, value)
        Matrix.diagonal(*Array.new(n).fill(value, 0, n))
      end

identity(n)

Creates an `n` by `n` identity matrix.

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

[Source]

    # File matrix.rb, line 273
      def Matrix.identity(n)
        Matrix.scalar(n, 1)
      end

unit(n)

Alias for identity

I(n)

Alias for identity

zero(n)

Creates an `n` by `n` zero matrix.

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

[Source]

    # File matrix.rb, line 287
      def Matrix.zero(n)
        Matrix.scalar(n, 0)
      end

row_vector(row)

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

[Source]

    # File matrix.rb, line 297
      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

column_vector(column)

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

[Source]

    # File matrix.rb, line 316
      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

new(init_method, *argv)

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

[Source]

    # File matrix.rb, line 331
      def initialize(init_method, *argv)
        self.send(init_method, *argv)
      end

## Public Instance methods

[](i, j)

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

[Source]

    # File matrix.rb, line 348
      def [](i, j)
        @rows[i][j]
      end

row_size()

Returns the number of rows.

[Source]

    # File matrix.rb, line 355
      def row_size
        @rows.size
      end

column_size()

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.

[Source]

    # File matrix.rb, line 365
      def column_size
        @rows[0].size
      end

row(i) {|e| ...}

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.

[Source]

    # File matrix.rb, line 373
      def row(i) # :yield: e

        if block_given?
          for e in @rows[i]
            yield e
          end
        else
          Vector.elements(@rows[i])
        end
      end

column(j) {|e| ...}

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.

[Source]

    # File matrix.rb, line 388
      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

collect() {|e| ...}

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 { |i| i**2 }
        => 1  4
           9 16

[Source]

    # File matrix.rb, line 410
      def collect # :yield: e

        rows = @rows.collect{|row| row.collect{|e| yield e}}
        Matrix.rows(rows, false)
      end

map()

Alias for collect

minor(*param)

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

[Source]

    # File matrix.rb, line 425
      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

regular?()

Returns `true` if this is a regular matrix.

[Source]

    # File matrix.rb, line 457
      def regular?
        square? and rank == column_size
      end

singular?()

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

[Source]

    # File matrix.rb, line 464
      def singular?
        not regular?
      end

square?()

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

[Source]

    # File matrix.rb, line 472
      def square?
        column_size == row_size
      end

==(other)

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

[Source]

    # File matrix.rb, line 483
      def ==(other)
        return false unless Matrix === other

        other.compare_by_row_vectors(@rows)
      end

eql?(other)

Alias for #==

compare_by_row_vectors(rows)

Not really intended for general consumption.

[Source]

    # File matrix.rb, line 493
      def compare_by_row_vectors(rows)
        return false unless @rows.size == rows.size

        0.upto(@rows.size - 1) do
          |i|
          return false unless @rows[i] == rows[i]
        end
        true
      end

clone()

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

[Source]

    # File matrix.rb, line 507
      def clone
        Matrix.rows(@rows)
      end

hash()

Returns a hash-code for the matrix.

[Source]

    # File matrix.rb, line 514
      def hash
        value = 0
        for row in @rows
          for e in row
            value ^= e.hash
          end
        end
        return value
      end

*(m)

Matrix multiplication.

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

[Source]

    # File matrix.rb, line 534
      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

+(m)

Matrix addition.

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

[Source]

    # File matrix.rb, line 577
      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)

Matrix subtraction.

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

[Source]

    # File matrix.rb, line 607
      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)

Matrix division (multiplication by the inverse).

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

[Source]

    # File matrix.rb, line 637
      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)
          rerurn x / y
        end
      end

inverse()

Returns the inverse of the matrix.

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

[Source]

    # File matrix.rb, line 662
      def inverse
        Matrix.Raise ErrDimensionMismatch unless square?
        Matrix.I(row_size).inverse_from(self)
      end

inv()

Alias for inverse

inverse_from(src)

Not for public consumption?

[Source]

    # File matrix.rb, line 671
      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
              Matrix.Raise ErrNotRegular if (i += 1) > size
            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

**(other)

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

[Source]

    # File matrix.rb, line 721
      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

determinant()

Returns the determinant of the matrix. If the matrix is not square, the result is 0.

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

[Source]

    # File matrix.rb, line 758
      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]
            det *= -1
          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

det()

Alias for determinant

rank()

Returns the rank of the matrix. Beware that using Float values, with their usual lack of precision, can affect the value returned by this method. Use Rational values instead if this is important to you.

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

[Source]

    # File matrix.rb, line 797
      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
            begin
              if (i += 1) > a_column_size - 1
                exists = false
                break
              end
            end while a[i][k] == 0
            if exists
              a[i], a[k] = a[k], a[i]
              akk = a[k][k]
            else
              i = k
              exists = true
              begin
                if (i += 1) > a_row_size - 1
                  exists = false
                  break
                end
              end while a[k][i] == 0
              if exists
                k.upto(a_column_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
          (k + 1).upto(a_row_size - 1) do
            |i|
            q = a[i][k] / akk
            (k + 1).upto(a_column_size - 1) do
              |j|
              a[i][j] -= a[k][j] * q
            end
          end
          rank += 1
        end while (k += 1) <= a_column_size - 1
        return rank
      end

trace()

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

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

[Source]

    # File matrix.rb, line 860
      def trace
        tr = 0
        0.upto(column_size - 1) do
          |i|
          tr += @rows[i][i]
        end
        tr
      end

tr()

Alias for trace

transpose()

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

[Source]

    # File matrix.rb, line 880
      def transpose
        Matrix.columns(@rows)
      end

t()

Alias for transpose

coerce(other)

FIXME: describe coerce.

[Source]

    # File matrix.rb, line 892
      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

row_vectors()

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

[Source]

    # File matrix.rb, line 904
      def row_vectors
        rows = (0 .. row_size - 1).collect {
          |i|
          row(i)
        }
        rows
      end

column_vectors()

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

[Source]

    # File matrix.rb, line 915
      def column_vectors
        columns = (0 .. column_size - 1).collect {
          |i|
          column(i)
        }
        columns
      end

to_a()

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

[Source]

    # File matrix.rb, line 926
      def to_a
        @rows.collect{|row| row.collect{|e| e}}
      end

to_s()

Overrides Object#to_s

[Source]

    # File matrix.rb, line 937
      def to_s
        "Matrix[" + @rows.collect{
          |row|
          "[" + row.collect{|e| e.to_s}.join(", ") + "]"
        }.join(", ")+"]"
      end

inspect()

Overrides Object#inspect

[Source]

    # File matrix.rb, line 947
      def inspect
        "Matrix"+@rows.inspect
      end

[Validate]