First NMatrix Alpha Released

Warning: Code in this blog post is very old and likely will not work with the current version of NMatrix. Please check the NMatrix wiki for the most recent information.

Two months ago, I mentioned the existence of a prototype Ruby linear algebra library, written in C.

I am pleased to announce that yesterday we released our first alpha of said library, NMatrix v0.0.1.

Creating Matrices

There are lots of different ways to create matrices. The first and easiest is to supply dimensions and initial data:

``````>> n = NMatrix.new(4, 0) # a square 4x4 dense zero matrix
=> #<NMatrix:0x9a57e14shape:[4,4] dtype:int32 stype:dense>
>> n.pretty_print
0  0  0  0
0  0  0  0
0  0  0  0
0  0  0  0
=> nil
``````

Data Types

You may notice that this first matrix defaulted to dtype=:int32. 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)
=> #<NMatrix:0x9959e04shape:[4,4] dtype:rational128 stype:dense>
>> 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
>> m.pretty_print
-1217641248  161386160  161386100  161385680
161385640  161385520  161385420  161384180
161384120  161381060  161381020  161412140
161411940  161411880  161411840  160879240
=> nil
``````

Storage Formats

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.

Conversion

You can also convert between any of these three stypes using cast, e.g.,

``````n = NMatrix.new(:list, 4, :int64)
n[0,0] = 5
n[0,3] = -2
dense = n.cast(:dense, :int64)
``````

Vectors

Currently, only dense vectors are implemented as a child class of NMatrix, and creation is similar:

``````>> nv = NVector.new(5, :int64)
=> #<NVector:0x9a62328shape:[5,1] dtype:int64 stype:dense orientation:column>
``````

Math Operations

Most element-wise mathematical operations are supported for Yale and dense types. These use the basic operators (e.g., +, -, /, *, ==).

For non-element-wise matrix multiplication, use the dot instance method of NMatrix. Whole-matrix comparison (returning a single boolean value) is the equal? or eql? method.

Much more remains to be written than has been completed. Here are some of our key priorities:

• determinants
• matrix-vector multiplication for Yale