nanoMat
A really small C++ matrix package
Author: Justin Domke
Contact: (the last name of the author)@cs.umd.edu
History:
First version written June 13-14, 2008.
The library is one file: nanoMat.cpp
Like many other people, I
finally broke down and wrote my own c++ matrix "library". In particular, I wanted to make it as easy
as possible to take a simple matlab function and reimplement it in C++. (Regular C is not an option,
since this library makes heavy use of operator overloading.)
Disadvantages
The disadvantages of nanoMat are:
- It isn't particularly fast. Though of course nanoMat will crush matlab, if you want the
best possible performance, you should use boost or blitz or something else.
- It only handles 2d arrays
- It only represents doubles. (No templates)
- There are no included functionality for doing anything other than basic matrix multiplication,
sums, repmat, etc.
Advantages
The advantages of nanoMat are:
(There are no other advantages.) More specifically,
- Matrix multiplication, etc. work the way they are supposed to. Namely, you write
C = A * B + D;
Not something horrible like
assign(C,add(mult(A,B),D)); // don't do this .
-
There is no "installation". It is just a single
.cpp file. To use it, you put this file somewhere your compiler will
find it, and then put the lines
#define nanoMatDebug 1 // 0 for faster and more unsafe
#include "nanoMat.cpp"
In your file where you want to manipulate matrices. There are no external
dependencies. There is one constructor, namely
nanoMat(const mxArray *matlab_array); that integrates with matlab. If you don't have
matlab, comment it out. There is nothing else matlab specific, and you can just use a different
constructor.
- Integration with matlab .mex files is trivial. If you want to pass an array from matlab, you just
write
nanoMat X = nanoMat(prhs[0]);
You can then immediately use X, passed as the first argument to the .mex file.
Moreover, this is done in place. If you now modify the elements of X, the changes are made
directly in the memory of matlab. There is no need to convert anything prior to returning.
An example
Here is a really simple example of making a .mex file. Here is the entire code:
//mexplay.cpp
#include "math.h"
#include "mex.h" //--This one is required
#define nanoMatDebug 1 // 1 for safety/error checking; 0 for faster and unsafe
#include "nanoMat.cpp"
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]){
nanoMat A = nanoMat(prhs[0]);
nanoMat B = nanoMat(prhs[1]);
A += B;
return;
}
To use it, you do
>> mex mexplay.cpp
>> A = ones(5)
A =
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
>> B = reshape(1:25,5,5)
B =
1 6 11 16 21
2 7 12 17 22
3 8 13 18 23
4 9 14 19 24
5 10 15 20 25
>> mexplay(A,B);
>> A
A =
2 7 12 17 22
3 8 13 18 23
4 9 14 19 24
5 10 15 20 25
6 11 16 21 26
Allowed operations
Some examples of basically all the operations.
nanoMat A; // initialize somehow
nanoMat B;
nanoMat C;
double d;
// initialize somehow, A and B both NxN
A(2,7)=2.3; // same as A[3,8]=2.3 in matlab. indices start at 0 in C++, 1 in matlab
A(14) =2.3; // same as A[15]=2.3 in matlab. indices start at 0 in C++, 1 in matlab
C = A*B; // matrix multiplication
C = A%B; // elementwise multiplication (C = A.*B in matlab)
C = A/B; // elementwise division
C = A+B; // elementwise addition
C = A-B; // elementwise addition
C = A*d; // multiply with a double
C = d*A; // multiply with a double
C = A/d; // divide each element by a double
C = d/A; // divide d by each element of A
C = A+d; // etc...
C = d-A; //
// etc;
A%=B; // same as A = A%B, but MORE EFFICIENT (no copies)
A+=B; // same as A = A+B, but MORE EFFICIENT (no copies)
A-=B; // same as A = A-B, but MORE EFFICIENT (no copies)
A/=B; // same as A = A/B, but MORE EFFICIENT (no copies)
// A *= B DOES NOT EXIST. (there would be no speed advantage)
A*=d; // same as A = A*d, but MORE EFFICIENT
A/=d; // same as A = A/d, but MORE EFFICIENT
A+=d; // same as A = A+d, but MORE EFFICIENT
A-=d; // same as A = A-d, but MORE EFFICIENT
C = A.T(); // transpose
C = A.sum(1); // sum columns (if A is MxN, then C is 1xN)
C = A.sum(2); // sum rown (if A is MxN, then C is Mx1)
C = A.repmat(2,3); // like repmat(A,2,3) in matlab
C = A.row(i); // get i-th row (for first row, i=0)
C = A.col(i); // get i-th column (for first column, i=0)
// the following is VERY DANGEROUS. It makes a pointer into the matrix of A
// Never use this unless you are sure you know what you're doing.
// A.col() has identical functionality
C = A.colD(i);
A.row_eq(i,C); // sets ith row of A to be C. (if A is MxN, C must be 1xN)
A.col_eq(i,C); // sets ith col of A to be C. (if A is MxN, C must be Mx1)
C = A.map(&sin); // "maps" the function sin over A. Same as C = sin(A) in matlab
A.map_eq(&sin); // same as A = A.map(&sin), but faster
Things that should be done
- Currently, this uses the naive O(N^3) algorithm for matrix multiplication. A faster algorithm will
make a huge difference for big matrices
How it works (if for some reason you care)
The arrays are stored in column major format. (i.e. as a single long vector of doubles, with the
columns "stacked on top of one another"). This is necessary to allow in place passing of matlab
arrays.
I placed a very high emphasis that matrix multiplication should look like
C = A * B;
Because operators cannot return results by reference, I decided that the way to proceed was to return
regular nanoMat objects. Done in a naive way, this results in a lot of temporary copies of all
results. To avoid this, functions like matrix multiplication. etc., set a special flag (dalloc). The
copy constructor and assignment use this flag to determine if they need to copy the memory representing the
array, or just set the pointer. This means that if you do the above matrix multiplication, the
dimensions of the result get wastefully copied several times, but the actual data does not. This
overhead is not large for small matrices, and totally irrelevant for big ones.
|