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