Matrices - OE-FET/JISA GitHub Wiki

Matrices

Matrices are represented in JISA as Matrix<T> objects. Matrix employs a generic type, T, to allow for matrices "of" different types of elements.

There are three implementations built-in to JISA:

  • RMatrix for real numbers
  • CMatrix for complex numbers
  • MatrixMatrix for making matrices of matrices

Contents

  1. Creating a Real Matrix
  2. Creating a Complex Matrix
  3. Matrix of Matrices
  4. Creating Matrices of Other Types
  5. Populating a Matrix
    1. Setting All Elements at Creation
    2. Setting One Element
    3. Set Entire Rows or Columns
    4. Setting All Elements at Once
  6. Extracting Values and Submatrices
  7. Multiplication and Division
  8. Determinant, Singularity, Inversion and Left-Division
  9. Addition and Subtraction
  10. Kotlin Operator Overloading
  11. Mapping

Creating a Real Matrix

To create a matrix of real numbers, you just instantiate an RMatrix object like so:

RMatrix matrix = new RMatrix(numRows, numCols);

For instance, if we were to write:

RMatrix matrix = new RMatrix(4, 5);

then we will have just created:

RMatrix objects implement Matrix<Double> so, if needed, you can type them as such:

Matrix<Double> matrix = new RMatrix(2, 3);

You can create row and column matrices by using the Row and Column sub-classes like so:

RMatrix matrix = new RMatrix.Row(1, 2, 3, 4, 5);

would create:

or writing

RMatrix matrix = new RMatrix.Col(1, 2, 3, 4, 5);

would create:

You can also create an identity matrix of size n by using:

RMatrix identity = new RMatrix.Identity(n);

For instance:

RMatrix matrix = new RMatrix.Identity(4);

would create:

Creating a Complex Matrix

Creating a matrix of complex numbers can be done by instantiating the CMatrix class like so:

CMatrix matrix = new CMatrix(numRows, numCols);

Much like with RMatrix you can also create Row, Col and Identity matrices:

CMatrix row = new CMatrix.Row(elements...);
CMatrix col = new CMatrix.Col(elements...);
CMatrix idn = new CMatrix.Identity(size);

For instance:

CMatrix A = new CMatrix.Row(
    new Complex(1.0, 3.0),
    new Complex(0.2, 1.2),
    new Complex(1.4, 9.2)
);

CMatrix B = new CMatrix.Col(
    new Complex(1.0, 3.0),
    new Complex(0.2, 1.2),
    new Complex(1.4, 9.2)
);

CMatrix C = new CMatrix.Identity(3);

will result in the following:

Matrix of Matrices

You can create a matrix of matrices by using the MatrixMatrix<Type> class like so:

Matrix<Matrix<Type>> = new MatrixMatrix<Type>(zero, unity, numRows, numCols);

where zero and unity are the matrices to be considered as zero and unity respectively (ie zero matrix and identity matrix).

For example:

Matrix<Matrix<Double>> matrix = new MatrixMatrix(
    new RMatrix(2,2), new RMatrix.Identity(2),
    3, 3
);

creates a Matrix that looks like this:

Then we can do something like:

Creating Matrices of Other Types

You can create matrices that contain any type of object you wish. However, you need to define what it means to perform the following operations on such objects:

  • Addition
  • Subtraction
  • Multiplication
  • Division
  • Copying / Cloning

as well as defining what values correspond to zero and unity for these objects.

To create such a matrix, we will first need to create a so-called "factory" object. We can then use our factory object to create as many of these custom matrices as we like without having to re-define the operations each time. Creating such a factory is done like so:

MatrixFactory<Type> factory = new MatrixFactory<>(
    zeroValue,
    unityValue,
    (a, b) -> // How to add b to a
    (a, b) -> // How to subtract b from a
    (a, b) -> // How to multiply a by b
    (a, b) -> // How to divide a by b
    (a)    -> // How to create a copy of a
);
val factory = MatrixFactory<Type>(
    zeroValue,
    unityValue,
    { a, b -> ... }, // Add b to a
    { a, b -> ... }, // Subtract b from a
    { a, b -> ... }, // Multiply a by b
    { a, b -> ... }, // Divide a by b
    { a    -> ... }  // Copy a
)
factory = MatrixFactory(
    zeroValue,
    unityValue,
    lambda a, b : ..., # Add b to a
    lambda a, b : ..., # Subtract b from a
    lambda a, b : ..., # Multiply a by b
    lambda a, b : ..., # Divide a by b
    lambda a : ...     # Copy a
)

You can then create matrices like so:

Matrix<Type> matrix = factory.create(numRows, numCols);

For instance, let's say we wanted to create matrices of fractional values represented as Fraction objects (from the apache commons maths library), ie values like:

Fraction twoThirds = new Fraction(2.0, 3.0);

then we would create a factory like so:

MatrixFactory<Fraction> factory = new MatrixFactory<>(
    Fraction.ZERO,
    Fraction.ONE,
    (a, b) -> a.add(b),
    (a, b) -> a.subtract(b),
    (a, b) -> a.multiply(b),
    (a, b) -> a.divide(b),
    (a) -> new Fraction(a.getNumerator(), a.getDenominator())
);

