Matrix of quantities¶
Vectors and Matrices are one-dimensional and two-dimensional mathematical data containers for Quantity values, where each instance of a Vector or Matrix contains values of one specific quantity. A Vector or Matrix has a displayUnit for the entire vector or matrix. Internally, vectors and matrices store all values in their SI or BASE unit, just like the Quantity.
Larger Vectors and Matrices are implemented in four different ways: Sparse or Dense data storage, combined with Double or Float precision, which gives four combinations. Sparse storage should be used for vectors or matrices that contain many zero values. Dense data storage would, in that case, store all the zeros, whereas in a sparse storage only the numbers unequal to zero are stored, together with an index. As the index adds some overhead, sparse storage only makes sense when the number of zeros is over 50% of the number of entries.
Matrix types¶
Several implementations of matrices exist, which are shown in the UML class diagram below:

As can be seen, the abstract class Matrix extends the abstract class VectorMatrix, which contains numerous methods for operations that are common to matrices, vectors and tables. Matrix multiplication is explicitly missing in the VectorMatrix class, since the QuantityTable, which extends VectorMatrix, does not implement matrix multiplication by design. The abstract Matrix class adds matrix multiplication to the VectorMatrix class. Square matrices as defined in the SquareMatrix abstract class contain additional methods that only make sense for square matrices, such as determinant(), trace(), inverse(), and adjugate().
The generic type of Matrix of any size is the MatrixNxM. This matrix can use sparse or dense storage, and be populated with single-precision float values or double precision double values.
The generic type of SquareMatrix of any size is MatrixNxN. This matrix can also use sparse or dense storage, and store float values or double values. For efficiency reasons, since the MatrixNxN carries some overhead for the flexible data storage, separate classes are defined for Matrix1x1, Matrix2x2, and Matrix3x3. Data inside these matrices is stored in a dense double[] array that uses row-major indexing.
Matrix operations¶
A Matrix implements the Hadamard interface for entry-by-entry operations. These include:
invertEntries(): Invert the matrix on an entry-by-entry basis (1/value), where the unit will also be inverted. The inversion of aDurationmatrix will result in a matrix of the same type (1x1, 2x2, 3x3, NxN, NxM) and size (number of rows and columns), with a unit of1/s, corresponding to aFrequencymatrix.multiplyEntries(Matrix other): Multiply the entries of this matrix on an entry-by-entry basis with those of another matrix of the same type and size (but generally containing values of another quantity).divideEntries(Matrix other): Divide the entries of this matrix on an entry-by-entry basis by those of another matrix of the same type and size (but generally containing values of another quantity).multiplyEntries(Quantity<?, ?> quantity): Multiply the entries of this matrix on an entry-by-entry basis with the provided quantity.divideEntries(Quantity<?, ?> quantity): Divide the entries of this matrix on an entry-by-entry basis by the provided quantity.
All Hadamard operations result in a new instance of the Matrix with a new unit, but of the same type (Matrix2x2, MatrixNxM, etc.) and with the same number of rows and columns.
The result of a Hadamard operation on, e.g. a MatrixNxM<Speed> will typically be a MatrixNxM<SIQuantity> since the inverse operation, multiplication or division will result in a Matrix with a unit that is unknown beforehand and cannot be determined by the compiler. In the above example of invertEntries for a Duration matrix, the resulting matrix can be transformed into a proper MatrixNxM<Frequency> matrix using the as(Frequency.Unit.Hz) method.
If a MatrixNxM is internally of a size congruent with a specific matrix or vector type, e.g. Vector2.Row or Matrix3x3, it can be obtained as such using methods such as asVector2Row() or asMatrix3x3(). The same holds for MatrixNxN that can be transformed to a strongly typed Matrix1x1, Matrix2x2, or Matrix3x3 (or MatrixNxM). Many such methods exist to carry out a transformation between vectors and matrices of various sizes. These methods will check the consistency of the matrix size with the desired matrix type at runtime. All matrices, irrespective of their size, can be transformed to a QuantityTable using the asQuantityTable() method.
If matrix calculations result in a special matrix type, for example multiplying a 3x4 matrix by a 4x3 matrix resulting in a 3x3 matrix, the resulting MatrixNxM from the calculation can be obtained as a Matrix3x3 using the method asMatrix3x3(). This allows it, for example, to be added to another Matrix3x3.
The transpose() method returns the transposed matrix, where rows and columns have been swapped. A transposed matrix has the same displayUnit as the original matrix.
Furthermore, a matrix is Additive, which means that matrices of the same type, size, and quantity can be added to and subtracted from each other. Matrices also implement the Scalable interface, which exposes the scaleBy(double factor) and divideBy(double factor) methods.
The generic methods of a Matrix are:
int rows()returns the number of rows of the matrix.int cols()returns the number of columns of the matrix.getDisplayUnit()returns the display unit of the entireMatrix.setDisplayUnit(unit)sets a new display unit for the entireMatrixbased on a strongly typedunit.setDisplayUnit(string)sets a new display unit for the entireMatrixbased on aStringrepresentation of the unit.boolean isRelative()returns whether the underlyingQuantityis relative or not. Note thatMatrixonly stores relative quantities.boolean isAbsolute()returns whether the underlyingQuantityis absolute or not. Note thatMatrixonly stores relative quantities.transpose()returns a newMatrixwhere the rows and columns are swapped.mx1.add(mx2)returns a newMatrixwhere all entries ofmx2have been added to the corresponding entries ofmx1. ThedisplayUnitis taken frommx1. The number of rows and columns ofmx1andmx2have to be equal, of course.mx1.subtract(mx2)returns a newMatrixwhere all entries ofmx2have been subtracted from the corresponding entries ofmx1. ThedisplayUnitis taken frommx1. The number of rows and columns ofmx1andmx2have to be equal, of course.mx.scaleBy(double factor)returns a newMatrixwhere all entries ofmxhave been scaled byfactor. ThedisplayUnitremains unchanged.mx.divideBy(double factor)returns a newMatrixwhere all entries ofmxhave been scaled by1.0/factor. ThedisplayUnitremains unchanged.
Many of the matrix operations are delegated to the mathematics utility classes ArrayMath and MatrixMath, which can be found in the org.djunits.util package.
Obtaining values of matrix entries¶
Several methods exist to get access to the entries of a Matrix. When single entries, rows or columns are retrieved, two versions of the methods exist: a version where the row and column number are 0-based, and a version where the row and column number are 1-based. The 1-based methods have a name that starts with m for matrix, since the entry numbering of a matrix start with m11, and not with m00. So, there is an si(row, col) method where row ranges from 0 to matrix.rows()-1 and col ranges from 0 to matrix.cols()-1, and an msi(mRow, mCol) method where mRow ranges from 1 up to and including matrix.rows() and mCol ranges from 1 up to and including matrix.cols.
Quantity-based value methods return a value Q that is consistent with the quantity stored in the Matrix. Suppose mx is a Matrix3x3<Mass>. The result of the operation mx.mget(1,3) will then be a strongly typed Mass quantity. The letter Q in the methods below indicates that strongly typed quantity such as Mass.
A Matrix contains the following methods to obtain its values:
SI-based value methods¶
double[][] getSiGrid()returns a 2-dimensionaldouble[][]array with the SI-values of the entries in the matrix.double[] getSiArray()returns the values of the matrix in SI-units as a row-majordouble[]array with the same length as the matrix. This means that for an n x m matrix (n rows, m columns), the data is stored as [a11, a12, ..., a1m, a21, a22, ..., a2m, ..., an1, an2, ..., anm].double si(int row, int col)returns the SI-value of the entry at the 0-based row and column.double msi(int mRow, int mCol)returns the SI-value of the entry at the 1-based row indicated bymRowand 1-based column indicated bymCol.
Quantity-based value methods¶
Q[][] getScalarGrid()returns a 2-dimensional strongly typed quantity array that represents the matrix. The quantities in the array will all have the samedisplayUnitas the originalMatrix.Q[] getScalarArray()returns a 1-dimensional strongly typed row-major quantity array that represents the matrix. The quantities in the array will all have the samedisplayUnitas the originalMatrix.Q get(int row, int col)returns the quantity representation of the entry at the 0-based row and column. The returnedQuantitywill have the samedisplayUnitas the originalMatrix.Q mget(int mRow, int mCol)returns the quantity representation of the entry at the 1-based row indicated bymRowand 1-based column indicated bymCol. The returnedQuantitywill have the samedisplayUnitas the originalMatrix.
Retrieving matrix rows¶
Vector getRowVector(int row)retrieves the matrix row at the 0-basedrowas a row-vector. When the matrix is aMatrix3x3, the vector returned is aVector3.Rowof the sameQuantity, and with the samedisplayUnit.Vector mgetRowVector(int mRow)retrieves the matrix row at the 1-basedmRowas a row-vector. When the matrix is aMatrixNxM, the vector returned is aVectorN.Rowof the sameQuantity, and with the samedisplayUnit.Q[] getRowScalars(int row)retrieves the matrix row at the 0-basedrowas an array of quantities. When the matrix is aMatrix2x2<Length>, the array returned is of typeLength[2], where the quantities in the array have the samedisplayUnitas the original matrix.Q[] mgetRowScalars(int mRow)retrieves the matrix row at the 1-basedmRowas an array of quantities. When the matrix is aMatrixNxM<Area>, the array returned is of typeArea[matrix.cols()], where the quantities in the array have the samedisplayUnitas the original matrix. Note that the resultingQ[]array is 0-based.double[] getRowSi(int row)retrieves the SI-values of the 0-basedrowas adouble[]array. When the matrix is aMatrix3x3, the array returned is of typedouble[3].double[] mgetRowSi(int mRow)retrieves the SI-values of the 1-basedmRowas adouble[]array. When the matrix is aMatrixNxM, the array returned is of typedouble[matrix.cols()]. Note that the resultingdouble[]array is 0-based.
Retrieving matrix columns¶
Vector getColumnVector(int col)retrieves the matrix column at the 0-basedcolas a column-vector. When the matrix is aMatrix3x3, the vector returned is aVector3.Colof the sameQuantity, and with the samedisplayUnit.Vector mgetColumnVector(int mCol)retrieves the matrix column at the 1-basedmColas a column-vector. When the matrix is aMatrixNxM, the vector returned is aVectorN.Colof the sameQuantity, and with the samedisplayUnit.Q[] getColumnScalars(int col)retrieves the matrix column at the 0-basedcolas an array of quantities. When the matrix is aMatrix2x2<Length>, the array returned is of typeLength[2], where the quantities in the array have the samedisplayUnitas the original matrix.Q[] mgetColumnScalars(int mCol)retrieves the matrix column at the 1-basedmColas an array of quantities. When the matrix is aMatrixNxM<Area>, the array returned is of typeArea[matrix.cols()], where the quantities in the array have the samedisplayUnitas the original matrix. Note that the resultingQ[]array is 0-based.double[] getColumnSi(int col)retrieves the SI-values of the 0-basedcolas adouble[]array. When the matrix is aMatrix3x3, the array returned is of typedouble[3].double[] mgetColumnSi(int mCol)retrieves the SI-values of the 1-basedmColas adouble[]array. When the matrix is aMatrixNxM, the array returned is of typedouble[matrix.cols()]. Note that the resultingdouble[]array is 0-based.
Mathematical operations for all matrices¶
A Matrix implements several mathematical operations. The most important ones are:
Q mean()returns the mean quantity value of the entries of theMatrixas a strongly typedQuantity.Q min()returns the minimum quantity value of the entries of theMatrixas a strongly typedQuantity.Q max()returns the maximum quantity value of the entries of theMatrixas a strongly typedQuantity.Q median()returns the median quantity value of the entries of theMatrixas a strongly typedQuantity. The median value is the value of the middle entry when all entries have been sorted on their SI-values. When the number of entries in the matrix is even, the average of the two values that together make up the middle is returned.Q sum()returns the sum of the entries of theMatrixas a strongly typedQuantity.M negate()returns aMatrixof the same type and size where all entries \(x_{ij}\) have been set to \(-x_{ij}\).M abs()returns aMatrixof the same type and size where all entries \(x_{ij}\) have been set to \(|x_{ij}|\).double nonZeroCount()anddouble nnz()both return the number of non-zero entries in the matrix.
Extra operations for square matrices¶
Square matrices have a number of additional operations:
int order()returns the number of rows or columns of the square matrix.Q trace()returns the trace of the matrix, which is the sum of the diagonal entries. It results in a quantity with the samedisplayUnitas the original matrix.SIQuantity determinant()returns the determinant of the square matrix as anSIQuantity. The unit of the determinant will be \(U^n\) where \(n\) is the order of the matrix, and \(U\) is the SI-unit of the matrix. TheSIUnitof the determinant of a 4x4Energymatrix is kg4·m8/s8.double determinantSi()returns the SI-value of the determinant of the square matrix as adoublevalue.inverse()returns the inverse of the square matrix, if the matrix is non-singular. When the unit of the original matrix is \(U\), the unit of of the inverse matrix is \(U^{-1}\). If the matrix is singular, aNonInvertibleMatrixExceptionwill be thrown.adjugate()returns the adjugate (classical adjoint) matrix for this matrix, often denoted as \(adj(M)\). When the unit of the original matrix is \(U\), the unit of \(adj(M)\) is \(U^{(n-1)}\). The adjugate of a square matrix \(A\) is the matrix \(\mathrm{adj}(A)\) satisfying \(A \cdot \mathrm{adj}(A) = \det(A) \cdot I\).normFrobenius()returns the Frobenius norm of the matrix, which is equal to \(\sqrt(\mathrm{trace}(A^*\cdot A))\). It results in a quantity with the same unit as the original matrix. See Frobenius norm on Wikipedia for more information.Vector getDiagonalVector()returns the quantities on the diagonal as a column vector of the same quantity and size as the square matrix. ThedisplayUnitwill be the same as that of the matrix.Q[] getDiagonalScalars()returns the quantities on the diagonal as an array of quantities. When the matrix has order N, the array will have length N. ThedisplayUnitof the quantities will be the same as that of the matrix.double[] getDiagonalSi()returns the SI-values of the quantities on the diagonal as adouble[]array. When the matrix has order N, the array will have length N.boolean isSymmetric()returns whether the matrix is symmetric or not. A small tolerance of of 10-12 times the largest absolute SI-quantity in the matrix is used to determine symmetry.boolean isSymmetric(final Q tolerance)returns whether the matrix is symmetric or not, using a provided tolerance.boolean isSkewSymmetric()returns whether the matrix is skew-symmetric or not. A small tolerance of of 10-12 times the largest absolute SI-quantity in the matrix is used to determine skew-symmetry. Skew-symmetry means that \(A^T=-A\), or \(a_{ij}=-a_{ji}\) for all entries \(a_{ij}\).boolean isSkewSymmetric(final Q tolerance)returns whether the matrix is skew-symmetric or not, using a provided tolerance.
Vector definition and storage¶
Creating a Matrix1x1¶
Several methods exist to instantiate a Matrix1x1:
new Matrix1x1<Q>(double[] dataSi, Unit displayUnit)
creates aMatrix1x1based on an array of length 1 with SI-values for a quantity with a displayUnit.Matrix1x1.of(double xInUnit, Unit unit)
creates aMatrix1x1based on a value expressed in the given unit, e.g.,60.0, Speed.Unit.km_h.Matrix1x1.of(double[] dataInUnit, Unit unit)
creates aMatrix1x1based on an array of length 1 with values expressed in the given unit.Matrix1x1.of(double[][] gridInUnit, Unit unit)
creates aMatrix1x1based on a 1x1 grid (array of arrays) of values expressed in the given unit.Matrix1x1.ofSi(double[] dataSi, Unit displayUnit)
creates aMatrix1x1based on an array of length 1 with SI-values for a quantity and a displayUnit.Matrix1x1.ofSi(double[][] gridSi, Unit displayUnit)
creates aMatrix1x1based on a 1x1 grid (array of arrays) of SI values and a displayUnit.Matrix1x1.of(Q[] data)
creates aMatrix1x1based on an array of length 1 containing the quantity. The display unit is taken from the quantity.Matrix1x1.of(Q[][] grid)
creates aMatrix1x1based on a 1x1 grid (array of arrays) of quantities. The display unit is taken from the quantity.
Creating a Matrix2x2¶
Several methods exist to instantiate a Matrix2x2.
The array-based methods use a row-major array. This means that the data is presented "row-by-row", so, {m11, m12, m21, m22}. A (r,c) value is retrieved by m[index], index = r * cols() + c where r, c are 0-based indices.
The grid-based methods count the rows in the 'outer' (first) array [r][], and the columns in the 'inner' second array [][c]. A (r,c)value is retrieved by m[r][c].
new Matrix2x2<Q>(double[] dataSi, Unit displayUnit)
creates aMatrix2x2based on a row-major array of length 4 with SI-values for a quantity with a displayUnit.Matrix2x2.of(double[] dataInUnit, Unit unit)
creates aMatrix2x2based on a row-major array of length 4 with values expressed in the given unit.Matrix2x2.of(double[][] gridInUnit, Unit unit)
creates aMatrix2x2based on a 2x2 grid (array of arrays) of values expressed in the given unit.Matrix2x2.ofSi(double[] dataSi, Unit displayUnit)
creates aMatrix2x2based on a row-major array of length 4 with SI-values for a quantity and a displayUnit.Matrix2x2.ofSi(double[][] gridSi, Unit displayUnit)
creates aMatrix2x2based on a 2x2 grid (array of arrays) of SI values and a displayUnit.Matrix2x2.of(Q[] data)
creates aMatrix2x2based on a row-major array of length 4 containing the quantity. The display unit is taken from the quantity at position[0].Matrix2x2.of(Q[][] grid)
creates aMatrix2x2based on a 2x2 grid (array of arrays) of quantities. The display unit is taken from the quantity at position[0][0].
Creating a Matrix3x3¶
Several methods exist to instantiate a Matrix3x3.
The array-based methods use a row-major array. This means that the data is presented "row-by-row", so, {m11, m12, m13, m21, m22, m23, m31, m32, m33}. A (r,c) value is retrieved by m[index], index = r * cols() + c where r, c are 0-based indices.
The grid-based methods count the rows in the 'outer' (first) array [r][], and the columns in the 'inner' second array [][c]. A (r,c)value is retrieved by m[r][c].
new Matrix3x3<Q>(double[] dataSi, Unit displayUnit)
creates aMatrix3x3based on a row-major array of length 9 with SI-values for a quantity with a displayUnit.Matrix3x3.of(double[] dataInUnit, Unit unit)
creates aMatrix3x3based on a row-major array of length 9 with values expressed in the given unit.Matrix3x3.of(double[][] gridInUnit, Unit unit)
creates aMatrix3x3based on a 3x3 grid (array of arrays) of values expressed in the given unit.Matrix3x3.ofSi(double[] dataSi, Unit displayUnit)
creates aMatrix3x3based on a row-major array of length 9 with SI-values for a quantity and a displayUnit.Matrix3x3.ofSi(double[][] gridSi, Unit displayUnit)
creates aMatrix3x3based on a 3x3 grid (array of arrays) of SI values and a displayUnit.Matrix3x3.of(Q[] data)
creates aMatrix3x3based on a row-major array of length 9 containing the quantity. The display unit is taken from the quantity at position[0].Matrix3x3.of(Q[][] grid)
creates aMatrix3x3based on a 3x3 grid (array of arrays) of quantities. The display unit is taken from the quantity at position[0][0].
Creating a MatrixNxN¶
The MatrixNxN class is used for storing square matrices of any size (1x1 and up). Data can be stored as single-precision float variable, or as double-precision double values. Both dense storage (store every number) and sparse storage (only store non-zero values) are possible.
Several methods exist to instantiate a MatrixNxN.
The DataGridSi-based methods store the data in the dataGridSi object, which can be DenseDoubleDataSi, SparseDoubleDataSi, DenseFloatDataSi, or SparseFloatDataSi. These objects are instantiated through one of their of(), ofSi() or constructor methods. For many of and ofSi methods and the constructor, the number of rows and columns of the matrix need to be provided for the DataGridSi object to know the shape of the matrix. A double[4] array of SI values can represent a 2x2 matrix, but also a 4x1 or 1x4 matrix or vector. All three shapes can be stored in the DataGridSi object.
The array-based methods use a row-major array. This means that the data is presented "row-by-row", so, {m11, m12, m13, m21, m22, m23, m31, m32, m33} for a 3x3 matrix. A (r,c) value is retrieved by m[index], index = r * cols() + c where r, c are 0-based indices. Since the construction methods know that a square matrix has to be constructed, they test whether the array length is a perfect square (e.g., 25) and construct the corresponding square matrix (e.g., 5x5) by taking the square root of the length for the number of rows and columns.
The grid-based methods count the rows in the 'outer' (first) array [r][], and the columns in the 'inner' second array [][c]. A (r,c)value is retrieved by m[r][c]. For a square NxN matrix, the number of rows and columns should be the same, and 'ragged' grids are not allowed and result in an IllegalArgumentException.
new MatrixNxN<Q>(DataGridSi dataSi, Unit displayUnit)
creates aMatrixNxNbased on aDataGridSistorage object. More information can be found in the storage section.MatrixNxN.of(DataGridSi dataSi, Unit displayUnit)
creates aMatrixNxNbased on aDataGridSistorage object. More information can be found in the storage section.MatrixNxN.of(double[] dataInUnit, Unit unit)
creates aMatrixNxNbased on a row-major array with values expressed in the given unit. The number of elements in the array needs to be a perfect square.MatrixNxN.of(double[][] gridInUnit, Unit unit)
creates aMatrixNxNbased on a grid (array of arrays) with values expressed in the given unit. The number of rows and columns in the grid have to be the same, and the grid cannot be 'ragged'.MatrixNxN.ofSi(double[] dataSi, Unit displayUnit)
creates aMatrixNxNbased on a row-major array with SI-values for the quantities. The number of elements in the array needs to be a perfect square.MatrixNxN.ofSi(double[][] gridSi, Unit displayUnit)
creates aMatrixNxNbased on a grid (array of arrays) with with SI-values for the quantities. The number of rows and columns in the grid have to be the same, and the grid cannot be 'ragged'.MatrixNxN.of(Q[] data)
creates aMatrixNxNbased on a row-major array with quantities. The number of elements in the array needs to be a perfect square.MatrixNxN.of(Q[][] grid)
creates aMatrixNxNbased on a grid (array of arrays) with with quantities. The number of rows and columns in the grid have to be the same, and the grid cannot be 'ragged'.
Creating a MatrixNxM¶
The MatrixNxM class is the most generic matrix class. It can be used for matrices of any size (1x1 and up). Data can be stored as single-precision float variable, or as double-precision double values. Both dense storage (store every number) and sparse storage (only store non-zero values) are possible.
Several methods exist to instantiate a MatrixNxM.
The DataGridSi-based methods store the data in the dataGridSi object, which can be DenseDoubleDataSi, SparseDoubleDataSi, DenseFloatDataSi, or SparseFloatDataSi. These objects are instantiated through one of their of(), ofSi() or constructor methods. For many of and ofSi methods and the constructor, the number of rows and columns of the matrix need to be provided for the DataGridSi object to know the shape of the matrix. A double[6] array of SI values can represent a 2x3 matrix, but also a 3x2 matrix or a 1x6 or 6x1 matrix or vector. All four shapes can be stored in the DataGridSi object by providing the number of rows and columns.
The array-based methods use a row-major array. This means that the data is presented "row-by-row", so, {m11, m12, m13, m21, m22, m23} for a 2x3 matrix. A (r,c) value is retrieved by m[index], index = r * cols() + c where r, c are 0-based indices.
The grid-based methods count the rows in the 'outer' (first) array [r][], and the columns in the 'inner' second array [][c]. A (r,c)value is retrieved by m[r][c]. 'Ragged' grids are not allowed and result in an IllegalArgumentException.
new MatrixNxM<Q>(DataGridSi dataSi, Unit displayUnit)
creates aMatrixNxMbased on aDataGridSistorage object. More information can be found in the storage section.MatrixNxM.of(DataGridSi dataSi, Unit displayUnit)
creates aMatrixNxMbased on aDataGridSistorage object. More information can be found in the storage section.MatrixNxM.of(double[] dataInUnit, int rows, int cols, Unit unit)
creates aMatrixNxMbased on a row-major array with values expressed in the given unit. The length of the array needs to be equal torows * cols.MatrixNxM.of(double[][] gridInUnit, Unit unit)
creates aMatrixNxMbased on a grid (array of arrays) with values expressed in the given unit. The grid cannot be 'ragged'.MatrixNxM.ofSi(double[] dataSi, int rows, int cols, Unit displayUnit)
creates aMatrixNxMbased on a row-major array with SI-values for the quantities. The length of the array needs to be equal torows * cols.MatrixNxM.ofSi(double[][] gridSi, Unit displayUnit)
creates aMatrixNxMbased on a grid (array of arrays) with with SI-values for the quantities. The grid cannot be 'ragged'.MatrixNxM.of(Q[] data, int rows, int cols)
creates aMatrixNxMbased on a row-major array with quantities. The length of the array needs to be equal torows * cols.MatrixNxM.of(Q[][] grid)
creates aMatrixNxMbased on a grid (array of arrays) with with quantities. The grid cannot be 'ragged'.
Example matrix definition and usage¶
The example below shows the instantiation and usage of a MatrixNxM:
MatrixNxM<Length> lm2x4 = MatrixNxM.of(
new double[][] {{1, 2, 3, 4}, {5, 6, 7, 8}}, Length.Unit.m);
MatrixNxM<Length> lm4x2 = MatrixNxM.of(
new double[][] {{1, 2}, {3, 4}, {5, 6}, {7, 8}}, Length.Unit.m);
var mult44 = lm4x2.multiply(lm2x4).as(Area.Unit.m2);
System.out.println("\nMatrix1 (4x2):\n" + lm4x2);
System.out.println("Matrix2 (2x4):\n" + lm2x4);
System.out.println("Multiplication (4x4):\n" + mult44);
Matrix2x2<Area> mult22 =
lm2x4.multiply(lm4x2).asMatrix2x2().as(Area.Unit.a);
System.out.println("\nMatrix1 (2x4):\n" + lm2x4);
System.out.println("Matrix2 (4x2):\n" + lm4x2);
System.out.println("Multiplication (2x2):\n" + mult22);
The above code prints the following:
Matrix1 (4x2):
[1.00000000, 2.00000000
3.00000000, 4.00000000
5.00000000, 6.00000000
7.00000000, 8.00000000] m
Matrix2 (2x4):
[1.00000000, 2.00000000, 3.00000000, 4.00000000
5.00000000, 6.00000000, 7.00000000, 8.00000000] m
Multiplication (4x4):
[11.0000000, 14.0000000, 17.0000000, 20.0000000
23.0000000, 30.0000000, 37.0000000, 44.0000000
35.0000000, 46.0000000, 57.0000000, 68.0000000
47.0000000, 62.0000000, 77.0000000, 92.0000000] m2
Matrix1 (2x4):
[1.00000000, 2.00000000, 3.00000000, 4.00000000
5.00000000, 6.00000000, 7.00000000, 8.00000000] m
Matrix2 (4x2):
[1.00000000, 2.00000000
3.00000000, 4.00000000
5.00000000, 6.00000000
7.00000000, 8.00000000] m
Multiplication (2x2):
[0.50000000, 0.60000000
1.14000000, 1.40000000] a
As can be seen, the multiplication of a 2x4 Length with a 4x2 Length matrix results in a 2x2 Area matrix. The 2x2 matrix can be 'cast' to a true Matrix2x2 class with more efficient storage and operations. When printing the content of a matrix, Area units such as are can be used.
Similarly, when multiplying in the opposite way, a 4x2 Length with a 2x4 Length matrix results in a 4x4 Area matrix. This matrix is of type MatrixNxM and could be cast to a MatrixNxN with the asMatrixNxN() method, since it is a square matrix. This cast would open the matrix for operations such as inverse, trace and determinant, which are not defined for the MatrixNxM.