001// Copyright (c) FIRST and other WPILib contributors.
002// Open Source Software; you can modify and/or share it under the terms of
003// the WPILib BSD license file in the root directory of this project.
004
005package edu.wpi.first.math;
006
007import edu.wpi.first.math.jni.EigenJNI;
008import edu.wpi.first.math.numbers.N1;
009import edu.wpi.first.math.proto.MatrixProto;
010import edu.wpi.first.math.struct.MatrixStruct;
011import edu.wpi.first.util.protobuf.Protobuf;
012import edu.wpi.first.util.protobuf.ProtobufSerializable;
013import edu.wpi.first.util.struct.Struct;
014import edu.wpi.first.util.struct.StructSerializable;
015import java.util.Objects;
016import org.ejml.MatrixDimensionException;
017import org.ejml.data.DMatrixRMaj;
018import org.ejml.dense.row.CommonOps_DDRM;
019import org.ejml.dense.row.MatrixFeatures_DDRM;
020import org.ejml.dense.row.NormOps_DDRM;
021import org.ejml.dense.row.factory.DecompositionFactory_DDRM;
022import org.ejml.interfaces.decomposition.CholeskyDecomposition_F64;
023import org.ejml.simple.SimpleMatrix;
024
025/**
026 * A shape-safe wrapper over Efficient Java Matrix Library (EJML) matrices.
027 *
028 * <p>This class is intended to be used alongside the state space library.
029 *
030 * @param <R> The number of rows in this matrix.
031 * @param <C> The number of columns in this matrix.
032 */
033public class Matrix<R extends Num, C extends Num>
034    implements ProtobufSerializable, StructSerializable {
035  /** Storage for underlying EJML matrix. */
036  protected final SimpleMatrix m_storage;
037
038  /**
039   * Constructs an empty zero matrix of the given dimensions.
040   *
041   * @param rows The number of rows of the matrix.
042   * @param columns The number of columns of the matrix.
043   */
044  public Matrix(Nat<R> rows, Nat<C> columns) {
045    this.m_storage =
046        new SimpleMatrix(
047            Objects.requireNonNull(rows).getNum(), Objects.requireNonNull(columns).getNum());
048  }
049
050  /**
051   * Constructs a new {@link Matrix} with the given storage. Caller should make sure that the
052   * provided generic bounds match the shape of the provided {@link Matrix}.
053   *
054   * @param rows The number of rows of the matrix.
055   * @param columns The number of columns of the matrix.
056   * @param storage The double array to back this value.
057   */
058  public Matrix(Nat<R> rows, Nat<C> columns, double[] storage) {
059    this.m_storage = new SimpleMatrix(rows.getNum(), columns.getNum(), true, storage);
060  }
061
062  /**
063   * Constructs a new {@link Matrix} with the given storage. Caller should make sure that the
064   * provided generic bounds match the shape of the provided {@link Matrix}.
065   *
066   * <p>NOTE:It is not recommend to use this constructor unless the {@link SimpleMatrix} API is
067   * absolutely necessary due to the desired function not being accessible through the {@link
068   * Matrix} wrapper.
069   *
070   * @param storage The {@link SimpleMatrix} to back this value.
071   */
072  public Matrix(SimpleMatrix storage) {
073    this.m_storage = Objects.requireNonNull(storage);
074  }
075
076  /**
077   * Constructs a new matrix with the storage of the supplied matrix.
078   *
079   * @param other The {@link Matrix} to copy the storage of.
080   */
081  public Matrix(Matrix<R, C> other) {
082    this.m_storage = Objects.requireNonNull(other).getStorage().copy();
083  }
084
085  /**
086   * Gets the underlying {@link SimpleMatrix} that this {@link Matrix} wraps.
087   *
088   * <p>NOTE:The use of this method is heavily discouraged as this removes any guarantee of type
089   * safety. This should only be called if the {@link SimpleMatrix} API is absolutely necessary due
090   * to the desired function not being accessible through the {@link Matrix} wrapper.
091   *
092   * @return The underlying {@link SimpleMatrix} storage.
093   */
094  public SimpleMatrix getStorage() {
095    return m_storage;
096  }
097
098  /**
099   * Gets the number of columns in this matrix.
100   *
101   * @return The number of columns, according to the internal storage.
102   */
103  public final int getNumCols() {
104    return this.m_storage.getNumCols();
105  }
106
107  /**
108   * Gets the number of rows in this matrix.
109   *
110   * @return The number of rows, according to the internal storage.
111   */
112  public final int getNumRows() {
113    return this.m_storage.getNumRows();
114  }
115
116  /**
117   * Get an element of this matrix.
118   *
119   * @param row The row of the element.
120   * @param col The column of the element.
121   * @return The element in this matrix at row,col.
122   */
123  public final double get(int row, int col) {
124    return this.m_storage.get(row, col);
125  }
126
127  /**
128   * Sets the value at the given indices.
129   *
130   * @param row The row of the element.
131   * @param col The column of the element.
132   * @param value The value to insert at the given location.
133   */
134  public final void set(int row, int col, double value) {
135    this.m_storage.set(row, col, value);
136  }
137
138  /**
139   * Sets a row to a given row vector.
140   *
141   * @param row The row to set.
142   * @param val The row vector to set the given row to.
143   */
144  public final void setRow(int row, Matrix<N1, C> val) {
145    this.m_storage.setRow(row, 0, Objects.requireNonNull(val).m_storage.getDDRM().getData());
146  }
147
148  /**
149   * Sets a column to a given column vector.
150   *
151   * @param column The column to set.
152   * @param val The column vector to set the given row to.
153   */
154  public final void setColumn(int column, Matrix<R, N1> val) {
155    this.m_storage.setColumn(column, 0, Objects.requireNonNull(val).m_storage.getDDRM().getData());
156  }
157
158  /**
159   * Sets all the elements in "this" matrix equal to the specified value.
160   *
161   * @param value The value each element is set to.
162   */
163  public void fill(double value) {
164    this.m_storage.fill(value);
165  }
166
167  /**
168   * Returns the diagonal elements inside a vector or square matrix.
169   *
170   * <p>If "this" {@link Matrix} is a vector then a square matrix is returned. If a "this" {@link
171   * Matrix} is a matrix then a vector of diagonal elements is returned.
172   *
173   * @return The diagonal elements inside a vector or a square matrix.
174   */
175  public final Matrix<R, C> diag() {
176    return new Matrix<>(this.m_storage.diag());
177  }
178
179  /**
180   * Returns the largest element of this matrix.
181   *
182   * @return The largest element of this matrix.
183   */
184  public final double max() {
185    return CommonOps_DDRM.elementMax(this.m_storage.getDDRM());
186  }
187
188  /**
189   * Returns the absolute value of the element in this matrix with the largest absolute value.
190   *
191   * @return The absolute value of the element with the largest absolute value.
192   */
193  public final double maxAbs() {
194    return CommonOps_DDRM.elementMaxAbs(this.m_storage.getDDRM());
195  }
196
197  /**
198   * Returns the smallest element of this matrix.
199   *
200   * @return The smallest element of this matrix.
201   */
202  public final double minInternal() {
203    return CommonOps_DDRM.elementMin(this.m_storage.getDDRM());
204  }
205
206  /**
207   * Calculates the mean of the elements in this matrix.
208   *
209   * @return The mean value of this matrix.
210   */
211  public final double mean() {
212    return this.elementSum() / this.m_storage.getNumElements();
213  }
214
215  /**
216   * Multiplies this matrix with another that has C rows.
217   *
218   * <p>As matrix multiplication is only defined if the number of columns in the first matrix
219   * matches the number of rows in the second, this operation will fail to compile under any other
220   * circumstances.
221   *
222   * @param other The other matrix to multiply by.
223   * @param <C2> The number of columns in the second matrix.
224   * @return The result of the matrix multiplication between "this" and the given matrix.
225   */
226  public final <C2 extends Num> Matrix<R, C2> times(Matrix<C, C2> other) {
227    return new Matrix<>(this.m_storage.mult(Objects.requireNonNull(other).m_storage));
228  }
229
230  /**
231   * Multiplies all the elements of this matrix by the given scalar.
232   *
233   * @param value The scalar value to multiply by.
234   * @return A new matrix with all the elements multiplied by the given value.
235   */
236  public Matrix<R, C> times(double value) {
237    return new Matrix<>(this.m_storage.scale(value));
238  }
239
240  /**
241   * Returns a matrix which is the result of an element by element multiplication of "this" and
242   * other.
243   *
244   * <p>c<sub>i,j</sub> = a<sub>i,j</sub>*other<sub>i,j</sub>
245   *
246   * @param other The other {@link Matrix} to perform element multiplication on.
247   * @return The element by element multiplication of "this" and other.
248   */
249  public final Matrix<R, C> elementTimes(Matrix<R, C> other) {
250    return new Matrix<>(this.m_storage.elementMult(Objects.requireNonNull(other).m_storage));
251  }
252
253  /**
254   * Subtracts the given value from all the elements of this matrix.
255   *
256   * @param value The value to subtract.
257   * @return The resultant matrix.
258   */
259  public final Matrix<R, C> minus(double value) {
260    return new Matrix<>(this.m_storage.minus(value));
261  }
262
263  /**
264   * Subtracts the given matrix from this matrix.
265   *
266   * @param value The matrix to subtract.
267   * @return The resultant matrix.
268   */
269  public final Matrix<R, C> minus(Matrix<R, C> value) {
270    return new Matrix<>(this.m_storage.minus(Objects.requireNonNull(value).m_storage));
271  }
272
273  /**
274   * Adds the given value to all the elements of this matrix.
275   *
276   * @param value The value to add.
277   * @return The resultant matrix.
278   */
279  public final Matrix<R, C> plus(double value) {
280    return new Matrix<>(this.m_storage.plus(value));
281  }
282
283  /**
284   * Adds the given matrix to this matrix.
285   *
286   * @param value The matrix to add.
287   * @return The resultant matrix.
288   */
289  public final Matrix<R, C> plus(Matrix<R, C> value) {
290    return new Matrix<>(this.m_storage.plus(Objects.requireNonNull(value).m_storage));
291  }
292
293  /**
294   * Divides all elements of this matrix by the given value.
295   *
296   * @param value The value to divide by.
297   * @return The resultant matrix.
298   */
299  public Matrix<R, C> div(int value) {
300    return new Matrix<>(this.m_storage.divide(value));
301  }
302
303  /**
304   * Divides all elements of this matrix by the given value.
305   *
306   * @param value The value to divide by.
307   * @return The resultant matrix.
308   */
309  public Matrix<R, C> div(double value) {
310    return new Matrix<>(this.m_storage.divide(value));
311  }
312
313  /**
314   * Calculates the transpose, Mᵀ of this matrix.
315   *
316   * @return The transpose matrix.
317   */
318  public final Matrix<C, R> transpose() {
319    return new Matrix<>(this.m_storage.transpose());
320  }
321
322  /**
323   * Returns a copy of this matrix.
324   *
325   * @return A copy of this matrix.
326   */
327  public final Matrix<R, C> copy() {
328    return new Matrix<>(this.m_storage.copy());
329  }
330
331  /**
332   * Returns the inverse matrix of "this" matrix.
333   *
334   * @return The inverse of "this" matrix.
335   * @throws org.ejml.data.SingularMatrixException If "this" matrix is non-invertable.
336   */
337  public final Matrix<R, C> inv() {
338    return new Matrix<>(this.m_storage.invert());
339  }
340
341  /**
342   * Returns the solution x to the equation Ax = b, where A is "this" matrix.
343   *
344   * <p>The matrix equation could also be written as x = A<sup>-1</sup>b. Where the pseudo inverse
345   * is used if A is not square.
346   *
347   * <p>Note that this method does not support solving using a QR decomposition with full-pivoting,
348   * as only column-pivoting is supported. For full-pivoting, use {@link
349   * #solveFullPivHouseholderQr}.
350   *
351   * @param <C2> Columns in b.
352   * @param b The right-hand side of the equation to solve.
353   * @return The solution to the linear system.
354   */
355  public final <C2 extends Num> Matrix<C, C2> solve(Matrix<R, C2> b) {
356    return new Matrix<>(this.m_storage.solve(Objects.requireNonNull(b).m_storage));
357  }
358
359  /**
360   * Solves the least-squares problem Ax=B using a QR decomposition with full pivoting, where this
361   * matrix is A.
362   *
363   * @param <R2> Number of rows in B.
364   * @param <C2> Number of columns in B.
365   * @param other The B matrix.
366   * @return The solution matrix.
367   */
368  public final <R2 extends Num, C2 extends Num> Matrix<C, C2> solveFullPivHouseholderQr(
369      Matrix<R2, C2> other) {
370    Matrix<C, C2> solution = new Matrix<>(new SimpleMatrix(this.getNumCols(), other.getNumCols()));
371    EigenJNI.solveFullPivHouseholderQr(
372        this.getData(),
373        this.getNumRows(),
374        this.getNumCols(),
375        other.getData(),
376        other.getNumRows(),
377        other.getNumCols(),
378        solution.getData());
379    return solution;
380  }
381
382  /**
383   * Computes the matrix exponential using Eigen's solver. This method only works for square
384   * matrices, and will otherwise throw an {@link MatrixDimensionException}.
385   *
386   * @return The exponential of A.
387   */
388  public final Matrix<R, C> exp() {
389    if (this.getNumRows() != this.getNumCols()) {
390      throw new MatrixDimensionException(
391          "Non-square matrices cannot be exponentiated! "
392              + "This matrix is "
393              + this.getNumRows()
394              + " x "
395              + this.getNumCols());
396    }
397    Matrix<R, C> toReturn = new Matrix<>(new SimpleMatrix(this.getNumRows(), this.getNumCols()));
398    EigenJNI.exp(
399        this.m_storage.getDDRM().getData(),
400        this.getNumRows(),
401        toReturn.m_storage.getDDRM().getData());
402    return toReturn;
403  }
404
405  /**
406   * Computes the matrix power using Eigen's solver. This method only works for square matrices, and
407   * will otherwise throw an {@link MatrixDimensionException}.
408   *
409   * @param exponent The exponent.
410   * @return The exponential of A.
411   */
412  public final Matrix<R, C> pow(double exponent) {
413    if (this.getNumRows() != this.getNumCols()) {
414      throw new MatrixDimensionException(
415          "Non-square matrices cannot be raised to a power! "
416              + "This matrix is "
417              + this.getNumRows()
418              + " x "
419              + this.getNumCols());
420    }
421    Matrix<R, C> toReturn = new Matrix<>(new SimpleMatrix(this.getNumRows(), this.getNumCols()));
422    EigenJNI.pow(
423        this.m_storage.getDDRM().getData(),
424        this.getNumRows(),
425        exponent,
426        toReturn.m_storage.getDDRM().getData());
427    return toReturn;
428  }
429
430  /**
431   * Returns the determinant of this matrix.
432   *
433   * @return The determinant of this matrix.
434   */
435  public final double det() {
436    return this.m_storage.determinant();
437  }
438
439  /**
440   * Computes the Frobenius normal of the matrix.
441   *
442   * <p>normF = Sqrt{ &sum;<sub>i=1:m</sub> &sum;<sub>j=1:n</sub> { a<sub>ij</sub><sup>2</sup>} }
443   *
444   * @return The matrix's Frobenius normal.
445   */
446  public final double normF() {
447    return this.m_storage.normF();
448  }
449
450  /**
451   * Computes the induced p = 1 matrix norm.
452   *
453   * <p>||A||<sub>1</sub>= max(j=1 to n; sum(i=1 to m; |a<sub>ij</sub>|))
454   *
455   * @return The norm.
456   */
457  public final double normIndP1() {
458    return NormOps_DDRM.inducedP1(this.m_storage.getDDRM());
459  }
460
461  /**
462   * Computes the sum of all the elements in the matrix.
463   *
464   * @return Sum of all the elements.
465   */
466  public final double elementSum() {
467    return this.m_storage.elementSum();
468  }
469
470  /**
471   * Computes the trace of the matrix.
472   *
473   * @return The trace of the matrix.
474   */
475  public final double trace() {
476    return this.m_storage.trace();
477  }
478
479  /**
480   * Returns a matrix which is the result of an element by element power of "this" and b.
481   *
482   * <p>c<sub>i,j</sub> = a<sub>i,j</sub> ^ b
483   *
484   * @param b Scalar.
485   * @return The element by element power of "this" and b.
486   */
487  public final Matrix<R, C> elementPower(double b) {
488    return new Matrix<>(this.m_storage.elementPower(b));
489  }
490
491  /**
492   * Returns a matrix which is the result of an element by element power of "this" and b.
493   *
494   * <p>c<sub>i,j</sub> = a<sub>i,j</sub> ^ b
495   *
496   * @param b Scalar.
497   * @return The element by element power of "this" and b.
498   */
499  public final Matrix<R, C> elementPower(int b) {
500    return new Matrix<>(this.m_storage.elementPower(b));
501  }
502
503  /**
504   * Extracts a given row into a row vector with new underlying storage.
505   *
506   * @param row The row to extract a vector from.
507   * @return A row vector from the given row.
508   */
509  public final Matrix<N1, C> extractRowVector(int row) {
510    return new Matrix<>(this.m_storage.extractVector(true, row));
511  }
512
513  /**
514   * Extracts a given column into a column vector with new underlying storage.
515   *
516   * @param column The column to extract a vector from.
517   * @return A column vector from the given column.
518   */
519  public final Matrix<R, N1> extractColumnVector(int column) {
520    return new Matrix<>(this.m_storage.extractVector(false, column));
521  }
522
523  /**
524   * Extracts a matrix of a given size and start position with new underlying storage.
525   *
526   * @param <R2> Number of rows to extract.
527   * @param <C2> Number of columns to extract.
528   * @param height The number of rows of the extracted matrix.
529   * @param width The number of columns of the extracted matrix.
530   * @param startingRow The starting row of the extracted matrix.
531   * @param startingCol The starting column of the extracted matrix.
532   * @return The extracted matrix.
533   */
534  public final <R2 extends Num, C2 extends Num> Matrix<R2, C2> block(
535      Nat<R2> height, Nat<C2> width, int startingRow, int startingCol) {
536    return new Matrix<>(
537        this.m_storage.extractMatrix(
538            startingRow,
539            startingRow + Objects.requireNonNull(height).getNum(),
540            startingCol,
541            startingCol + Objects.requireNonNull(width).getNum()));
542  }
543
544  /**
545   * Extracts a matrix of a given size and start position with new underlying storage.
546   *
547   * @param <R2> Number of rows to extract.
548   * @param <C2> Number of columns to extract.
549   * @param height The number of rows of the extracted matrix.
550   * @param width The number of columns of the extracted matrix.
551   * @param startingRow The starting row of the extracted matrix.
552   * @param startingCol The starting column of the extracted matrix.
553   * @return The extracted matrix.
554   */
555  public final <R2 extends Num, C2 extends Num> Matrix<R2, C2> block(
556      int height, int width, int startingRow, int startingCol) {
557    return new Matrix<>(
558        this.m_storage.extractMatrix(
559            startingRow, startingRow + height, startingCol, startingCol + width));
560  }
561
562  /**
563   * Assign a matrix of a given size and start position.
564   *
565   * @param <R2> Rows in block assignment.
566   * @param <C2> Columns in block assignment.
567   * @param startingRow The row to start at.
568   * @param startingCol The column to start at.
569   * @param other The matrix to assign the block to.
570   */
571  public <R2 extends Num, C2 extends Num> void assignBlock(
572      int startingRow, int startingCol, Matrix<R2, C2> other) {
573    this.m_storage.insertIntoThis(
574        startingRow, startingCol, Objects.requireNonNull(other).m_storage);
575  }
576
577  /**
578   * Extracts a submatrix from the supplied matrix and inserts it in a submatrix in "this". The
579   * shape of "this" is used to determine the size of the matrix extracted.
580   *
581   * @param <R2> Number of rows to extract.
582   * @param <C2> Number of columns to extract.
583   * @param startingRow The starting row in the supplied matrix to extract the submatrix.
584   * @param startingCol The starting column in the supplied matrix to extract the submatrix.
585   * @param other The matrix to extract the submatrix from.
586   */
587  public <R2 extends Num, C2 extends Num> void extractFrom(
588      int startingRow, int startingCol, Matrix<R2, C2> other) {
589    CommonOps_DDRM.extract(
590        other.m_storage.getDDRM(), startingRow, startingCol, this.m_storage.getDDRM());
591  }
592
593  /**
594   * Decompose "this" matrix using Cholesky Decomposition. If the "this" matrix is zeros, it will
595   * return the zero matrix.
596   *
597   * @param lowerTriangular Whether we want to decompose to the lower triangular Cholesky matrix.
598   * @return The decomposed matrix.
599   * @throws RuntimeException if the matrix could not be decomposed(i.e. is not positive
600   *     semidefinite).
601   */
602  public Matrix<R, C> lltDecompose(boolean lowerTriangular) {
603    SimpleMatrix temp = m_storage.copy();
604
605    CholeskyDecomposition_F64<DMatrixRMaj> chol =
606        DecompositionFactory_DDRM.chol(temp.getNumRows(), lowerTriangular);
607    if (!chol.decompose(temp.getMatrix())) {
608      // check that the input is not all zeros -- if they are, we special case and return all
609      // zeros.
610      var matData = temp.getDDRM().data;
611      var isZeros = true;
612      for (double matDatum : matData) {
613        isZeros &= Math.abs(matDatum) < 1e-6;
614      }
615      if (isZeros) {
616        return new Matrix<>(new SimpleMatrix(temp.getNumRows(), temp.getNumCols()));
617      }
618
619      throw new RuntimeException("Cholesky decomposition failed! Input matrix:\n" + m_storage);
620    }
621
622    return new Matrix<>(SimpleMatrix.wrap(chol.getT(null)));
623  }
624
625  /**
626   * Returns the row major data of this matrix as a double array.
627   *
628   * @return The row major data of this matrix as a double array.
629   */
630  public double[] getData() {
631    return m_storage.getDDRM().getData();
632  }
633
634  /**
635   * Creates the identity matrix of the given dimension.
636   *
637   * @param dim The dimension of the desired matrix as a {@link Nat}.
638   * @param <D> The dimension of the desired matrix as a generic.
639   * @return The DxD identity matrix.
640   */
641  public static <D extends Num> Matrix<D, D> eye(Nat<D> dim) {
642    return new Matrix<>(SimpleMatrix.identity(Objects.requireNonNull(dim).getNum()));
643  }
644
645  /**
646   * Creates the identity matrix of the given dimension.
647   *
648   * @param dim The dimension of the desired matrix as a {@link Num}.
649   * @param <D> The dimension of the desired matrix as a generic.
650   * @return The DxD identity matrix.
651   */
652  public static <D extends Num> Matrix<D, D> eye(D dim) {
653    return new Matrix<>(SimpleMatrix.identity(Objects.requireNonNull(dim).getNum()));
654  }
655
656  /**
657   * Reassigns dimensions of a {@link Matrix} to allow for operations with other matrices that have
658   * wildcard dimensions.
659   *
660   * @param <R1> Row dimension to assign.
661   * @param <C1> Column dimension to assign.
662   * @param mat The {@link Matrix} to remove the dimensions from.
663   * @return The matrix with reassigned dimensions.
664   */
665  public static <R1 extends Num, C1 extends Num> Matrix<R1, C1> changeBoundsUnchecked(
666      Matrix<?, ?> mat) {
667    return new Matrix<>(mat.m_storage);
668  }
669
670  /**
671   * Checks if another {@link Matrix} is identical to "this" one within a specified tolerance.
672   *
673   * <p>This will check if each element is in tolerance of the corresponding element from the other
674   * {@link Matrix} or if the elements have the same symbolic meaning. For two elements to have the
675   * same symbolic meaning they both must be either Double.NaN, Double.POSITIVE_INFINITY, or
676   * Double.NEGATIVE_INFINITY.
677   *
678   * <p>NOTE:It is recommended to use {@link Matrix#isEqual(Matrix, double)} over this method when
679   * checking if two matrices are equal as {@link Matrix#isEqual(Matrix, double)} will return false
680   * if an element is uncountable. This method should only be used when uncountable elements need to
681   * be compared.
682   *
683   * @param other The {@link Matrix} to check against this one.
684   * @param tolerance The tolerance to check equality with.
685   * @return true if this matrix is identical to the one supplied.
686   */
687  public boolean isIdentical(Matrix<?, ?> other, double tolerance) {
688    return MatrixFeatures_DDRM.isIdentical(
689        this.m_storage.getDDRM(), other.m_storage.getDDRM(), tolerance);
690  }
691
692  /**
693   * Checks if another {@link Matrix} is equal to "this" within a specified tolerance.
694   *
695   * <p>This will check if each element is in tolerance of the corresponding element from the other
696   * {@link Matrix}.
697   *
698   * <p>tol &ge; |a<sub>ij</sub> - b<sub>ij</sub>|
699   *
700   * @param other The {@link Matrix} to check against this one.
701   * @param tolerance The tolerance to check equality with.
702   * @return true if this matrix is equal to the one supplied.
703   */
704  public boolean isEqual(Matrix<?, ?> other, double tolerance) {
705    return MatrixFeatures_DDRM.isEquals(
706        this.m_storage.getDDRM(), other.m_storage.getDDRM(), tolerance);
707  }
708
709  /**
710   * Performs an inplace Cholesky rank update (or downdate).
711   *
712   * <p>If this matrix contains L where A = LLᵀ before the update, it will contain L where LLᵀ = A +
713   * σvvᵀ after the update.
714   *
715   * @param v Vector to use for the update.
716   * @param sigma Sigma to use for the update.
717   * @param lowerTriangular Whether this matrix is lower triangular.
718   */
719  public void rankUpdate(Matrix<R, N1> v, double sigma, boolean lowerTriangular) {
720    EigenJNI.rankUpdate(this.getData(), this.getNumRows(), v.getData(), sigma, lowerTriangular);
721  }
722
723  @Override
724  public String toString() {
725    return m_storage.toString();
726  }
727
728  /**
729   * Checks if an object is equal to this {@link Matrix}.
730   *
731   * <p>a<sub>ij</sub> == b<sub>ij</sub>
732   *
733   * @param other The Object to check against this {@link Matrix}.
734   * @return true if the object supplied is a {@link Matrix} and is equal to this matrix.
735   */
736  @Override
737  public boolean equals(Object other) {
738    return this == other
739        || other instanceof Matrix<?, ?> matrix
740            && !MatrixFeatures_DDRM.hasUncountable(matrix.m_storage.getDDRM())
741            && MatrixFeatures_DDRM.isEquals(this.m_storage.getDDRM(), matrix.m_storage.getDDRM());
742  }
743
744  @Override
745  public int hashCode() {
746    return Objects.hash(m_storage);
747  }
748
749  /**
750   * Creates an implementation of the {@link Protobuf} interface for matrices.
751   *
752   * @param <R> The number of rows of the matrices this serializer processes.
753   * @param <C> The number of cols of the matrices this serializer processes.
754   * @param rows The number of rows of the matrices this serializer processes.
755   * @param cols The number of cols of the matrices this serializer processes.
756   * @return The protobuf implementation.
757   */
758  public static <R extends Num, C extends Num> MatrixProto<R, C> getProto(
759      Nat<R> rows, Nat<C> cols) {
760    return new MatrixProto<>(rows, cols);
761  }
762
763  /**
764   * Creates an implementation of the {@link Struct} interfaces for matrices.
765   *
766   * @param <R> The number of rows of the matrices this serializer processes.
767   * @param <C> The number of cols of the matrices this serializer processes.
768   * @param rows The number of rows of the matrices this serializer processes.
769   * @param cols The number of cols of the matrices this serializer processes.
770   * @return The struct implementation.
771   */
772  public static <R extends Num, C extends Num> MatrixStruct<R, C> getStruct(
773      Nat<R> rows, Nat<C> cols) {
774    return new MatrixStruct<>(rows, cols);
775  }
776}