Simple C++ library for dealing with matrices.
Some of the advanced functionality of simple-matrix include
- Determinants of any sized square matrix
- Square matrix inverting
- Minors and cofactors
- Transpositions
- Adjugates
- Solving equations
simple-matrix can be compiled into a static library using GNU Make simply by typing make
in this directory.
A test executable can be created with make test
and object files can be cleared with make clean
.
To reset the directory by removing all produced binaries, including the generated library, make reset
can be executed.
Alternatively, make all
can be called to create the library, the tests and the examples.
All library functions, classes and constants are defined within the simple_matrix
namespace, this can be imported through
using namespace simple_matrix;
For convenience, we will assume that this namespace has been imported for the code snippets.
We can create a matrix of size m by n filled with zeros
matrix A(m, n);
Optionally, at initialisation, we can specify the entries within the matrix
matrix B(3, 2, {1, 3, 5, 2, 4, 6});
We can also use a string to initialise the matrix
matrix C("[1, 3; 5, 2; 4, 6]");
Or even from an input stream
matrix D;
std::cin >> D;
It's worth noting that using this style, the matrix is filled from the top-left corner rightwards, and then down.
The matrix object can simply be outputted to any output stream
matrix B(3, 2, {1, 2, 3, 4, 5, 6});
std::cout << B << std::endl;
[ 1, 2; 3, 4; 5, 6 ]
This is formatted in such a way that it can be re-inputted into the matrix.
We can also print the matrix in a more user friendly way
matrix B(3, 2, {1, 2, 3, 4, 5, 6});
std::cout << B.pretty() << std::endl;
┌─ ─┐
│ 1 2 │
│ 3 4 │
│ 5 6 │
└─ ─┘
Given any two matrices strictly of the same size, we can add or subtract them
matrix A(3, 2), B(3, 2);
// ...
matrix C = A + B;
// or
matrix C = A;
C += B;
Or even subtract
matrix C = A - B;
Given a matrix of any size, we can multiply all the elements of the matrix by the same scalar constant
matrix A;
double c;
// ...
matrix B = A * c;
// or
matrix B = c * A;
// or
matrix B = A;
B *= c;
Given two matrices A and B, if the number of columns in A equals the number of rows in B, then we can calculate A * B. NOTE: As matrix multiplication is not commutative, this is not the same as B * A.
matrix A, B;
// ...
assert(A.n() == B.m());
matrix C = A * B;
// or
matrix C = B;
C *= A;
The second example may be confusing, this order was decided on as this would make more sense when using matrices as a map between vectors, such as transformations in 3D or 2D.
Indexes in simple-matrix use zero-based indexing. This means if you want the top-left element of a matrix, this would be referenced as MyMatrix(0, 0)
.
This may be changed in the future.
Individual elements may be accessed through setter/getter functions. We reference elements in a matrix through row by column, usually denoted by variables i
and j
respectively.
Suppose we have the following matrix
matrix A(3, 3, {1, 2, 3, 4, 5, 6, 7, 8, 9);
We can get, say the top-right element of the matrix as A(0, 2)
as this is the first row, third column (remember zero-based indexing).
We could also set the top-right element to 8.5
A(0, 2) = 8.5;
This modified the matrix A itself and doesn't create a new one.
We can get and set individual rows and columns in a simular manner to elements. If we want to access a row, we refer to the ith row, if we want to access a column, we refer to the jth column. We can simply get rows and columns as matrices
matrix A(3, 4);
// ...
matrix row = A.get_row(1); // Gets the second row
assert(row.m() == 1 && row.n() == A.n());
matrix col = A.get_col(2); // Gets the third column
assert(col.m() == A.m() && col.n() == 1);
If you'd rather have a row as a column vector or vice-versa, you can always transpose the row/column.
If, on the other hand, you'd like to set whole rows or columns, you can do that in a simular manner to element setting.
matrix row(1, 4);
matrix col(3, 1);
matrix A(3, 4);
// ...
A.set_row(2, row); // Sets the third row to 'row'
A.set_col(3, col); // Sets the fourth column to 'col'
This does change the matrix A
but leaves row
and col
unmodified.
We can simply and efficiently calculate the determinant of a square matrix
matrix A;
// ...
assert(A.is_square()); // We can only calculate determiants for square matrices
double det = A.det();
Determinants are an extremely useful tool for dealing with matrices, if you would like to know about them, click here
The transpose operator is a simple operation that 'flips' the matrix over on its diagonal. Given a mxn matrix, a transposition would give an nxm matrix.
Consider a matrix object
matrix mat(2, 3, {1, 2, 3, 4, 5, 6});
cout << mat.pretty() << endl;
matrix mat_t = mat.transpose();
cout << mat_t.pretty() << endl;
Gives the output
┌─ ─┐
│ 1 2 3 │
│ 4 5 6 │
└─ ─┘
┌─ ─┐
│ 1 4 │
│ 2 5 │
│ 3 6 │
└─ ─┘
TODO
TODO
TODO
With simple-matrix, a square matrix can be simply inverted with matrix::invert()
.
matrix mat(3, 3, {1, 2, 3, 4, 5, 6, 7, 8, 8});
cout << mat.pretty() << endl;
matrix mat_inv = mat.invert();
cout << mat_inv.pretty() << endl;
cout << (mat * mat_inv).pretty() << endl;
┌─ ─┐
│ 1 2 3 │
│ 4 5 6 │
│ 7 8 8 │
└─ ─┘
┌─ ─┐
│ -2.66667 2.66667 -1 │
│ 3.33333 -4.33333 2 │
│ -1 2 -1 │
└─ ─┘
┌─ ─┐
│ 1 0 0 │
│ 0 1 0 │
│ 0 0 1 │
└─ ─┘
Keep in mind that a matrix can only be inverted if it is square and if the determinant of the matrix is not zero.
If the matrix is not square, an exception of type simple_matrix::not_square
will be thrown. If the determinant is zero, an exception of type simple_matrix::not_invertible
will be thrown.
TODO