• matrix.rb

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 122
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 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) 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 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) 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 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) click to toggle source

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

```Matrix.identity(2)
=> 1 0
0 1```
```
# File matrix.rb, line 190
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 248
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 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) 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 133
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 180
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 204
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 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) 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 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) click to toggle source

```Matrix.scalar(2,5) + Matrix[[1,0], [-4,7]]
=>  6  0
-4 12```
```
# 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) click to toggle source

Matrix subtraction.

```Matrix[[1,5], [4,2]] - Matrix[[9,3], [-4,1]]
=> -8  2
8  1```
```
# 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) 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 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) click to toggle source

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

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

other.compare_by_row_vectors(@rows)
end
```
Also aliased as: eql?
[](i, j) click to toggle source

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

```
# File matrix.rb, line 265
def [](i, j)
@rows[i][j]
end
```
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 424
def clone
Matrix.rows(@rows)
end
```
coerce(other) click to toggle source

FIXME: describe coerce.

```
# 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() 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 { |i| i**2 }
=> 1  4
9 16```
```
# File matrix.rb, line 327
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 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() 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 282
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 832
def column_vectors
columns = (0 .. column_size - 1).collect {
|i|
column(i)
}
columns
end
```
compare_by_row_vectors(rows) click to toggle source

Not really intended for general consumption.

```
# 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() click to toggle source
Alias for: determinant
determinant() click to toggle source

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

```Matrix[[7,6], [3,9]].determinant
=> 63```
```
# 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
```
Also aliased as: det
eql?(other) click to toggle source
Alias for: ==
hash() click to toggle source

Returns a hash-code for the matrix.

```
# 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() click to toggle source

Overrides Object#inspect

```
# File matrix.rb, line 864
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 579
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 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() 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 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() click to toggle source

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```
```
# 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?() click to toggle source

Returns `true` if this is a regular matrix.

```
# File matrix.rb, line 374
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 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() click to toggle source

Returns the number of rows.

```
# File matrix.rb, line 272
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 821
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 381
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 389
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 843
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 854
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 777
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 797
def transpose
Matrix.columns(@rows)
end
```
Also aliased as: t

Commenting is here to help enhance the documentation. For example, code samples, or clarification of the documentation.

If you have questions about Ruby or the documentation, please post to one of the Ruby mailing lists. You will get better, faster, help that way.

If you wish to post a correction of the docs, please do so, but also file bug report so that it can be corrected for the next release. Thank you.

If you want to help improve the Ruby documentation, please visit Documenting-ruby.org.