then we can create Fraction matrices using create(...). For example:

Matrix<Fraction> matrixA = factory.create(2, 2,
    new Fraction(2, 3), new Fraction(3, 11),
    new Fraction(1, 6), new Fraction(5, 9)
);

will result in:

You can also create row, column and identity matrices:

Matrix<Fraction> row = factory.createRow(new Fraction(1,3), new Fraction(1,6), ...)
Matrix<Fraction> col = factory.createCol(new Fraction(1,3), new Fraction(1,6), ...)
Matrix<Fraction> idn = factory.createIdentity(4); // 4x4 identity matrix

Alternatively, you can extend the AbtractMatrix<Type> abstract class to create your own Matrix classes.

public class FractionMatrix extends AbstractMatrix<Fraction> {

    public FractionMatrix(int rows, int cols) {
        super(Fraction.ZERO, Fraction.ONE, rows, cols);
    }

    public Fraction add(Fraction a, Fraction b) {
        return a.add(b);
    }

    public Fraction subtract(Fraction a, Fraction b) {
        return a.subtract(b);
    }

    public Fraction multiply(Fraction a, Fraction b) {
        return a.multiply(b);
    }

    public Fraction divide(Fraction a, Fraction b) {
        return a.divide(b);
    }

    public Fraction copy(Fraction a) {
        return new Fraction(a.getNumerator(), a.getDenominator());
    }

}

thus allowing:

Matrix<Fraction> matrix = new FractionMatrix(numRows, numCols);

Populating a Matrix

The first thing you will likely want to do is populate your matrix with values. By default, newly constructed matrices will contain all zeros to being with. To set what you want the values to be there are several methods available:

matrix.set(...);          // Set one element
matrix.setRow(...);       // Set all elements in one row
matrix.setCol(...);       // Set all elements in one column
matrix.setDiagonal(...);  // Set all elements in leading diagonal
matrix.setAll(...);       // Set all elements at once

In this section T is used in place of the type of value being stored in the matrix (ie Double for real matrices or Complex for complex).

Setting All Elements at Creation

You can define the value of each element when creating most types of matrix by listing them in order. For example, when creating an RMatrix:

RMatrix matrix = new RMatrix(int numRows, int numCols, T... elements);

where T... indicates a variable number of arguments of type T. For instance we could create the following:

RMatrix matrix = new RMatrix(3, 3, 1, 2, 3, 4, 5, 6, 7, 8, 9);

resulting in the following:

To make the code more readable (and to make it look more like the matrix itself) you could split it over multiple lines like so:

RMatrix matrix = new RMatrix(3, 3,
    1, 2, 3, 
    4, 5, 6, 
    7, 8, 9
);

Alternatively, you can define an RMatrix using a MATLAB-style string:

RMatrix matrix = new RMatrix("[ 1, 2, 3; 4, 5, 6; 7, 8, 9 ]");
// or without [...]
RMatrix matrix = new RMatrix("1, 2, 3; 4, 5, 6; 7, 8, 9");

Setting One Element

Setting the value of one element at a time can be achieved by use of set(...):

matrix.set(int row, int col, T value);

For example, if we have the following:

RMatrix matrix = new RMatrix(2,2);

Then calling:

matrix.set(0, 1, 3.5);

will cause the following change:

Set Entire Rows or Columns

Setting entire rows/columns can be achieved by use of the following methods:

matrix.setRow(int rowIndex, T... values);
matrix.setCol(int colIndex, T... values);

Where T... indicates a variable number of T arguments.

For instance, let's say we had the following:

RMatrix matrix = new RMatrix(2, 5);

We could set the entire first row by calling:

matrix.setRow(0, 4, 3, 7, 8, 4);

causing the following change:

Then, by calling:

matrix.setCol(3, 2, 4);

we would get:

You can also do this by supplying a row or column matrix respectively:

RMatrix rowMatrix = new RMatrix.Row(1, 2, 3, 4, 5);
RMatrix colMatrix = new RMatrix.Col(12, 32);
RMatrix matrix    = new RMatrix(2, 5);

matrix.setRow(0, rowMatrix);
matrix.setCol(3, colMatrix);

resulting in the following changes:

Setting All Elements at Once

Similar to how you can define all elements when creating most types of Matrix, you can set the value of all elements after creation by use of setAll(...):

matrix.setAll(T... values);

where T... indicates a variable number of T arguments. The number you supply should equal the number of elements there are in the matrix.

For example, if we have the following Matrix:

RMatrix matrix = new RMatrix(3, 3);

then calling the following:

matrix.setAll(
    1, 2, 3,
    4, 5, 6,
    7, 8, 9
);

will result in the matrix becoming:

Extracting Values and Submatrices

In this section Type is a place-holder for whatever type of elements are stored in the matrix. (Replace with Double for real numbers or Complex for complex numbers).

Inidividual matrix elements can be extracted by use of get(...) like so:

Type value = matrix.get(row, column);

Entire rows and columns can also be extracted a arrays like so:

Type[] row    = matrix.getRowArray(row);
Type[] column = matrix.getColumnArray(column);

