Class: Matrix

Class | Matrix
In: | matrix.rb
Parent: | Object

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`

### Methods

* ** + - / == [] [] 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 zero

### Included Modules

ExceptionForMatrix

### External Aliases

identity | -> | unit
identity | -> | I

### 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 122
      def Matrix.[](*rows)
        new(:init_rows, rows, false)
      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 233
      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)

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 144
      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 162
      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)

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

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

[Source]

    # File matrix.rb, line 190
      def Matrix.identity(n)
        Matrix.scalar(n, 1)
      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 248
      def initialize(init_method, *argv)
        self.send(init_method, *argv)
      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 214
      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)

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 133
      def Matrix.rows(rows, copy = true)
        new(:init_rows, rows, copy)
      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 180
      def Matrix.scalar(n, value)
        Matrix.diagonal(*Array.new(n).fill(value, 0, n))
      end

zero(n)

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

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

[Source]

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

### Public Instance methods

*(m)

Matrix multiplication.

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

[Source]

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

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

Matrix addition.

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

[Source]

    # File matrix.rb, line 494
      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 524
      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 554
      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

==(other)

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

[Source]

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

        other.compare_by_row_vectors(@rows)
      end

[](i, j)

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

[Source]

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

clone()

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

[Source]

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

coerce(other)

FIXME: describe coerce.

[Source]

    # File matrix.rb, line 809
      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( {|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 327
      def collect # :yield: e
        rows = @rows.collect{|row| row.collect{|e| yield e}}
        Matrix.rows(rows, false)
      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 305
      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()

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 282
      def column_size
        @rows[0].size
      end

column_vectors()

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

[Source]

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

compare_by_row_vectors(rows)

Not really intended for general consumption.

[Source]

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

det()

Alias for determinant

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 675
      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

eql?(other)

Alias for #==

hash()

Returns a hash-code for the matrix.

[Source]

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

inspect()

Overrides Object#inspect

[Source]

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

inv()

Alias for inverse

inverse()

Returns the inverse of the matrix.

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

[Source]

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

inverse_from(src)

Not for public consumption?

[Source]

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

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 342
      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()

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 714
      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

regular?()

Returns `true` if this is a regular matrix.

[Source]

    # File matrix.rb, line 374
      def regular?
        square? and rank == column_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 290
      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()

Returns the number of rows.

[Source]

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

row_vectors()

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

[Source]

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

singular?()

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

[Source]

    # File matrix.rb, line 381
      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 389
      def square?
        column_size == row_size
      end

t()

Alias for transpose

to_a()

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

[Source]

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

to_s()

Overrides Object#to_s

[Source]

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

tr()

Alias for trace

trace()

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

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

[Source]

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

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 797
      def transpose
        Matrix.columns(@rows)
      end

[Validate]