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