Skip to content
John Woods edited this page Sep 9, 2013 · 10 revisions

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.

Installation

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

Features

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

Differences from other matrix libraries

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.

Brief Tutorial

Creating matrices

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)
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 )
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])
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])
1  2  3  1  2  3
1  2  3  1  2  3
1  2  3  1  2  3

Simpler constructor for matrices

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

NVector is a short-cut for a one-dimensional matrix.

>> x = NVector.new(5, [1,2,3,4,5])
1  2  3  4  5
=> nil

Matrix functions and operations (wishlist)

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.

Shortcuts

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.

Solving linear systems

FIXME: How to solve "A x = b"?

Matrix decomposition

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.

Advanced features

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)
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)
Clone this wiki locally