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