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