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