You can also extract rows and columns as sub-matrices by use of:

Matrix<Type> row    = matrix.getRowMatrix(row);
Matrix<Type> column = matrix.getColumnMatrix(column);

Any arbitrary sub-matrix can be extraced by use of getSubMatrix(...):

Matrix<Type> sub = matrix.getSubMatrix(startRow, endRow, startCol, endCol);

For instance, if matrix was:

then calling:

Matrix<Double> sub = matrix.getSubMatrix(1, 2, 0, 1)

will result in sub containing:

Multiplication and Division

Matrix multiplication is performed by use of the multiply(...) method. For instance if we have two matrices A and B, then we can multiply them by use of:

Matrix<Type> A = ...;
Matrix<Type> B = ...;
Matrix<Type> C = A.multiply(B);

Similarly, matrix "division" (ie multiplying A by the inverse of B) can be done by calling:

Matrix<Type> C = A.divide(B);

Both of these operations can be done element-wise instead. That is instead of performing matrix multiplication, a new matrix is instead created by multiplying the same elements in each matrix also known as the Hadamard product:

Matrix<Type> C = A.elementMultuply(B);
Matrix<Type> D = A.elementDivide(B);

Mathematically this would be written as:

You can also multiply/divide all elements by a single value by supplying multiply(...) or divide(...) with a single value. For instance, if we had a real matrix we could multiply/divide all elements by 3 like so:

Matrix<Double> A = ...;
Matrix<Double> B = a.multiply(3.0);
Matrix<Double> C = a.divide(3.0);

which would be written mathematically as:

Determinant, Singularity, Inversion and Left-Division

You can easily calculate the determinant of a Matrix, whether it is singular and its inverse by use of the following methods:

Type          detA     = A.getDeterminant();
boolean       sqaure   = A.isSquare();
boolean       singular = A.isSingular();
Matrix<Type>  inverse  = A.getInverse();

Further to this, if you have the following problem:

where you know A and B but want to find X, then you can use the leftDivide(...) method like so:

Matrix<Type> X = A.leftDivide(B);

Addition and Subtraction

Adding and subtracting two matrices of equal size (ie subtracting each element) can be done by use of add(...) and subtract(...).

Matrix<Type> C = A.add(B);
Matrix<Type> D = A.subtract(B);

These can also be used with scalar values. For instance, 12.0:

Matrix<Double> E = A.add(12.0);
Matrix<Double> F = A.subtract(12.0);

Kotlin Operator Overloading

If you are using Matrix objects in Kotlin then you can take advantage of Kotlin's operator overloading:

val A = RMatrix(...)
val B = RMatrix(...)
                        // Same as calling:
                        // ========================
A[r, c] = value         // A.set(r, c, value)
val element = A[r, c]   // val element = A.get(r,c)
  
val C = A + B           // A.add(B)
val D = A - B           // A.subtract(B)
val E = A * B           // A.multiply(B)
val F = A / B           // A.divide(B)

For example, if we had the following in Java:

RMatrix A = new RMatrix(2, 2);
A.set(0, 0, 12);
A.set(1, 1, 25);

RMatrix B = new RMatrix(2, 2);
B.set(0, 1, 15);
B.set(1, 1, 12);

RMatrix C = A.multiply(B);
RMatrix D = A.add(B);

we could write this in Kotlin like so:

val A = RMatrix(2, 2)
A[0, 0] = 12.0
A[1, 1] = 25.0

val B = RMatrix(2, 2)
B[0, 1] = 15.0
B[1, 1] = 12.0

val C = A * B
val D = A + B

Mapping

If we wanted to create a new matrix, B, where each element is a function of the same element in a previous matrix, A, then we can use the map(...) method. This needs to be supplied with a Function object/lambda that represents the mapping between A and B:

Matrix<Type> B = A.map(GFunction<Type, Type> mapping);

For example, if A is a real matrix and we wanted B such that

then we would write

Matrix<Double> B = A.map(x -> x*x + 2);

For example, taking a 3x3 matrix

You can also map with (row,col) indices present:

Matrix<Type> B = A.map((row,col,x) -> ...);

For example, you may want to use different mappings on every other row:

Matrix<Double> B = A.map((row, col, x) -> {

    if (row % 2 == 0) {
        return x*x;    // x^2 if even
    } else {
        return x*x*x;  // x^3 if odd
    }

});

You can map a matrix onto itself (instead of creating a new one) by using mapSelf(...) much in the same way:

A.mapSelf(x -> ...);
// or
A.mapSelf((row, col, x) -> ...)

You can also map one type of matrix to another. In this case, you need to provide the destination matrix as an argument.

Matrix<V> mapFrom = ...; // NxM
Matrix<U> mapTo   = ...; // NxM

mapFrom.map(mapTo, GFunction<U,V> mapper);

For instance, say we have a matrix of complex numbers and we want a matrix of only the real parts:

Matrix<Complex> comp = ...;
Matrix<Double>  real   = new RMatrix(comp.rows(), comp.cols());

comp.map(real, v -> v.getReal());
comp.map(x -> x.pow(0.5));
⚠️ **GitHub.com Fallback** ⚠️