class Apatite::Matrix(T)

Included Modules

Defined in:

apatite/matrix.cr
apatite/matrix/eigenvalue_decomposition.cr
apatite/matrix/lup_decomposition.cr

Constant Summary

SELECTORS = {all: true, diagonal: true, off_diagonal: true, lower: true, strict_lower: true, strict_upper: true, upper: true}

Constructors

Class Method Summary

Instance Method Summary

Constructor Detail

def self.new(pull : JSON::PullParser) #

Creates a new Matrix instance from a JSON::PullParser. Defaults to a Float64 matrix.


[View source]
def self.new(pull : JSON::PullParser, &) #

[View source]

Class Method Detail

def self.[](*rows) #

Creates a matrix where each argument is a row.

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

[View source]
def self.build(row_count, column_count = row_count, &block : Int32, Int32 -> T) #

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

[View source]
def self.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 ]

[View source]
def self.columns(columns) #

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

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

[View source]
def self.combine(*matrices, &) #

Create a matrix by combining matrices entrywise, using the given block

x = Matrix[[6, 6], [4, 4]]
y = Matrix[[1, 2], [3, 4]]
Matrix.combine(x, y) { |a, b| a - b }
# => Matrix[[5, 4], [1, 0]]

[View source]
def self.diagonal(values : Indexable(T), dummy = nil) #

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 ]

[View source]
def self.diagonal(*values : T) #

ditto


[View source]
def self.empty(row_count = 0, column_count = 0) #

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

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

[View source]
def self.hstack(x, *matrices) #

Create a matrix by stacking matrices horizontally

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

[View source]
def self.identity(n) #

Creates an n by n identity matrix.

NOTE An explicit type is required since it cannot be inferred.

Matrix(Int32).identity(2)
# => [ 1, 0,
#      0, 1 ]

[View source]
def self.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 ]

[View source]
def self.rows(rows : Indexable(Indexable(T)), copy = true) #

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 ]

[View source]
def self.scalar(n, value : T) #

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

Matrix.scalar(2, 5)
# => [ 5, 0,
#      0, 5 ]

[View source]
def self.unit(n : T) #

ditto


[View source]
def self.vstack(x, *matrices) #

Create a matrix by stacking matrices vertically

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

[View source]
def self.zero(row_count, column_count = row_count) #

Creates a zero matrix. Note that a type definition is required.

Matrix(Int32).zero(2)
# => [ 0, 0,
#      0, 0 ]

[View source]

Instance Method Detail

def *(other) #

Matrix multiplication

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

[View source]
def **(other) #

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 ]

[View source]
def +(other : Matrix | Indexable) #

Matrix addition

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

[View source]
def -(other : Matrix | Indexable) #

Matrix subtraction

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

[View source]
def /(other) #

Matrix division (multiplication by the inverse).

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

[View source]
def <=>(other : Matrix) #
Description copied from module Comparable(Apatite::Matrix(T))

The comparison operator. Returns 0 if the two objects are equal, a negative number if this object is considered less than other, a positive number if this object is considered greater than other, or nil if the two objects are not comparable.

Subclasses define this method to provide class-specific ordering.

The comparison operator is usually used to sort values:

# Sort in a descending way:
[3, 1, 2].sort { |x, y| y <=> x } # => [3, 2, 1]

# Sort in an ascending way:
[3, 1, 2].sort { |x, y| x <=> y } # => [1, 2, 3]

[View source]
def ==(other : Matrix) #

Equality operator


[View source]
def [](i, j) #

Returns element (i, j) of the matrix. That is: row i, column j. Raises if either index is not found.


[View source]
def [](i) #

Returns row i of the matrix as an Array. Raises if the index is not found.


[View source]
def []=(i, j, v : T) #

Set the value at index (i, j). That is: row i, column j.


[View source]
def []?(i, j) #

Returns element (i, j) of the matrix. That is: row i, column j. Returns nil if either index is not found.


[View source]
def []?(i) #

Returns row i of the matrix as an Array. Returns nil if the index is not found.


[View source]
def adjugate #

Returns the adjugate of the matrix.

Matrix[ [7,6],[3,9] ].adjugate

=> [ 9, -6,

-3, 7 ]


[View source]
def clone #

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.


[View source]
def coerce(klass) #

Attempt to coerce the elements in the matrix to another type.


[View source]
def cofactor(row, column) #

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

[View source]
def column(j) #

Returns column vector j of the Matrix as a Vector (starting at 0). Raises if the column doesn't exist.


[View source]
def column(j, &block : T -> ) #

Returns a block which yields every item in column j of the Matrix.


[View source]
def column?(j) #

Returns column vector j of the Matrix as a Vector (starting at 0). Returns nil if the column doesn't exist.


[View source]
def column_count : Int32 #

Returns the number of columns.


[View source]
def column_vectors #

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


[View source]
def combine(*matrices, &block) #

ditto


[View source]
def conj #

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]].conj
# => 1-2i  -i  0
#       1   2  3

[View source]
def determinant #

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

[View source]
def diagonal? #

Returns true if this is a diagonal matrix.


[View source]
def each(which = :all, &block : T -> ) #

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:

  • :all (default): yields all elements
  • :diagonal: yields only elements on the diagonal
  • :off_diagonal: yields all elements except on the diagonal
  • :lower: yields only elements on or below the diagonal
  • :strict_lower: yields only elements below the diagonal
  • :strict_upper: yields only elements above the diagonal
  • :upper: yields only elements on or above the diagonal
Matrix[[1, 2], [3, 4]].each { |e| puts e }
# => prints the numbers 1 to 4
Matrix[[1, 2], [3, 4]].each(:strict_lower).to_a # => [3]

