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