-
Notifications
You must be signed in to change notification settings - Fork 133
Getting started
NMatrix is a linear algebra library for Ruby, written mostly in C. It can be used with or without SciRuby, but is part of the SciRuby project. NMatrix was inspired by and based heavily upon NArray, by Masahiro Tanaka.
See Installation for details.
Generally, you'd only need gem install nmatrix
. If you want to install from source:
git clone git://github.com/SciRuby/nmatrix.git
cd nmatrix
rake install
In addition to dense matrices, NMatrix supports two types of sparse storage (list-of-list and Yale). We support the following data classes:
- Byte (unsigned 8-bit integer)
- Integer (8, 16, 32, and 64-bit)
- Floating point (32 and 64-bit)
- Complex (64 and 128-bit)
- Rational (32, 64, and 128-bit)
- Ruby objects
NMatrix makes use of and exposes the CBLAS API included in ATLAS. However, we have also written BLAS templates for NMatrix-supported data types that are not included in ATLAS or LAPACK (e.g., rationals).
In contrast to ruby's Matrix
class, and scipy.matrix
, the *
operator is used for element-wise multiplication and the #dot method for matrix-matrix or matrix-vector multiplication.
Currently, you create NMatrix objects with the NMatrix#new
method. The first parameter is the dimensions of the matrix, and the second parameter contains the initial values. For example, the following creates a 3x4 matrix filled with zeros:
>> n = NMatrix.new( [3, 4], 0)
>> n.pretty_print
0 0 0 0
0 0 0 0
0 0 0 0
=> nil
If the first parameter is an integer, NMatrix.new
returns a square matrix.
>> n = NMatrix.new( 4, 0) # Same as NMatri.new( [4,4], 0 )
>> n.pretty_print
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
=> nil
Matrices need not have only two dimensions:
# Create a 3D matrix (4 x 4 x 4)
b = NMatrix.new([4,4,4], 0)
In addition, the second parameter to NMatrix.new
can be an array which specifies all the values in the matrix:
>> B = NMatrix.new([2,5], [1,2,3,4,5,6,7,8,9,0])
>> B.pretty_print
1 2 3 4 5
6 7 8 9 0
If the array of values has fewer elements than the matrix requires, NMatrix.new
will repeat the array as many times as required:
>> q = NMatrix.new([3,6], [1,2,3])
>> q.pretty_print
1 2 3 1 2 3
1 2 3 1 2 3
1 2 3 1 2 3
Thanks to Daniel Carrera, we have an in-line constructor that makes creating matrices a very simple and readable operation:
a = N[ 1,2,3,4 ] # => 1.0 2.0 3.0 4.0
a = N[ 1,2,3,4, :int32 ] # => 1 2 3 4
a = N[ [1,2,3], [3,4,5] ] # => 1.0 2.0 3.0
3.0 4.0 5.0
NVector is a short-cut for a one-dimensional matrix.
>> x = NVector.new(5, [1,2,3,4,5])
>> x.pretty_print
1 2 3 4 5
=> nil
NMatrix supports the standard arithmetic operations on matrices:
# a = NMatrix.new([3,3], 2)
a + a # Element-wise / Matrix addition.
a - a # Element-wise / Matrix subtraction.
a ** 3 # Element-wise exponentiation.
a / 2 # Element-wise division.
a * a # Element-wise multiplication.
a.dot a # Matrix dot-product multiplication.
Daniel Carrera and Carlos Agarie have written some shortcuts for NMatrix and NVector. They are as follows:
ones(3,3) # A 3x3 matrix of ones.
zeros(4,4) or zeroes(4,4) # Creates a matrix of zeros with dimensions as parameters.
identity(5,5) or eye(5,5) # A 5x5 identity matrix. Only works with NMatrix.
seq(8) # An 8-vector with a sequence of values from 0 to 7.
random(6,6) # A 6x6 matrix of random values between 0 and 1.
row(0) # Return the 1st column of the original matrix. Only works with NMatrix.
column(2) # Return the 3rd column of the original matrix. Only works with NMatrix.
indgen(8) # To make IDL users happy. Same as `seq(8, :int32)`.
findgen(8) # Same as `seq(8, :float32)`.
bindgen(8) # Same as `seq(8, :byte)`.
cindgen(8) # Same as `seq(8, :complex64)`.
linspace(0, pi, 100) # A vector of 100 values from 0 to pi, inclusive. Only works with NVector.
FIXME: How to solve "A x = b"?
FIXME: How to do standard decompositions (QR, LU, SVD)? It's VERY important to be able to get the SVD of a sparse matrix in lots of data analysis and machine learning applications.
NMatrix supports several data types. NMatrix will try to guess the dtype based on the first initial value you provide (e.g., NMatrix.new(4, [0.0, 1]) will be :float32), but you can choose to provide a dtype in addition to or in lieu of initial values:
>> n = NMatrix.new(4, [0,1], :rational128)
>> n.pretty_print
0/1 1/1 0/1 1/1
0/1 1/1 0/1 1/1
0/1 1/1 0/1 1/1
0/1 1/1 0/1 1/1
>> m = NMatrix.new(4, :int64) # no initialization of values
The storage type (stype) can also be specified, prior to the dimension argument. However, with sparse storage formats, initial values don’t make sense, and these matrices will contain zeros by default.
# empty list-of-lists-of-lists 4x3x4 matrix
n = NMatrix.new(:list, [4,3,4], :int64)
# Ruby objects in a 'Yale' sparse matrix
m = NMatrix.new(:yale, [5,4], :object)
# A byte matrix containing a gradient
o = NMatrix.new(:dense, 5, [0,1,2,3,4], :byte)
The matrix m created above is a Yale-format sparse matrix, or more specifically, “new Yale,” which differs from “old Yale” in that the diagonal is stored separately from the non-diagonal elements. Thus, diagonals can be accessed and set in constant time.
Currently, all storage is row-based.
You can also convert between any of these three stypes using cast:
n = NMatrix.new(:list, 4, :int64)
n[0,0] = 5
n[0,3] = -2
dense = n.cast(:dense, :int64)