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{ ∑<sub>i=1:m</sub> ∑<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 ≥ |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}