[View source]
def each_with_index(which = :all, &block : T, Int32, Int32 -> ) #

Same as #each, but yields 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
# => Prints:
#    1 at 0, 0
#    2 at 0, 1
#    3 at 1, 0
#    4 at 1, 1

[View source]
def eigensystem #

Returns the Eigensystem of the matrix See EigenvalueDecomposition.

NOTE Not working yet

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

[View source]
def empty? #

Returns true if this matrix is empty.


[View source]
def first_minor(row, column) #

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 ]

[View source]
def hadamard_product(m) #

Hadamard product

Matrix[[1, 2], [3, 4]].hadamard_product Matrix[[1, 2], [3, 2]]
# => [ 1,  4,
#      9,  8 ]

[View source]
def hermitian? #

Returns true if this is an hermitian matrix.


[View source]
def hstack(*matrices) #

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) # => Matrix[[1, 2, 5, 6], [3, 4, 7, 8]]

[View source]
def imag #

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]].imag
# => [ 2i,  i,  0,
#       0,  0,  0 ]

[View source]
def index(i, selector = :all) #

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, 1], [1, 1]].index(1, :strict_lower)
# => {1, 0}

[View source]
def index(selector = :all, &block : T -> Bool) #

Returns the index as {row, column}, using &block to filter the result. It also accepts an optional selector argument, see #each for details.

Matrix[[1, 2], [3, 4]].index(&.even?)
# => {0, 1}

[View source]
def inverse #

Returns the inverse of the matrix.

NOTE Always returns a Float64 regardless of Ts type. To coerce back into an Int, use #coerce.

Matrix[[-1, -1], [0, -1]].inverse
# => [ -1.0,  1.0,
#       0.0, -1.0 ]

[View source]
def laplace_expansion(*, row = nil, column = nil) #

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]

[View source]
def lower_triangular? #

Returns true if this matrix is a lower triangular matrix.


[View source]
def lup #

Returns the LUP decomposition of the matrix See +LUPDecomposition+.

NOTE Not working yet

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

[View source]
def map(&block : T -> T) #

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


[View source]
def map_with_index(&block : T, Int32, Int32 -> U) forall U #

Same as #map, but yields the row index and column index in addition to the element

matrix = Matrix[[1, 2], [3, 4]].map_with_index do |e, row, col|
  e*row + col
end
matrix == Matrix[[0, 1], [3, 5]] # => true

[View source]
def minor(from_row : Int, nrows : Int, from_col : Int, ncols : Int) #

Returns a section of the Matrix.

Matrix.diagonal(9, 5, -3).minor(0, 2, 0, 3)
# => [ 9, 0, 0,
#      0, 5, 0 ]

[View source]
def minor(row_range : Range, col_range : Range) #

Returns a section of the Matrix.

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

[View source]
def normal? #

Returns true if this is a normal matrix.

Matrix[[1, 1, 0], [0, 1, 1], [1, 0, 1]].normal?
# => true

[View source]
def orthogonal? #

Returns true if this is an orthogonal matrix

Matrix[[1, 0], [0, 1]].orthogonal?
# => true

[View source]
def permutation? #

Returns true if this is a permutation matrix

Matrix[[1, 0], [0, 1]].permutation?
# => true

[View source]
def pretty_print(pp) : Nil #

[View source]
def rank #

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

[View source]
def real #

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 ]

[View source]
def real? #

Returns true if this matrix contains real numbers, i.e. not Complex.

require "complex"
Matrix[[Complex.new(1, 0)], [Complex.new(0, 1)]].real?
# => false

[View source]
def rect #

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

[View source]
def regular? #

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


[View source]
def round(n = 0) #

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


[View source]
def row(i) #

Returns row vector number i of the Matrix as a Vector (starting at 0 like a good boy). Raises if the row doesn't exist.


[View source]
def row(i, &block : Vector -> ) #

Returns a block which yields every Vector in the row (starting at 0).


[View source]
def row?(i) #

Returns row vector number i of the Matrix as a Vector (starting at 0 like a good boy). Returns nil if the row doesn't exist.


[View source]
def row_count #

Returns the number of rows.


[View source]
def row_vectors #

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


[View source]
def singular? #

Returns true if this is a singular matrix.


[View source]
def size #

Return the row and column size as a Tuple(Int32, Int32)


[View source]
def square? #

Returns true if this is a square matrix.


[View source]
def swap_columns(col1, col2) #

Swaps col1 and col2


[View source]
def swap_rows(row1, row2) #

Swaps row1 and row2


[View source]
def symmetric? #

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


[View source]
def t #

ditto


[View source]
def to_a #

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


[View source]
def to_json(json : JSON::Builder) #

Convert the matrix to a json array


[View source]
def to_matrix #

Just in case


[View source]
def to_s(io) #

[View source]
def tr #

ditto


[View source]
def trace #

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

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

[View source]
def 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 ]

[View source]
def unitary? #

Returns true if this is a unitary matrix


[View source]
def unsafe_fetch(index : Int) #
Description copied from module Indexable(Apatite::Vector(T))

Returns the element at the given index, without doing any bounds check.

Indexable makes sure to invoke this method with index in 0...size, so converting negative indices to positive ones is not needed here.

Clients never invoke this method directly. Instead, they access elements with #[](index) and #[]?(index).

This method should only be directly invoked if you are absolutely sure the index is in bounds, to avoid a bounds check for a small boost of performance.


[View source]
def upper_triangular? #

Returns true if this matrix is a upper triangular matrix.


[View source]
def vstack(*matrices) #

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)
# => Matrix[[1, 2], [3, 4], [5, 6], [7, 8]]

[View source]
def zero? #

Returns true if this is a matrix with only zero elements


[View source]